summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCliff Click <cliffc@acm.org>2025-02-07 08:58:36 -0800
committerCliff Click <cliffc@acm.org>2026-10-08 16:52:27 -0700
commit18fd28edce813d36c491e42d9e64c53c7251d26e (patch)
treee54ac214fb1ff76d0758a6ed544396bcbcbfe256
parent84a70fd744f83e2d701e15fc3d17109eb59ac707 (diff)
downloadSimple-18fd28edce813d36c491e42d9e64c53c7251d26e.tar.gz
Simple-18fd28edce813d36c491e42d9e64c53c7251d26e.zip
Chapter 19: Instruction Selection and Portable Compilation
Co-authored-by: Tamás Hadházy <85063808+Hels15@users.noreply.github.com> Co-authored-by: Hsiang-Ying Fu <66062782+fuhsnn@users.noreply.github.com> Co-authored-by: hadha <helsgithub15@gmail.com> Co-authored-by: godalming123 <68993177+godalming123@users.noreply.github.com> Co-authored-by: fuhsnn <66062782+fuhsnn@users.noreply.github.com> Co-authored-by: Symious <14933944+symious@users.noreply.github.com> Co-authored-by: RobertObkircher <robert.obkircher@gmail.com> Co-authored-by: Jun Kawasaki <jun784@gmail.com> Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
-rw-r--r--Makefile2
-rw-r--r--README.ja.md2
-rw-r--r--README.md2
-rw-r--r--docs/chapter19/Makefile12
-rw-r--r--docs/chapter19/README.ja.md149
-rw-r--r--docs/chapter19/README.md236
-rw-r--r--docs/chapter19/font-awesome-4.7.0.zipbin0 -> 669808 bytes
-rw-r--r--docs/chapter19/lattice.gv203
-rw-r--r--docs/chapter19/lattice.svg691
-rw-r--r--docs/chapter19/lattice2.gv85
-rw-r--r--docs/chapter19/lattice2.svg260
-rw-r--r--docs/chapter19/webtcp.min.js65
-rw-r--r--src/main/java/com/seaofnodes/simple/ASMPrinter.java51
-rw-r--r--src/main/java/com/seaofnodes/simple/Ary.java34
-rw-r--r--src/main/java/com/seaofnodes/simple/CodeGen.java234
-rw-r--r--src/main/java/com/seaofnodes/simple/GlobalCodeMotion.java85
-rw-r--r--src/main/java/com/seaofnodes/simple/IRPrinter.java4
-rw-r--r--src/main/java/com/seaofnodes/simple/IterPeeps.java50
-rw-r--r--src/main/java/com/seaofnodes/simple/ListScheduler.java245
-rw-r--r--src/main/java/com/seaofnodes/simple/Machine.java21
-rw-r--r--src/main/java/com/seaofnodes/simple/Parser.java97
-rw-r--r--src/main/java/com/seaofnodes/simple/RegMask.java47
-rw-r--r--src/main/java/com/seaofnodes/simple/SB.java17
-rw-r--r--src/main/java/com/seaofnodes/simple/node/AddNode.java3
-rw-r--r--src/main/java/com/seaofnodes/simple/node/BoolNode.java26
-rw-r--r--src/main/java/com/seaofnodes/simple/node/BulkMemPhiNode.java16
-rw-r--r--src/main/java/com/seaofnodes/simple/node/CFGNode.java19
-rw-r--r--src/main/java/com/seaofnodes/simple/node/CProjNode.java4
-rw-r--r--src/main/java/com/seaofnodes/simple/node/CallEndNode.java15
-rw-r--r--src/main/java/com/seaofnodes/simple/node/CallNode.java24
-rw-r--r--src/main/java/com/seaofnodes/simple/node/ConstantNode.java17
-rw-r--r--src/main/java/com/seaofnodes/simple/node/CtrlNode.java3
-rw-r--r--src/main/java/com/seaofnodes/simple/node/FunNode.java38
-rw-r--r--src/main/java/com/seaofnodes/simple/node/IfNode.java6
-rw-r--r--src/main/java/com/seaofnodes/simple/node/LoadNode.java71
-rw-r--r--src/main/java/com/seaofnodes/simple/node/LoopNode.java1
-rw-r--r--src/main/java/com/seaofnodes/simple/node/MachConcreteNode.java27
-rw-r--r--src/main/java/com/seaofnodes/simple/node/MachNode.java49
-rw-r--r--src/main/java/com/seaofnodes/simple/node/MemMergeNode.java13
-rw-r--r--src/main/java/com/seaofnodes/simple/node/MemOpNode.java41
-rw-r--r--src/main/java/com/seaofnodes/simple/node/MemPhiNode.java6
-rw-r--r--src/main/java/com/seaofnodes/simple/node/MulNode.java28
-rw-r--r--src/main/java/com/seaofnodes/simple/node/MultiUse.java5
-rw-r--r--src/main/java/com/seaofnodes/simple/node/NewNode.java17
-rw-r--r--src/main/java/com/seaofnodes/simple/node/Node.java103
-rw-r--r--src/main/java/com/seaofnodes/simple/node/ParmNode.java3
-rw-r--r--src/main/java/com/seaofnodes/simple/node/PhiNode.java4
-rw-r--r--src/main/java/com/seaofnodes/simple/node/ProjNode.java6
-rw-r--r--src/main/java/com/seaofnodes/simple/node/ReadOnlyNode.java1
-rw-r--r--src/main/java/com/seaofnodes/simple/node/RegionNode.java66
-rw-r--r--src/main/java/com/seaofnodes/simple/node/ReturnNode.java3
-rw-r--r--src/main/java/com/seaofnodes/simple/node/ScopeNode.java6
-rw-r--r--src/main/java/com/seaofnodes/simple/node/StartNode.java7
-rw-r--r--src/main/java/com/seaofnodes/simple/node/StopNode.java1
-rw-r--r--src/main/java/com/seaofnodes/simple/node/StoreNode.java49
-rw-r--r--src/main/java/com/seaofnodes/simple/node/XCtrlNode.java3
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/AddFRISC.java30
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/AddIRISC.java39
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/AddRISC.java36
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/AndIRISC.java40
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/AndRISC.java41
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/CBranchRISC.java49
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/CallRISC.java37
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/CallRRISC.java31
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/CmpIRISC.java44
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/CmpRISC.java28
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/DivFRISC.java30
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/DivRISC.java33
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/F8ToIRISC.java27
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/FltRISC.java39
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/FunRISC.java22
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/I2F8RISC.java29
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/IntRISC.java43
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/LoadRISC.java34
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/MemOpRISC.java39
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/MulFRISC.java30
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/MulIRISC.java35
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/MulRISC.java33
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/NewRISC.java44
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/OrIRISC.java43
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/OrRISC.java43
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/ParmRISC.java25
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/ProjRISC.java23
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/RetRISC.java48
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/SetRISC.java35
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/SllIRISC.java40
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/SllRISC.java36
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/SraIRISC.java43
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/SraRISC.java42
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/SrlIRISC.java43
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/SrlRISC.java38
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/StoreRISC.java40
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/SubFRISC.java30
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/SubRISC.java29
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/TFPRISC.java43
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/XorIRISC.java45
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/XorRISC.java43
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/riscv.java332
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddFMemX86.java33
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddFX86.java29
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddIX86.java39
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddMemX86.java25
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddX86.java29
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AndIX86.java42
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AndX86.java33
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CallEndX86.java37
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CallRX86.java31
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CallX86.java41
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CmpIX86.java43
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CmpMemX86.java33
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CmpX86.java28
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/DivFX86.java33
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/DivX86.java31
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/F8ToIX86.java27
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/FltX86.java36
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/FunX86.java21
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/I2f8X86.java28
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/IntX86.java45
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/JmpX86.java46
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/LeaX86.java42
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/LoadX86.java25
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MemAddX86.java28
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MemOpX86.java90
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MulFX86.java30
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MulIX86.java36
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MulX86.java30
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/NewX86.java40
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/OrIX86.java44
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/OrX86.java33
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ParmX86.java23
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ProjX86.java25
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/RetX86.java48
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SarIX86.java37
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SarX86.java42
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SetX86.java39
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShlIX86.java38
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShlX86.java39
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShrIX86.java40
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShrX86.java38
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/StoreX86.java31
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SubFX86.java29
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SubX86.java29
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/TFPX86.java41
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/XorIX86.java42
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/x86_64_v2.java443
-rw-r--r--src/main/java/com/seaofnodes/simple/print/SimpleGraphAdapter.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/print/SimpleGraphObserver.java9
-rw-r--r--src/main/java/com/seaofnodes/simple/type/Type.java4
-rw-r--r--src/main/java/com/seaofnodes/simple/type/TypeFunPtr.java68
-rw-r--r--src/main/java/com/seaofnodes/simple/type/TypeStruct.java8
-rw-r--r--src/test/java/com/seaofnodes/simple/BrainFuckTest.java2
-rw-r--r--src/test/java/com/seaofnodes/simple/Chapter03Test.java2
-rw-r--r--src/test/java/com/seaofnodes/simple/Chapter04Test.java21
-rw-r--r--src/test/java/com/seaofnodes/simple/Chapter05Test.java64
-rw-r--r--src/test/java/com/seaofnodes/simple/Chapter06Test.java30
-rw-r--r--src/test/java/com/seaofnodes/simple/Chapter08Test.java4
-rw-r--r--src/test/java/com/seaofnodes/simple/Chapter09Test.java4
-rw-r--r--src/test/java/com/seaofnodes/simple/Chapter11Test.java10
-rw-r--r--src/test/java/com/seaofnodes/simple/Chapter12Test.java2
-rw-r--r--src/test/java/com/seaofnodes/simple/Chapter15Test.java12
-rw-r--r--src/test/java/com/seaofnodes/simple/Chapter17bTest.java8
-rw-r--r--src/test/java/com/seaofnodes/simple/Chapter18Test.java58
-rw-r--r--src/test/java/com/seaofnodes/simple/Chapter19Test.java529
-rw-r--r--src/test/java/com/seaofnodes/simple/Eval2.java6
-rw-r--r--src/test/java/com/seaofnodes/simple/FuzzerWrap.java7
-rw-r--r--src/test/java/com/seaofnodes/simple/Main.java17
-rw-r--r--src/test/java/com/seaofnodes/simple/evaluator/Evaluator.java13
-rw-r--r--src/test/java/com/seaofnodes/simple/evaluator/Scheduler.java2
-rw-r--r--src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java15
-rw-r--r--src/test/java/com/seaofnodes/simple/fuzzer/FuzzerUtils.java36
-rw-r--r--src/test/java/com/seaofnodes/simple/fuzzer/ScriptGenerator.java16
-rw-r--r--src/test/java/com/seaofnodes/simple/progs/fcn.smp15
-rw-r--r--src/test/java/com/seaofnodes/simple/progs/fcn2.smp5
-rw-r--r--src/test/java/com/seaofnodes/simple/progs/fcn3.smp42
-rw-r--r--src/test/java/com/seaofnodes/simple/progs/sieve.smp32
175 files changed, 7506 insertions, 736 deletions
diff --git a/Makefile b/Makefile
index 11a698a8..1102b788 100644
--- a/Makefile
+++ b/Makefile
@@ -34,7 +34,7 @@ SIMPLE := com/seaofnodes/simple
SRC := src/main/java
TST := src/test/java
CLZDIR:= build/classes
-main_javas := $(wildcard $(SRC)/$(SIMPLE)/*java $(SRC)/$(SIMPLE)/*/*java)
+main_javas := $(wildcard $(SRC)/$(SIMPLE)/*java $(SRC)/$(SIMPLE)/*/*java $(SRC)/$(SIMPLE)/node/cpus/*/*java)
test_javas := $(wildcard $(TST)/$(SIMPLE)/*java $(TST)/$(SIMPLE)/*/*java)
main_classes := $(patsubst $(SRC)/%java,$(CLZDIR)/main/%class,$(main_javas))
include $(firstword $(wildcard ../graph/graph.mk graph/graph.mk))
diff --git a/README.ja.md b/README.ja.md
index 2ce5ce95..c2904f40 100644
--- a/README.ja.md
+++ b/README.ja.md
@@ -56,7 +56,7 @@ Simple 言語のスタイルは、C または Java のサブセットに倣っã�
* [第17a章](docs/chapter17a/README.ja.md):束縛の可変性、参照の権限、深い読み取り専用ビュー。
* [第17b章](docs/chapter17b/README.ja.md):糖衣構文。`var`、`val`、`x+=y`、`for(init; test; next) body`。
* [第18章](docs/chapter18/README.ja.md):関数と呼び出し。
-* 第19章:命令選択と移植可能なコンパイル。
+* [第19章](docs/chapter19/README.ja.md):命令選択と移植可能なコンパイル。
* 第20章:グラフ彩色によるレジスタ割り当て。
* 第21章:命令エンコーディングと ELF。
* 第22章:Simple の Hello, World!
diff --git a/README.md b/README.md
index 88cfd152..15fcc493 100644
--- a/README.md
+++ b/README.md
@@ -65,7 +65,7 @@ The Simple language is styled after a subset of C or Java.
* [Chapter 17a](docs/chapter17a/README.md): Binding mutability, reference permissions, and deep read-only views.
* [Chapter 17b](docs/chapter17b/README.md): Syntax sugar: `var`, `val`, `x+=y`, `for(init; test; next) body`
* [Chapter 18](docs/chapter18/README.md): Functions and calls.
-* Chapter 19: Instruction selection and portable compilation
+* [Chapter 19](docs/chapter19/README.md): Instruction selection and portable compilation
* Chapter 20: Graph Coloring Register Allocation
* Chapter 21: Instruction Encodings & ELF
* Chapter 22: A Simple Hello, World!
diff --git a/docs/chapter19/Makefile b/docs/chapter19/Makefile
new file mode 100644
index 00000000..12659b8f
--- /dev/null
+++ b/docs/chapter19/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/chapter19/README.ja.md b/docs/chapter19/README.ja.md
new file mode 100644
index 00000000..c52ff522
--- /dev/null
+++ b/docs/chapter19/README.ja.md
@@ -0,0 +1,149 @@
+# 第19章: 命令選択と移植可能なコンパイル
+
+[English](README.md) | 日本語
+
+この文書は [原文](README.md) の日本語訳です。
+
+[前の章: 第18章](../chapter18/README.ja.md) |
+[次の章: 第20章](../chapter20/README.ja.md)
+
+[linear](https://github.com/SeaOfNodes/Simple/tree/linear) ブランチ上の直線的な Git リビジョン履歴で [この章](https://github.com/SeaOfNodes/Simple/tree/linear-chapter19) を読み、前の章と [比較](https://github.com/SeaOfNodes/Simple/compare/linear-chapter18...linear-chapter19) することもできます。
+
+文法は[第18章](../chapter18/18-grammar.ja.md)から変わりません。
+
+## 命令選択、レジスタ割り当て、命令エンコーディングにまつわる全体的な問題と開発上の問題
+
+命令選択の出力、すなわち理想化された Simple ノードからマシン固有のノードへの対応付けは、実行方法がないと大量にテストするのが困難です。命令選択、レジスタ割り当て、命令エンコーディングの3つすべてが完成するまでは、実行方法が得られません。そのため、エンコーディングの完成前後には、命令選択とレジスタ割り当てで多くのバグが見つかると予想しています。もちろん出力を手作業で調べて明らかな問題は修正しますが、実際にコードを動かせるようになるまで、多くのバグが残ります。
+
+つまり、この章で説明する作業に対する更新やバグ修正が、ほかの章に含まれています。
+
+## 移植可能なコンパイル
+
+CPU の移植先ごとに1つの、新しい抽象 `Machine` クラスを追加します。`Machine` は移植先の名前、すべてのレジスタ名、そして *理想* ノードからマシンノードを生成する方法を定義します。さらに、コード生成時にマシン非依存のコンパイラ部分が使う *split* 演算と *jump* の生成も可能にします。
+
+各種呼び出し規約を選択できるように、新しい CallingConv 列挙型を追加します。
+
+## 命令選択フェーズ
+
+*大域的コード移動* の前に、新しい *InstructionSelection* コンパイルフェーズを追加します。これは任意ですが、*理想* ノードをマシン固有の派生形に置き換えます。命令選択には対象 CPU 名を渡し、その時点で移植コードを検索して使用します。これによって、少なくとも Java 実装では CPU 移植先を遅延して追加できます。
+
+## マシン固有のノード
+
+`MachNode` インターフェースを実装する「マシン固有」のノードという概念を追加します。`MachNode` ノードは、各命令の入力・出力レジスタ、ビットエンコーディング、利用者向けの表現を定義します。後ほど、局所スケジュールを改善するための機能ユニット情報を追加します。
+
+## CodeGen コンパイルドライバの整理
+
+さらに多くの静的なグローバル状態を、すべての最上位ドライバ間で受け渡す CodeGen オブジェクトに移します。Node のコンストラクタには渡さないため、idealize() 呼び出しにも渡しません。静的なグローバル状態が必要なケースの大半は、依然としてこれらです。要するに、コンパイラはずっと関数型に近づき、並行コンパイルやマルチスレッドコンパイルに向けて大きく前進します。
+
+## 命令選択を通じたメモリの扱い
+
+パーサーは1つの `$mem` 束縛を保持します。BulkMemPhi は必要に応じてエイリアスを並列の MemPhi に分割し、MemMerge は既定のメモリと明示的なスライスをまとめます。両方のマシン選択器は、これらのノードクラスとエイリアス情報を保持します。メモリの Phi を通常の値の Phi には変換しません。
+
+この章では、コンストラクタの非ゼロ値を明示的な Store に変換し、割り当てそのものはゼロ初期化演算として維持します。New は `{ctrl, $mem, size}` を消費し、`{ptr, $mem}` を生成します。その入力は、割り当てる構造体のエイリアスをカバーする部分的な MemMerge です。単一のメモリ結果も、同じエイリアスをカバーします。パーサーは周囲の集合体に無関係なメモリを保持します。割り当てのサイズレジスタは入力2、ポインタ結果は出力0で、メモリにはレジスタがありません。
+
+選択されたメモリ演算は、x86 の算術命令や比較命令が Load を吸収しても、フィールドのエイリアスを保持します。そのため大域的スケジューリングは、共通の MemOpNode を通じ、MemMerge 内の該当スライスをたどって反依存関係を見つけます。メモリを結果とする命令は書き手で、ほかのメモリ演算は読み手です。書き手の末尾にある順序付け辺は、レジスタを要求しません。Phi の因数分解で使用する単純な1段階の Load ガードは変更しません。
+
+Call は依然としてメモリ全体を消費し、生成します。Call はブロックを終了するため、スケジューリングは引数リストに入力を追加せず、読み取りを Call の前に引き上げます。直接マシン呼び出しは定数の対象を埋め込み、制御、メモリ、引数の入力だけを保持します。
+
+## レジスタとレジスタマスク
+
+`Machines` はレジスタを小さな密な整数として定義します。レジスタ割り当て器は割り当て時にこれらの番号を使います。小さな整数からマシンレジスタへの対応は移植者に任されますが、通常は明らかな1対1の対応があります。X86 では RAX がレジスタ0、RCX がレジスタ1となり、16個の GPR まで続きます。XMM レジスタ番号は16から31です。レジスタ番号は一意でなければなりません。割り当て器はこれによってレジスタを追跡します。
+
+レジスタマスククラス `RegMask` は、許可されたレジスタのビット集合を保持します。大半のマシン演算は全レジスタの部分集合に制限され、許可集合を RegMask で記述します。大半の RegMask は、たとえば `Mul` 演算で許可されるレジスタ集合のように不変ですが、割り当て器は可変のレジスタ集合も広範囲に使います。
+
+呼び出し規約は多くの固定レジスタを指定し、実際には許可レジスタ集合をかなり「縛る」傾向があります。
+
+## SystemV または Win64 呼び出し規約を持つ X86_64_V2 移植
+
+`node/cpus/x86_64_v2` ディレクトリには、X86 64 V2 への移植 `x86_64_v2.java` があります。16個の64ビット GPR、16個の XMM レジスタ、X86 フラグをサポートします。理想化された Simple の Sea-of-Nodes から X86 演算を対応付けるパターンマッチャがあります。アドレッシングモードを除けば、理想ノードから X86 演算へほぼ1対1で対応付けます。
+
+### 通常の命令選択
+
+instSelect フェーズは理想グラフを走査し、理想演算からマシン演算への対応を保持します。先着順で最初に一致したものを採用する、貪欲なパターンマッチを行います。特別な動作のない演算は局所的な検索だけを行います。たとえば `Mul` は X86 の `MulX86` に直接対応します。大半のマッチの右辺は即値定数にでき、即値版(`MulIX86`)に対応します。
+
+### アドレッシングモード
+
+多くの X86 演算では、入力の1つが `Load` なら、メモリを入力にする派生形を使えます。X86 マッチャの専用の `address()` 呼び出しが各種アドレッシングモードを探し、メモリ専用の派生形を構築します。これらは個別に明示する必要がありますが、単純な1行のマッチと、メモリ版を割り当てる1行で済みます。
+
+### Compare と Jmp
+
+Simple の `BoolNode` は `0/1` の値を生成します。X86 では `Cmp/SetXX` という命令列で行います。X86 の `Jxx` は `0/1` ではなく `FLAGS` を受け取るので、`SetXX` を飛ばして直接 `Cmp` を使います。理想 `Bool` を直接使う場合は `SetXX` が必要です。つまり `return a==3` は `CmpX86 RAX,#3; SetEQ RAX; ret;` に対応し、`if( a==3 )` は `CmpX86 RAX,#3; jeq;` に対応します。
+
+## RISC-V 移植
+
+`node/cpus/riscv` ディレクトリには RISC-V への移植 `riscv.java` があります。32個の64ビット GPR と32個の浮動小数点レジスタをサポートします。理想化された Simple の Sea-of-Nodes から riscv 演算を対応付けるパターンマッチャがあります。現在の対象は `RVA23U64` です。
+
+### 通常の命令選択
+
+**X86_64_V2** と同様に動作します。riscv には *複雑な* アドレッシングモードがないため、ほとんどの場合は貪欲なパターンマッチを適用しません。特別な動作のない演算は、単純に検索します。
+
+### 制限された ISA
+
+RISC-V は単純なアドレッシングモードとオーバーロードを備える、制限された ISA です。たとえば `lea` はありません。
+
+### 分岐
+
+`x86_64_v2` の分岐は、フラグを設定する比較命令と、そのフラグを使うジャンプ命令を必要とします。一方 RISC-V は、必要なオペランドをすべて分岐命令そのものに含め、別の比較ステップを不要にします。
+
+例:
+
+```
+beq x10, x11, some_label
+```
+
+`CBranchRISC` がこの動作を実装します。
+
+### R フラグを持たない
+
+RISC-V には R フラグがありません。同様の機能を得るには、比較結果に応じてレジスタ値を明示的に設定できます。
+
+```
+xor a0, a0, a1
+seqz a0, a0
+```
+
+最初の `xor` の後、`a0` と `a1` が等しければ `a0` はゼロになります。`seqz` 命令は、前の演算で `a0` がゼロになっていれば `a0` を1に設定します。これは `nodes/cpus/riscv` 移植の `SetRISC` に対応します。
+
+### 固定長命令
+
+RISC-V の命令長は(ほとんどの場合)32ビット固定であり、エンコーディング側で変位を活用できます。
+
+例:
+
+```
+flw fa5, 0(a0)
+flw fa4, 12(a0)
+fadd.s fa0, fa5, fa4
+```
+
+(`flw` の前にロードは必要ありません。)
+
+### ABI 名(レジスタ)
+
+`RISC-V` の汎用レジスタ(`GPR`)は `x0` から `x31`、浮動小数点レジスタは `f0` から `f31` です。
+
+読みやすさを高め、呼び出し規約と整合させるため、RISC-V はレジスタの ABI 名を定義し、生の番号よりも直感的な別名を提供します。
+
+| レジスタ | 別名 | 用途 | 保存する側 |
+|----------|-------|------------------------------|----------|
+| x10 | a0 | 関数の引数/戻り値 | 呼び出し側 |
+| x11 | a1 | 関数の引数/戻り値 | 呼び出し側 |
+| x12 | a2 | 関数の引数 | 呼び出し側 |
+| x13 | a3 | 関数の引数 | 呼び出し側 |
+| x14 | a4 | 関数の引数 | 呼び出し側 |
+| x15 | a5 | 関数の引数 | 呼び出し側 |
+| x16 | a6 | 関数の引数 | 呼び出し側 |
+| x17 | a7 | 関数の引数 | 呼び出し側 |
+
+関数に引数を渡すとき、呼び出し側からの最初の8個の引数はレジスタ `a0` から `a7` で渡します。
+
+### 浮動小数点値のロード
+
+現在、浮動小数点定数は、浮動小数点レジスタに変換する前に、まず `integer GPR` にロードします。この処理は RA フェーズで次のように書き換えます。
+
+````
+lui a0, 262144
+fmv.w.x fa0, a0
+````
+
+非定数値には、GPR と FPR の両方をサポートする、より広いレジスタマスクを使います。この実装は `nodes/cpus/riscv/LoadRISC` にあります。
diff --git a/docs/chapter19/README.md b/docs/chapter19/README.md
new file mode 100644
index 00000000..4d9e0572
--- /dev/null
+++ b/docs/chapter19/README.md
@@ -0,0 +1,236 @@
+# Chapter 19: Instruction Selection and Portable Compilation
+
+English | [日本語](README.ja.md)
+
+[Previous: Chapter 18](../chapter18/README.md) |
+[Next: Chapter 20](../chapter20/README.md)
+
+You can also read [this chapter](https://github.com/SeaOfNodes/Simple/tree/linear-chapter19) 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-chapter18...linear-chapter19) it to the previous chapter.
+
+
+The grammar is unchanged from [Chapter 18](../chapter18/18-grammar.md).
+
+## Meta-Issues / Code-Dev Issues of Instruction Selection, Register Allocation and Encodings
+
+The output of Instruction Selection - the mapping from idealized Simple Nodes
+to machine-specific ones - is difficult to test in bulk without an execution
+strategy. We won't have an execution strategy until all three of Instruction
+Selection, Register Allocation and Encodings are done. Hence in and around the
+completion of Encoding we expect to find lots of bugs in Instruction Selection
+and Register Allocation. We certainly hand-inspect the output and fix obvious
+problems, but still lots of bugs linger until we can actually run the code.
+
+This means that other chapters have updates and bug-fixes to the work being
+described here.
+
+
+## Portable Compilation
+
+A new abstract `Machine` class is added, one per unique CPU port. `Machine`
+defines a name for the port, names for all registers, and how to generate
+machine Nodes from *ideal* Nodes. `Machine` also allows generating *split* ops
+and *jumps*, both used by the machine independent compiler parts during
+code-gen.
+
+A new CallingConv enum is added, to allow picking between various calling conventions.
+
+## The Instruction Selection Phase
+
+A new *InstructionSelection* compile phase is added before *Global Code
+Motion*. It is optional, but will replace the *ideal* Nodes with machine-
+specific variants. Instruction selection is called with the target CPU name;
+the porting code is looked up and used then. This allows the Java
+implementation at least the ability to add CPU ports lazily.
+
+
+## Machine Specific Nodes
+
+We add a notion of "machine specific" Nodes, which implement the `MachNode`
+interface. `MachNode` nodes define incoming and outgoing registers for every
+instruction, their bit encodings and user representations. Later we'll add
+function unit information for a better local schedule.
+
+
+## Clean up CodeGen compile driver
+
+More static globals move into the CodeGen object, which is passed around at all
+the top level drivers. It's not passed into the Node constructors, hence not
+into the idealize() calls, which remains the majority of cases needing a static
+global. In short the compiler gets a lot more functional, and a big step
+towards concurrent or multi-threaded compilation.
+
+
+## Memory through instruction selection
+
+The parser carries one `$mem` binding. BulkMemPhi splits aliases on demand into
+parallel MemPhis, while MemMerge packages a default memory and explicit slices.
+Both machine selectors preserve these node classes and their alias information;
+they do not turn memory Phis into ordinary value Phis.
+
+This chapter lowers nonzero constructor values into explicit Stores, keeping the
+allocation itself a zeroing operation. New consumes `{ctrl, $mem, size}` and
+produces `{ptr, $mem}`. Its input is a partial MemMerge covering the allocated
+struct's aliases; its single memory result covers those same aliases. The parser
+preserves unrelated memory in the surrounding aggregate. The allocation's size
+register is input 2, its pointer result is output 0, and memory has no register.
+
+Selected memory operations retain their field alias even when an x86 arithmetic
+or comparison instruction absorbs a Load. Global scheduling therefore finds
+anti-dependencies through the common MemOpNode, following the relevant slice
+through MemMerge. Memory-result instructions are writers; other memory operations
+are readers. Trailing ordering edges on writers impose no register requirement.
+The simple one-step Load guard used when factoring Phis is unchanged.
+
+Calls still consume and produce whole memory. They terminate their blocks, so
+scheduling raises a read before a Call without adding inputs to its argument
+list. Direct machine calls embed their constant target and retain only control,
+memory, and argument inputs.
+
+
+## Registers and Register Masks
+
+`Machines` define registers as small dense integers. The Register Allocator
+will use these numbers when allocating. The mappings from small integers to
+machine registers is up to the porter, but there's usually an obvious
+one-to-one. For the X86, RAX will be register 0, RCX register 1 and so on up
+to the 16 GPRs. The XMM register numbers start at 16 and go through 31. Register numbers
+must be unique; this is how the register allocator tracks them.
+
+A `RegMask` Register Mask class holds onto bitsets of allowed registers. Most
+machine ops are limited to some subset of all registers, and the allowed sets
+are written as a RegMask. Most RegMasks are immutable, e.g. the set of allowed
+registers into a `Mul` operation, but the register allocator will make
+extensive use of mutable register sets.
+
+Calling conventions dictate many fixed registers, and in practice tend to
+really "bind up" the set of allowed registers.
+
+
+## An X86_64_V2 port, with SystemV or Win64 calling conventions
+
+In the `node/cpus/x86_64_v2` directory is a `x86_64_v2.java` port to an X86 64
+V2. This port supports 16 64-bit GPRs, 16 XMM registers and the X86 flags.
+There is a pattern matcher for matching X86 ops from idealized Simple
+Sea-of-Nodes. Except for the addressing modes pattern matcher matches nearly
+one-for-one X86 ops from ideal nodes.
+
+### Normal instruction selection
+
+The instSelect phase walks the ideal graph, and maintains a mapping
+from ideal-to-machine ops. It does a greedy pattern match, first come first
+match. Ops without any special behavior simply do the local lookup e.g. an
+`Mul` exactly matches to an X86 `MulX86` directly. The RHS of most matches can
+be an immediate constant, matching an immediate-flavored version (`MulIX86`).
+
+### Addressing Modes
+
+For many X86 operations, if one of the inputs is a `Load`, the X86 can use an
+op-from-memory variant. There's a specific `address()` call in the X86 matcher
+that looks for the various kinds of addressing modes and will build memory-
+specific variations. These all need to be called out specifically, but with a
+simple one-line match and one-line allocation of the memory version.
+
+### Compare vs Jmp
+
+In simple, a `BoolNode` produces a `0/1` value. On an X86 this is done with
+the `Cmp/SetXX` opcode sequence. The X86 `Jxx` takes in `FLAGS` and not a
+`0/1`, so the `Jxx` will skip the `SetXX` and directly use the `Cmp`. Using
+the ideal `Bool` directly will require the `SetXX`. i.e. `return a==3` will
+match to a `CmpX86 RAX,#3; SetEQ RAX; ret;` whereas a `if( a==3 )` will match
+to a `CmpX86 RAX,#3; jeq;`
+
+## RISC-V port
+
+In the `node/cpus/riscv` directory is a `riscv.java` port to a RISC-V.
+This port supports 32 64-bit GPRs, and 32 floating-point registers.
+There is a pattern matcher for matching riscv ops from idealized Simple
+Sea-of-Nodes. Currently, we are targeting `RVA23U64`.
+
+### Normal instruction selection
+
+Works the same way as **X86_64_V2**, since riscv doesn't have *complex* addressing modes, greedy
+pattern matching doesn't apply to most of the cases. Ops without any special behavior simply do the lookup.
+
+### Restricted ISA
+RISC-V is a restricted ISA with simple addressing modes and overloads.
+E.g `lea` is not present.
+
+
+### Branching
+
+In `x86_64_v2`, branching requires a comparison instruction to set flags,
+followed by a jump instruction that uses those flags. In contrast, RISC-V
+includes all necessary operands within the branch instruction itself,
+eliminating the need for a separate comparison step.
+
+E.g
+```
+beq x10, x11, some_label
+```
+
+`CBranchRISC` implements this behavior.
+
+### No R-Flags
+
+No R-flags exist in RISC-V. To obtain similar functionality:
+We can set the value of registers based on the result of the comparison this way explicitly.
+```
+xor a0, a0, a1
+seqz a0, a0
+```
+After the first `xor` - `a0` will be zero if `a0` and `a1` are equal.
+The `seqz` instruction will set `a0` to 1 if `a0` is zero from the previous operation.
+This matches `SetRISC` in the `nodes/cpus/riscv` port.
+
+### Fixed instruction length
+
+RISC-V instructions have fixed length of 32 bits(in most cases) which means
+that we can benefit from displacements in the encoding side.
+
+E.g
+```
+flw fa5, 0(a0)
+flw fa4, 12(a0)
+fadd.s fa0, fa5, fa4
+```
+(No load is needed prior to `flw`)
+
+### ABI names(registers)
+In `RISC-V`, general-purpose registers (`GPRs`) range from `x0` to `x31`, and floating-point registers range
+from `f0` to `f31`.
+
+To enhance readability and align with calling conventions, RISC-V defines ABI names for registers, providing more intuitive aliases
+instead of raw register numbers.
+
+
+
+| Register | Alias | Usage | Saved By |
+|----------|-------|------------------------------|----------|
+| x10 | a0 | Function arguments/return values | Caller |
+| x11 | a1 | Function arguments/return values | Caller |
+| x12 | a2 | Function arguments | Caller |
+| x13 | a3 | Function arguments | Caller |
+| x14 | a4 | Function arguments | Caller |
+| x15 | a5 | Function arguments | Caller |
+| x16 | a6 | Function arguments | Caller |
+| x17 | a7 | Function arguments | Caller |
+
+When passing in arguments to a function, the first 8 arguments from the caller are passed in registers `a0` to `a7`.
+
+### Load floats
+Currently, floating-point constants are first loaded into an `integer GPR` before being converted into a floating-point register.
+This process is rewritten in the RA stage as:
+
+
+````
+lui a0, 262144
+fmv.w.x fa0, a0
+````
+For non-constant values, we use a broader register mask that
+supports both GPRs and FPRs. This implementation can be found in:
+`nodes/cpus/riscv/LoadRISC`.
+
+
+
+
+
diff --git a/docs/chapter19/font-awesome-4.7.0.zip b/docs/chapter19/font-awesome-4.7.0.zip
new file mode 100644
index 00000000..b6bdb4bd
--- /dev/null
+++ b/docs/chapter19/font-awesome-4.7.0.zip
Binary files differ
diff --git a/docs/chapter19/lattice.gv b/docs/chapter19/lattice.gv
new file mode 100644
index 00000000..8661082d
--- /dev/null
+++ b/docs/chapter19/lattice.gv
@@ -0,0 +1,203 @@
+digraph Lattice {
+ compound = true;
+ // Define nodes
+ splines=false;
+ node [shape=circle, style=filled];
+ nodesep=0.2;
+ ranksep=0.2;
+
+ TOP [label=<<B>⊤</B >>, fillcolor=black, fontcolor=white];
+ BOT [label=<<B>⊥</B>>, fillcolor=black, fontcolor=white];
+
+ // ----------------------
+ // Control
+ XCTRL [label=<<B>⊤</B >:ctrl>, fillcolor=yellow, shape=oval];
+ CTRL [label=<<B>⊥</B>:ctrl>, fillcolor=yellow, shape=oval];
+
+ TOP -> XCTRL;
+ XCTRL -> CTRL;
+ CTRL -> BOT;
+
+ // ----------------------
+ // Integers
+ INT_TOP [label=<<B>⊤</B >:int>, fillcolor=lightblue, shape=oval];
+ INT_BOT [label=<<B>⊥</B>:int>, fillcolor=lightblue, shape=oval];
+
+ INT_HI [label="[min>max]" , fillcolor=lightblue, shape=oval];
+ INT_XBOOL [label="[1...0]", fillcolor=lightblue, shape=oval];
+ INT_CON [label="constants", fillcolor=lightblue, shape=oval];
+ INT_CON0 [label="0", fillcolor=lightblue];
+ INT_CON1 [label="1", fillcolor=lightblue];
+ INT_BOOL [label="[0...1]", fillcolor=lightblue,shape=oval];
+ INT_LO [label="[min<max]" , fillcolor=lightblue, shape=oval];
+
+ TOP -> INT_TOP;
+ INT_TOP -> INT_HI;
+ INT_HI -> INT_XBOOL;
+ INT_XBOOL -> INT_CON0;
+ INT_XBOOL -> INT_CON1;
+ INT_HI -> INT_CON;
+ INT_CON0 -> INT_BOOL;
+ INT_CON1 -> INT_BOOL;
+ INT_BOOL -> INT_LO;
+ INT_CON -> INT_LO;
+ INT_LO -> INT_BOT;
+ INT_BOT -> BOT;
+
+ INT_CON0 -> INT_CON1 -> INT_CON [style=invis];
+
+ // ----------------------
+ // Floats
+ FLT_TOP [label=<<B>⊤</B >:flt>, fillcolor=aqua, shape=oval];
+ FLT_BOT [label=<<B>⊥</B>:flt>, fillcolor=aqua, shape=oval];
+
+ FLT_CON64 [label="64b consts\n[excludes\n32b consts]" , fillcolor=aqua, shape=oval];
+ FLT_CON32 [label="32b consts", fillcolor=aqua, shape=oval];
+ FLT_CONPI [label="3.14159...", fillcolor=aqua, shape=oval];
+ FLT_X32 [label="~f32", fillcolor=aqua];
+ FLT_CON0 [label="0." , fillcolor=aqua];
+ FLT_32 [label="f32" , fillcolor=aqua];
+
+ TOP -> FLT_TOP;
+ FLT_TOP -> FLT_CON64
+ FLT_TOP -> FLT_CONPI
+ FLT_CON64 -> FLT_BOT
+ FLT_CONPI -> FLT_BOT
+
+ FLT_TOP -> FLT_X32;
+ FLT_X32 -> FLT_CON0;
+ FLT_X32 -> FLT_CON32;
+ FLT_CON0 -> FLT_32;
+ FLT_CON32 -> FLT_32;
+ FLT_32 -> FLT_BOT
+
+ FLT_BOT -> BOT;
+
+ FLT_CON0 -> FLT_CON32 -> FLT_CONPI -> FLT_CON64 [style=invis];
+
+
+ // ----------------------
+ // Pointers, just a boolean nil/not and a struct
+ PTR [shape=none, fillcolor=none, label=<
+ <TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
+ <TR><TD BGCOLOR="green"><FONT color="white">*ptr</FONT></TD></TR>
+ <TR><TD BGCOLOR="lightgreen">struct</TD></TR>
+ </TABLE>
+ >];
+ PTR0 [shape=none, fillcolor=none, label=<
+ <TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
+ <TR><TD BGCOLOR="green"><FONT color="white">*ptr?</FONT></TD></TR>
+ <TR><TD BGCOLOR="lightgreen">struct</TD></TR>
+ </TABLE>
+ >];
+
+ TOP -> PTR;
+ PTR -> PTR0;
+ PTR0 -> BOT;
+
+ // ----------------------
+ // Structs
+ STRUCT_TOP [label=<<B>⊤</B >:struct>, fillcolor=lightgreen, shape=oval];
+ STRUCT_BOT [label=<<B>⊥</B>:struct>, fillcolor=lightgreen, shape=oval];
+
+ STRUCT_S1 [shape=none, fillcolor=none, label=<
+ <TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
+ <TR><TD BGCOLOR="lightgreen">field</TD><TD BGCOLOR="lightblue:green" gradientangle="315">int/ptr</TD></TR>
+ </TABLE>
+ >];
+ STRUCT_S2 [shape=none, fillcolor=none, label=<
+ <TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
+ <TR><TD BGCOLOR="lightgreen">field</TD><TD BGCOLOR="lightblue:green" gradientangle="315">int/ptr</TD></TR>
+ <TR><TD BGCOLOR="lightgreen">field</TD><TD BGCOLOR="lightblue:green" gradientangle="315">int/ptr</TD></TR>
+ </TABLE>
+ >];
+ STRUCT_SN [shape=none, fillcolor=none, label=<
+ <TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
+ <TR><TD BGCOLOR="lightgreen">field</TD><TD BGCOLOR="lightblue:green" gradientangle="315">int/ptr</TD></TR>
+ <TR><TD BGCOLOR="lightgreen">... </TD><TD BGCOLOR="lightblue:green" gradientangle="315">int/ptr</TD></TR>
+ <TR><TD BGCOLOR="lightgreen">field</TD><TD BGCOLOR="lightblue:green" gradientangle="315">int/ptr</TD></TR>
+ </TABLE>
+ >];
+
+
+ TOP -> STRUCT_TOP;
+ STRUCT_TOP -> STRUCT_S1;
+ STRUCT_TOP -> STRUCT_S2;
+ STRUCT_TOP -> STRUCT_SN;
+ STRUCT_S1 -> STRUCT_BOT;
+ STRUCT_S2 -> STRUCT_BOT;
+ STRUCT_SN -> STRUCT_BOT;
+ STRUCT_BOT -> BOT;
+
+ STRUCT_S1 -> STRUCT_S2 -> STRUCT_SN [style=invis];
+
+ // ----------------------
+ // Memory
+ MEM_TOP [label=<<B>⊤</B >:mem>, fillcolor=blue, fontcolor=white, shape=oval];
+ MEM_BOT [label=<<B>⊥</B>:mem>, fillcolor=blue, fontcolor=white, shape=oval];
+ MEM_ALIAS1 [label="#1" , fillcolor=blue, fontcolor=white];
+ MEM_ALIASDOTS [label="..." , fillcolor=blue, fontcolor=white, shape=oval];
+ MEM_ALIASN [label="#n" , fillcolor=blue, fontcolor=white];
+
+ TOP -> MEM_TOP;
+ MEM_TOP -> MEM_ALIAS1;
+ MEM_TOP -> MEM_ALIASDOTS;
+ MEM_TOP -> MEM_ALIASN;
+ MEM_ALIAS1 -> MEM_BOT;
+ MEM_ALIASDOTS -> MEM_BOT;
+ MEM_ALIASN -> MEM_BOT;
+ MEM_BOT -> BOT;
+
+ MEM_ALIAS1 -> MEM_ALIASDOTS -> MEM_ALIASN [style=invis];
+
+ // ----------------------
+ // Tuples
+
+ TUPLE_TOP [label=<<B>⊤</B >:tuple>, fillcolor=pink, shape=oval];
+ TUPLE_BOT [label=<<B>⊥</B>:tuple>, fillcolor=pink, shape=oval];
+
+ TUPLE1 [shape=none, fillcolor=none, label=<
+ <TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
+ <TH><TD BGCOLOR="pink">Tuple</TD></TH>
+ <TR><TD BGCOLOR="black" ><FONT color="white">type</FONT></TD></TR>
+ </TABLE>
+ >];
+ TUPLE2 [shape=none, fillcolor=none, label=<
+ <TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
+ <TH><TD BGCOLOR="pink">Tuple</TD></TH>
+ <TR><TD BGCOLOR="black" ><FONT color="white">type</FONT></TD></TR>
+ <TR><TD BGCOLOR="black" ><FONT color="white">type</FONT></TD></TR>
+ </TABLE>
+ >];
+ TUPLEN [shape=none, fillcolor=none, label=<
+ <TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
+ <TH><TD BGCOLOR="pink">Tuple</TD></TH>
+ <TR><TD BGCOLOR="black" ><FONT color="white">type</FONT></TD></TR>
+ <TR><TD BGCOLOR="black" ><FONT color="white">...</FONT></TD></TR>
+ <TR><TD BGCOLOR="black" ><FONT color="white">type</FONT></TD></TR>
+ </TABLE>
+ >];
+
+
+ TOP -> TUPLE_TOP;
+ TUPLE_TOP -> TUPLE1;
+ TUPLE_TOP -> TUPLE2;
+ TUPLE_TOP -> TUPLEN;
+ TUPLE1 -> TUPLE_BOT;
+ TUPLE2 -> TUPLE_BOT;
+ TUPLEN -> TUPLE_BOT;
+ TUPLE_BOT -> BOT;
+
+ // ----------------------
+ // Additional customization
+ {rank=same; TOP;}
+ {rank=same; INT_TOP; FLT_TOP; XCTRL; PTR; STRUCT_TOP; MEM_TOP; TUPLE_TOP}
+ {rank=same; INT_CON; INT_CON0; INT_CON1;
+ FLT_CON32; FLT_CON64; FLT_CON0; FLT_CONPI;
+ STRUCT_S1; STRUCT_S2; STRUCT_SN;
+ MEM_ALIAS1; MEM_ALIASDOTS; MEM_ALIASN;
+ TUPLE1; TUPLE2; TUPLEN;
+ }
+ {rank=same; INT_BOT; FLT_BOT; CTRL; PTR0; STRUCT_BOT; MEM_BOT; TUPLE_BOT}
+ {rank=same; BOT;}
+}
diff --git a/docs/chapter19/lattice.svg b/docs/chapter19/lattice.svg
new file mode 100644
index 00000000..030f4826
--- /dev/null
+++ b/docs/chapter19/lattice.svg
@@ -0,0 +1,691 @@
+<?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 12.2.0 (20241103.1931)
+ -->
+<!-- Title: Lattice Pages: 1 -->
+<svg width="1330pt" height="584pt"
+ viewBox="0.00 0.00 1329.51 583.54" 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 579.54)">
+<title>Lattice</title>
+<polygon fill="white" stroke="none" points="-4,4 -4,-579.54 1325.51,-579.54 1325.51,4 -4,4"/>
+<!-- TOP -->
+<g id="node1" class="node">
+<title>TOP</title>
+<ellipse fill="black" stroke="black" cx="641.51" cy="-554.42" rx="21.12" ry="21.12"/>
+<text text-anchor="start" x="633.63" y="-549.99" font-family="Times New Roman,serif" font-weight="bold" font-size="14.00" fill="white">⊤</text>
+</g>
+<!-- XCTRL -->
+<g id="node3" class="node">
+<title>XCTRL</title>
+<ellipse fill="yellow" stroke="black" cx="36.51" cy="-492.79" rx="36.51" ry="18"/>
+<text text-anchor="start" x="17.76" y="-488.74" font-family="Times New Roman,serif" font-weight="bold" font-size="14.00">⊤</text>
+<text text-anchor="start" x="33.51" y="-488.74" font-family="Times New Roman,serif" font-size="14.00">:ctrl</text>
+</g>
+<!-- TOP&#45;&gt;XCTRL -->
+<g id="edge1" class="edge">
+<title>TOP&#45;&gt;XCTRL</title>
+<path fill="none" stroke="black" d="M620.33,-551.33C533.82,-542.8 206.01,-510.5 83.28,-498.4"/>
+<polygon fill="black" stroke="black" points="83.86,-494.94 73.56,-497.44 83.17,-501.91 83.86,-494.94"/>
+</g>
+<!-- INT_TOP -->
+<g id="node5" class="node">
+<title>INT_TOP</title>
+<ellipse fill="lightblue" stroke="black" cx="316.51" cy="-492.79" rx="33.95" ry="18"/>
+<text text-anchor="start" x="299.63" y="-488.74" font-family="Times New Roman,serif" font-weight="bold" font-size="14.00">⊤</text>
+<text text-anchor="start" x="315.38" y="-488.74" font-family="Times New Roman,serif" font-size="14.00">:int</text>
+</g>
+<!-- TOP&#45;&gt;INT_TOP -->
+<g id="edge4" class="edge">
+<title>TOP&#45;&gt;INT_TOP</title>
+<path fill="none" stroke="black" d="M620.68,-549.6C568.33,-539.99 430.36,-514.68 359.64,-501.71"/>
+<polygon fill="black" stroke="black" points="360.44,-498.29 349.97,-499.93 359.18,-505.18 360.44,-498.29"/>
+</g>
+<!-- FLT_TOP -->
+<g id="node14" class="node">
+<title>FLT_TOP</title>
+<ellipse fill="aqua" stroke="black" cx="494.51" cy="-492.79" rx="32.41" ry="18"/>
+<text text-anchor="start" x="478.76" y="-488.74" font-family="Times New Roman,serif" font-weight="bold" font-size="14.00">⊤</text>
+<text text-anchor="start" x="494.51" y="-488.74" font-family="Times New Roman,serif" font-size="14.00">:flt</text>
+</g>
+<!-- TOP&#45;&gt;FLT_TOP -->
+<g id="edge18" class="edge">
+<title>TOP&#45;&gt;FLT_TOP</title>
+<path fill="none" stroke="black" d="M621.99,-545.5C598.79,-536.09 559.6,-520.19 530.78,-508.5"/>
+<polygon fill="black" stroke="black" points="532.29,-505.34 521.7,-504.82 529.65,-511.82 532.29,-505.34"/>
+</g>
+<!-- PTR -->
+<g id="node22" class="node">
+<title>PTR</title>
+<polygon fill="none" stroke="none" points="668.51,-519.29 614.51,-519.29 614.51,-466.29 668.51,-466.29 668.51,-519.29"/>
+<polygon fill="green" stroke="none" points="623.51,-492.79 623.51,-515.29 659.51,-515.29 659.51,-492.79 623.51,-492.79"/>
+<polygon fill="none" stroke="black" points="623.51,-492.79 623.51,-515.29 659.51,-515.29 659.51,-492.79 623.51,-492.79"/>
+<text text-anchor="start" x="630.63" y="-498.99" font-family="Times New Roman,serif" font-size="14.00" fill="white">*ptr</text>
+<polygon fill="lightgreen" stroke="none" points="623.51,-470.29 623.51,-492.79 659.51,-492.79 659.51,-470.29 623.51,-470.29"/>
+<polygon fill="none" stroke="black" points="623.51,-470.29 623.51,-492.79 659.51,-492.79 659.51,-470.29 623.51,-470.29"/>
+<text text-anchor="start" x="626.51" y="-476.49" font-family="Times New Roman,serif" font-size="14.00">struct</text>
+</g>
+<!-- TOP&#45;&gt;PTR -->
+<g id="edge33" class="edge">
+<title>TOP&#45;&gt;PTR</title>
+<path fill="none" stroke="black" d="M641.51,-532.9C641.51,-532.06 641.51,-531.22 641.51,-530.37"/>
+<polygon fill="black" stroke="black" points="645.01,-530.61 641.51,-520.61 638.01,-530.61 645.01,-530.61"/>
+</g>
+<!-- STRUCT_TOP -->
+<g id="node24" class="node">
+<title>STRUCT_TOP</title>
+<ellipse fill="lightgreen" stroke="black" cx="746.51" cy="-492.79" rx="44.7" ry="18"/>
+<text text-anchor="start" x="721.76" y="-488.74" font-family="Times New Roman,serif" font-weight="bold" font-size="14.00">⊤</text>
+<text text-anchor="start" x="737.51" y="-488.74" font-family="Times New Roman,serif" font-size="14.00">:struct</text>
+</g>
+<!-- TOP&#45;&gt;STRUCT_TOP -->
+<g id="edge36" class="edge">
+<title>TOP&#45;&gt;STRUCT_TOP</title>
+<path fill="none" stroke="black" d="M659.8,-543.03C674.04,-534.94 694.24,-523.47 711.55,-513.64"/>
+<polygon fill="black" stroke="black" points="713.07,-516.81 720.03,-508.83 709.61,-510.72 713.07,-516.81"/>
+</g>
+<!-- MEM_TOP -->
+<g id="node29" class="node">
+<title>MEM_TOP</title>
+<ellipse fill="blue" stroke="black" cx="998.51" cy="-492.79" rx="43.67" ry="18"/>
+<text text-anchor="start" x="974.51" y="-488.74" font-family="Times New Roman,serif" font-weight="bold" font-size="14.00" fill="white">⊤</text>
+<text text-anchor="start" x="990.26" y="-488.74" font-family="Times New Roman,serif" font-size="14.00" fill="white">:mem</text>
+</g>
+<!-- TOP&#45;&gt;MEM_TOP -->
+<g id="edge46" class="edge">
+<title>TOP&#45;&gt;MEM_TOP</title>
+<path fill="none" stroke="black" d="M662.51,-549.91C717.64,-540.7 867.72,-515.64 947.33,-502.34"/>
+<polygon fill="black" stroke="black" points="947.77,-505.82 957.06,-500.72 946.62,-498.91 947.77,-505.82"/>
+</g>
+<!-- TUPLE_TOP -->
+<g id="node34" class="node">
+<title>TUPLE_TOP</title>
+<ellipse fill="pink" stroke="black" cx="1182.51" cy="-492.79" rx="42.65" ry="18"/>
+<text text-anchor="start" x="1159.26" y="-488.74" font-family="Times New Roman,serif" font-weight="bold" font-size="14.00">⊤</text>
+<text text-anchor="start" x="1175.01" y="-488.74" font-family="Times New Roman,serif" font-size="14.00">:tuple</text>
+</g>
+<!-- TOP&#45;&gt;TUPLE_TOP -->
+<g id="edge56" class="edge">
+<title>TOP&#45;&gt;TUPLE_TOP</title>
+<path fill="none" stroke="black" d="M662.72,-551.08C740.91,-542.46 1014.51,-512.31 1130.21,-499.56"/>
+<polygon fill="black" stroke="black" points="1130.5,-503.05 1140.05,-498.47 1129.73,-496.09 1130.5,-503.05"/>
+</g>
+<!-- BOT -->
+<g id="node2" class="node">
+<title>BOT</title>
+<ellipse fill="black" stroke="black" cx="641.51" cy="-21.12" rx="21.12" ry="21.12"/>
+<text text-anchor="start" x="633.63" y="-16.7" font-family="Times New Roman,serif" font-weight="bold" font-size="14.00" fill="white">⊥</text>
+</g>
+<!-- CTRL -->
+<g id="node4" class="node">
+<title>CTRL</title>
+<ellipse fill="yellow" stroke="black" cx="49.51" cy="-82.75" rx="36.51" ry="18"/>
+<text text-anchor="start" x="30.76" y="-78.7" font-family="Times New Roman,serif" font-weight="bold" font-size="14.00">⊥</text>
+<text text-anchor="start" x="46.51" y="-78.7" font-family="Times New Roman,serif" font-size="14.00">:ctrl</text>
+</g>
+<!-- XCTRL&#45;&gt;CTRL -->
+<g id="edge2" class="edge">
+<title>XCTRL&#45;&gt;CTRL</title>
+<path fill="none" stroke="black" d="M37.09,-474.38C39.15,-409.6 46.05,-191.88 48.57,-112.25"/>
+<polygon fill="black" stroke="black" points="52.06,-112.73 48.88,-102.63 45.06,-112.51 52.06,-112.73"/>
+</g>
+<!-- CTRL&#45;&gt;BOT -->
+<g id="edge3" class="edge">
+<title>CTRL&#45;&gt;BOT</title>
+<path fill="none" stroke="black" d="M85.21,-78.15C191.74,-67.42 506.36,-35.74 608.84,-25.41"/>
+<polygon fill="black" stroke="black" points="609.15,-28.9 618.75,-24.42 608.45,-21.94 609.15,-28.9"/>
+</g>
+<!-- INT_HI -->
+<g id="node7" class="node">
+<title>INT_HI</title>
+<ellipse fill="lightblue" stroke="black" cx="174.51" cy="-434.29" rx="53.91" ry="18"/>
+<text text-anchor="middle" x="174.51" y="-429.24" font-family="Times New Roman,serif" font-size="14.00">[min&gt;max]</text>
+</g>
+<!-- INT_TOP&#45;&gt;INT_HI -->
+<g id="edge5" class="edge">
+<title>INT_TOP&#45;&gt;INT_HI</title>
+<path fill="none" stroke="black" d="M289.8,-481.17C269.89,-473.25 242.26,-462.25 219.03,-453.01"/>
+<polygon fill="black" stroke="black" points="220.37,-449.77 209.78,-449.33 217.78,-456.28 220.37,-449.77"/>
+</g>
+<!-- INT_BOT -->
+<g id="node6" class="node">
+<title>INT_BOT</title>
+<ellipse fill="lightblue" stroke="black" cx="329.51" cy="-82.75" rx="33.95" ry="18"/>
+<text text-anchor="start" x="312.63" y="-78.7" font-family="Times New Roman,serif" font-weight="bold" font-size="14.00">⊥</text>
+<text text-anchor="start" x="328.38" y="-78.7" font-family="Times New Roman,serif" font-size="14.00">:int</text>
+</g>
+<!-- INT_BOT&#45;&gt;BOT -->
+<g id="edge15" class="edge">
+<title>INT_BOT&#45;&gt;BOT</title>
+<path fill="none" stroke="black" d="M361.14,-75.7C420.84,-64.3 549.47,-39.71 609.47,-28.25"/>
+<polygon fill="black" stroke="black" points="609.86,-31.73 619.03,-26.42 608.55,-24.86 609.86,-31.73"/>
+</g>
+<!-- INT_XBOOL -->
+<g id="node8" class="node">
+<title>INT_XBOOL</title>
+<ellipse fill="lightblue" stroke="black" cx="97.51" cy="-373.97" rx="33.95" ry="18"/>
+<text text-anchor="middle" x="97.51" y="-368.92" font-family="Times New Roman,serif" font-size="14.00">[1...0]</text>
+</g>
+<!-- INT_HI&#45;&gt;INT_XBOOL -->
+<g id="edge6" class="edge">
+<title>INT_HI&#45;&gt;INT_XBOOL</title>
+<path fill="none" stroke="black" d="M153.47,-417.36C144.79,-410.78 134.6,-403.06 125.31,-396.03"/>
+<polygon fill="black" stroke="black" points="127.71,-393.46 117.63,-390.21 123.48,-399.04 127.71,-393.46"/>
+</g>
+<!-- INT_CON -->
+<g id="node9" class="node">
+<title>INT_CON</title>
+<ellipse fill="lightblue" stroke="black" cx="174.51" cy="-282.65" rx="45.21" ry="18"/>
+<text text-anchor="middle" x="174.51" y="-277.6" font-family="Times New Roman,serif" font-size="14.00">constants</text>
+</g>
+<!-- INT_HI&#45;&gt;INT_CON -->
+<g id="edge9" class="edge">
+<title>INT_HI&#45;&gt;INT_CON</title>
+<path fill="none" stroke="black" d="M174.51,-416C174.51,-390.27 174.51,-343.1 174.51,-312.31"/>
+<polygon fill="black" stroke="black" points="178.01,-312.52 174.51,-302.52 171.01,-312.52 178.01,-312.52"/>
+</g>
+<!-- INT_CON0 -->
+<g id="node10" class="node">
+<title>INT_CON0</title>
+<ellipse fill="lightblue" stroke="black" cx="47.51" cy="-282.65" rx="18" ry="18"/>
+<text text-anchor="middle" x="47.51" y="-277.6" font-family="Times New Roman,serif" font-size="14.00">0</text>
+</g>
+<!-- INT_XBOOL&#45;&gt;INT_CON0 -->
+<g id="edge7" class="edge">
+<title>INT_XBOOL&#45;&gt;INT_CON0</title>
+<path fill="none" stroke="black" d="M88.34,-356.59C80.82,-343.17 70.07,-323.96 61.49,-308.62"/>
+<polygon fill="black" stroke="black" points="64.63,-307.08 56.69,-300.06 58.53,-310.5 64.63,-307.08"/>
+</g>
+<!-- INT_CON1 -->
+<g id="node11" class="node">
+<title>INT_CON1</title>
+<ellipse fill="lightblue" stroke="black" cx="97.51" cy="-282.65" rx="18" ry="18"/>
+<text text-anchor="middle" x="97.51" y="-277.6" font-family="Times New Roman,serif" font-size="14.00">1</text>
+</g>
+<!-- INT_XBOOL&#45;&gt;INT_CON1 -->
+<g id="edge8" class="edge">
+<title>INT_XBOOL&#45;&gt;INT_CON1</title>
+<path fill="none" stroke="black" d="M97.51,-355.75C97.51,-343.47 97.51,-326.68 97.51,-312.39"/>
+<polygon fill="black" stroke="black" points="101.01,-312.64 97.51,-302.64 94.01,-312.64 101.01,-312.64"/>
+</g>
+<!-- INT_LO -->
+<g id="node13" class="node">
+<title>INT_LO</title>
+<ellipse fill="lightblue" stroke="black" cx="174.51" cy="-141.25" rx="53.91" ry="18"/>
+<text text-anchor="middle" x="174.51" y="-136.2" font-family="Times New Roman,serif" font-size="14.00">[min&lt;max]</text>
+</g>
+<!-- INT_CON&#45;&gt;INT_LO -->
+<g id="edge13" class="edge">
+<title>INT_CON&#45;&gt;INT_LO</title>
+<path fill="none" stroke="black" d="M174.51,-264.17C174.51,-240.45 174.51,-199.04 174.51,-170.9"/>
+<polygon fill="black" stroke="black" points="178.01,-171.25 174.51,-161.25 171.01,-171.25 178.01,-171.25"/>
+</g>
+<!-- INT_CON0&#45;&gt;INT_CON1 -->
+<!-- INT_BOOL -->
+<g id="node12" class="node">
+<title>INT_BOOL</title>
+<ellipse fill="lightblue" stroke="black" cx="97.51" cy="-196.45" rx="33.95" ry="18"/>
+<text text-anchor="middle" x="97.51" y="-191.4" font-family="Times New Roman,serif" font-size="14.00">[0...1]</text>
+</g>
+<!-- INT_CON0&#45;&gt;INT_BOOL -->
+<g id="edge10" class="edge">
+<title>INT_CON0&#45;&gt;INT_BOOL</title>
+<path fill="none" stroke="black" d="M56.45,-266.6C63.5,-254.72 73.51,-237.87 81.88,-223.77"/>
+<polygon fill="black" stroke="black" points="84.75,-225.79 86.85,-215.41 78.73,-222.22 84.75,-225.79"/>
+</g>
+<!-- INT_CON1&#45;&gt;INT_CON -->
+<!-- INT_CON1&#45;&gt;INT_BOOL -->
+<g id="edge11" class="edge">
+<title>INT_CON1&#45;&gt;INT_BOOL</title>
+<path fill="none" stroke="black" d="M97.51,-264.21C97.51,-253.25 97.51,-238.87 97.51,-226.25"/>
+<polygon fill="black" stroke="black" points="101.01,-226.39 97.51,-216.39 94.01,-226.39 101.01,-226.39"/>
+</g>
+<!-- INT_BOOL&#45;&gt;INT_LO -->
+<g id="edge12" class="edge">
+<title>INT_BOOL&#45;&gt;INT_LO</title>
+<path fill="none" stroke="black" d="M117.34,-181.75C124.98,-176.47 133.91,-170.3 142.42,-164.42"/>
+<polygon fill="black" stroke="black" points="144.27,-167.4 150.5,-158.83 140.29,-161.64 144.27,-167.4"/>
+</g>
+<!-- INT_LO&#45;&gt;INT_BOT -->
+<g id="edge14" class="edge">
+<title>INT_LO&#45;&gt;INT_BOT</title>
+<path fill="none" stroke="black" d="M210.07,-127.29C234.52,-118.37 266.93,-106.56 291.82,-97.49"/>
+<polygon fill="black" stroke="black" points="292.71,-100.89 300.91,-94.17 290.31,-94.31 292.71,-100.89"/>
+</g>
+<!-- FLT_CON64 -->
+<g id="node16" class="node">
+<title>FLT_CON64</title>
+<ellipse fill="aqua" stroke="black" cx="564.51" cy="-282.65" rx="55.33" ry="40.66"/>
+<text text-anchor="middle" x="564.51" y="-294.1" font-family="Times New Roman,serif" font-size="14.00">64b consts</text>
+<text text-anchor="middle" x="564.51" y="-277.6" font-family="Times New Roman,serif" font-size="14.00">[excludes</text>
+<text text-anchor="middle" x="564.51" y="-261.1" font-family="Times New Roman,serif" font-size="14.00">32b consts]</text>
+</g>
+<!-- FLT_TOP&#45;&gt;FLT_CON64 -->
+<g id="edge19" class="edge">
+<title>FLT_TOP&#45;&gt;FLT_CON64</title>
+<path fill="none" stroke="black" d="M500.48,-474.88C510.8,-443.88 532.35,-379.2 547.59,-333.44"/>
+<polygon fill="black" stroke="black" points="550.9,-334.57 550.74,-323.98 544.26,-332.36 550.9,-334.57"/>
+</g>
+<!-- FLT_CONPI -->
+<g id="node18" class="node">
+<title>FLT_CONPI</title>
+<ellipse fill="aqua" stroke="black" cx="446.51" cy="-282.65" rx="48.79" ry="18"/>
+<text text-anchor="middle" x="446.51" y="-277.6" font-family="Times New Roman,serif" font-size="14.00">3.14159...</text>
+</g>
+<!-- FLT_TOP&#45;&gt;FLT_CONPI -->
+<g id="edge20" class="edge">
+<title>FLT_TOP&#45;&gt;FLT_CONPI</title>
+<path fill="none" stroke="black" d="M490.33,-474.51C482.02,-438.11 463.32,-356.27 453.25,-312.16"/>
+<polygon fill="black" stroke="black" points="456.69,-311.49 451.05,-302.52 449.86,-313.04 456.69,-311.49"/>
+</g>
+<!-- FLT_X32 -->
+<g id="node19" class="node">
+<title>FLT_X32</title>
+<ellipse fill="aqua" stroke="black" cx="333.51" cy="-373.97" rx="28.32" ry="28.32"/>
+<text text-anchor="middle" x="333.51" y="-368.92" font-family="Times New Roman,serif" font-size="14.00">~f32</text>
+</g>
+<!-- FLT_TOP&#45;&gt;FLT_X32 -->
+<g id="edge23" class="edge">
+<title>FLT_TOP&#45;&gt;FLT_X32</title>
+<path fill="none" stroke="black" d="M474.77,-478.22C447.56,-458.14 398.22,-421.73 365.65,-397.69"/>
+<polygon fill="black" stroke="black" points="367.88,-394.99 357.75,-391.86 363.72,-400.62 367.88,-394.99"/>
+</g>
+<!-- FLT_BOT -->
+<g id="node15" class="node">
+<title>FLT_BOT</title>
+<ellipse fill="aqua" stroke="black" cx="507.51" cy="-82.75" rx="32.41" ry="18"/>
+<text text-anchor="start" x="491.76" y="-78.7" font-family="Times New Roman,serif" font-weight="bold" font-size="14.00">⊥</text>
+<text text-anchor="start" x="507.51" y="-78.7" font-family="Times New Roman,serif" font-size="14.00">:flt</text>
+</g>
+<!-- FLT_BOT&#45;&gt;BOT -->
+<g id="edge29" class="edge">
+<title>FLT_BOT&#45;&gt;BOT</title>
+<path fill="none" stroke="black" d="M532.4,-70.67C554.98,-60.63 588.29,-45.81 612.16,-35.19"/>
+<polygon fill="black" stroke="black" points="613.29,-38.51 621,-31.25 610.44,-32.12 613.29,-38.51"/>
+</g>
+<!-- FLT_CON64&#45;&gt;FLT_BOT -->
+<g id="edge21" class="edge">
+<title>FLT_CON64&#45;&gt;FLT_BOT</title>
+<path fill="none" stroke="black" d="M553.11,-242.68C542.05,-203.88 525.53,-145.95 515.71,-111.5"/>
+<polygon fill="black" stroke="black" points="519.17,-110.89 513.06,-102.23 512.44,-112.81 519.17,-110.89"/>
+</g>
+<!-- FLT_CON32 -->
+<g id="node17" class="node">
+<title>FLT_CON32</title>
+<ellipse fill="aqua" stroke="black" cx="333.51" cy="-282.65" rx="50.33" ry="18"/>
+<text text-anchor="middle" x="333.51" y="-277.6" font-family="Times New Roman,serif" font-size="14.00">32b consts</text>
+</g>
+<!-- FLT_CON32&#45;&gt;FLT_CONPI -->
+<!-- FLT_32 -->
+<g id="node21" class="node">
+<title>FLT_32</title>
+<ellipse fill="aqua" stroke="black" cx="333.51" cy="-196.45" rx="23.2" ry="23.2"/>
+<text text-anchor="middle" x="333.51" y="-191.4" font-family="Times New Roman,serif" font-size="14.00">f32</text>
+</g>
+<!-- FLT_CON32&#45;&gt;FLT_32 -->
+<g id="edge27" class="edge">
+<title>FLT_CON32&#45;&gt;FLT_32</title>
+<path fill="none" stroke="black" d="M333.51,-264.21C333.51,-254.7 333.51,-242.59 333.51,-231.31"/>
+<polygon fill="black" stroke="black" points="337.01,-231.51 333.51,-221.51 330.01,-231.51 337.01,-231.51"/>
+</g>
+<!-- FLT_CONPI&#45;&gt;FLT_BOT -->
+<g id="edge22" class="edge">
+<title>FLT_CONPI&#45;&gt;FLT_BOT</title>
+<path fill="none" stroke="black" d="M452.03,-264.56C462.62,-229.86 485.86,-153.69 498.65,-111.79"/>
+<polygon fill="black" stroke="black" points="501.94,-112.98 501.51,-102.39 495.25,-110.93 501.94,-112.98"/>
+</g>
+<!-- FLT_CONPI&#45;&gt;FLT_CON64 -->
+<!-- FLT_X32&#45;&gt;FLT_CON32 -->
+<g id="edge25" class="edge">
+<title>FLT_X32&#45;&gt;FLT_CON32</title>
+<path fill="none" stroke="black" d="M333.51,-345.2C333.51,-334.71 333.51,-322.76 333.51,-312.19"/>
+<polygon fill="black" stroke="black" points="337.01,-312.52 333.51,-302.52 330.01,-312.53 337.01,-312.52"/>
+</g>
+<!-- FLT_CON0 -->
+<g id="node20" class="node">
+<title>FLT_CON0</title>
+<ellipse fill="aqua" stroke="black" cx="251.51" cy="-282.65" rx="18.08" ry="18.08"/>
+<text text-anchor="middle" x="251.51" y="-277.6" font-family="Times New Roman,serif" font-size="14.00">0.</text>
+</g>
+<!-- FLT_X32&#45;&gt;FLT_CON0 -->
+<g id="edge24" class="edge">
+<title>FLT_X32&#45;&gt;FLT_CON0</title>
+<path fill="none" stroke="black" d="M314.9,-352.7C301.83,-338.47 284.38,-319.46 271.1,-304.99"/>
+<polygon fill="black" stroke="black" points="273.81,-302.77 264.47,-297.77 268.65,-307.5 273.81,-302.77"/>
+</g>
+<!-- FLT_CON0&#45;&gt;FLT_CON32 -->
+<!-- FLT_CON0&#45;&gt;FLT_32 -->
+<g id="edge26" class="edge">
+<title>FLT_CON0&#45;&gt;FLT_32</title>
+<path fill="none" stroke="black" d="M263.96,-268.86C276.06,-256.44 294.74,-237.26 309.69,-221.91"/>
+<polygon fill="black" stroke="black" points="312.03,-224.53 316.5,-214.92 307.01,-219.64 312.03,-224.53"/>
+</g>
+<!-- FLT_32&#45;&gt;FLT_BOT -->
+<g id="edge28" class="edge">
+<title>FLT_32&#45;&gt;FLT_BOT</title>
+<path fill="none" stroke="black" d="M353.13,-183.63C383.19,-163.99 440.9,-126.28 476.61,-102.94"/>
+<polygon fill="black" stroke="black" points="478.34,-105.99 484.79,-97.59 474.51,-100.13 478.34,-105.99"/>
+</g>
+<!-- PTR0 -->
+<g id="node23" class="node">
+<title>PTR0</title>
+<polygon fill="none" stroke="none" points="668.51,-109.25 614.51,-109.25 614.51,-56.25 668.51,-56.25 668.51,-109.25"/>
+<polygon fill="green" stroke="none" points="623.51,-82.75 623.51,-105.25 659.51,-105.25 659.51,-82.75 623.51,-82.75"/>
+<polygon fill="none" stroke="black" points="623.51,-82.75 623.51,-105.25 659.51,-105.25 659.51,-82.75 623.51,-82.75"/>
+<text text-anchor="start" x="627.63" y="-88.95" font-family="Times New Roman,serif" font-size="14.00" fill="white">*ptr?</text>
+<polygon fill="lightgreen" stroke="none" points="623.51,-60.25 623.51,-82.75 659.51,-82.75 659.51,-60.25 623.51,-60.25"/>
+<polygon fill="none" stroke="black" points="623.51,-60.25 623.51,-82.75 659.51,-82.75 659.51,-60.25 623.51,-60.25"/>
+<text text-anchor="start" x="626.51" y="-66.45" font-family="Times New Roman,serif" font-size="14.00">struct</text>
+</g>
+<!-- PTR&#45;&gt;PTR0 -->
+<g id="edge34" class="edge">
+<title>PTR&#45;&gt;PTR0</title>
+<path fill="none" stroke="black" d="M641.51,-466.52C641.51,-396.01 641.51,-202.44 641.51,-120.34"/>
+<polygon fill="black" stroke="black" points="645.01,-120.58 641.51,-110.58 638.01,-120.58 645.01,-120.58"/>
+</g>
+<!-- PTR0&#45;&gt;BOT -->
+<g id="edge35" class="edge">
+<title>PTR0&#45;&gt;BOT</title>
+<path fill="none" stroke="black" d="M641.51,-56.46C641.51,-55.58 641.51,-54.7 641.51,-53.81"/>
+<polygon fill="black" stroke="black" points="645.01,-53.9 641.51,-43.9 638.01,-53.9 645.01,-53.9"/>
+</g>
+<!-- STRUCT_S1 -->
+<g id="node26" class="node">
+<title>STRUCT_S1</title>
+<polygon fill="none" stroke="none" points="741.38,-300.65 663.63,-300.65 663.63,-264.65 741.38,-264.65 741.38,-300.65"/>
+<polygon fill="lightgreen" stroke="none" points="671.63,-273.4 671.63,-291.9 698.38,-291.9 698.38,-273.4 671.63,-273.4"/>
+<polygon fill="none" stroke="black" points="671.63,-273.4 671.63,-291.9 698.38,-291.9 698.38,-273.4 671.63,-273.4"/>
+<text text-anchor="start" x="672.63" y="-277.6" font-family="Times New Roman,serif" font-size="14.00">field</text>
+<defs>
+<linearGradient id="l_0" gradientUnits="userSpaceOnUse" x1="703.51" y1="-289.19" x2="728.26" y2="-276.11" >
+<stop offset="0" style="stop-color:lightblue;stop-opacity:1.;"/>
+<stop offset="1" style="stop-color:green;stop-opacity:1.;"/>
+</linearGradient>
+</defs>
+<polygon fill="url(#l_0)" stroke="none" points="698.38,-273.4 698.38,-291.9 733.38,-291.9 733.38,-273.4 698.38,-273.4"/>
+<polygon fill="none" stroke="black" points="698.38,-273.4 698.38,-291.9 733.38,-291.9 733.38,-273.4 698.38,-273.4"/>
+<text text-anchor="start" x="699.38" y="-277.6" font-family="Times New Roman,serif" font-size="14.00">int/ptr</text>
+</g>
+<!-- STRUCT_TOP&#45;&gt;STRUCT_S1 -->
+<g id="edge37" class="edge">
+<title>STRUCT_TOP&#45;&gt;STRUCT_S1</title>
+<path fill="none" stroke="black" d="M742.68,-474.51C735.02,-437.92 717.75,-355.45 708.55,-311.5"/>
+<polygon fill="black" stroke="black" points="712.02,-311 706.54,-301.93 705.17,-312.43 712.02,-311"/>
+</g>
+<!-- STRUCT_S2 -->
+<g id="node27" class="node">
+<title>STRUCT_S2</title>
+<polygon fill="none" stroke="none" points="841.38,-309.15 755.63,-309.15 755.63,-256.15 841.38,-256.15 841.38,-309.15"/>
+<polygon fill="lightgreen" stroke="none" points="763.63,-282.65 763.63,-305.15 794.38,-305.15 794.38,-282.65 763.63,-282.65"/>
+<polygon fill="none" stroke="black" points="763.63,-282.65 763.63,-305.15 794.38,-305.15 794.38,-282.65 763.63,-282.65"/>
+<text text-anchor="start" x="766.63" y="-288.85" font-family="Times New Roman,serif" font-size="14.00">field</text>
+<defs>
+<linearGradient id="l_1" gradientUnits="userSpaceOnUse" x1="800.1" y1="-301.86" x2="827.67" y2="-285.95" >
+<stop offset="0" style="stop-color:lightblue;stop-opacity:1.;"/>
+<stop offset="1" style="stop-color:green;stop-opacity:1.;"/>
+</linearGradient>
+</defs>
+<polygon fill="url(#l_1)" stroke="none" points="794.38,-282.65 794.38,-305.15 833.38,-305.15 833.38,-282.65 794.38,-282.65"/>
+<polygon fill="none" stroke="black" points="794.38,-282.65 794.38,-305.15 833.38,-305.15 833.38,-282.65 794.38,-282.65"/>
+<text text-anchor="start" x="797.38" y="-288.85" font-family="Times New Roman,serif" font-size="14.00">int/ptr</text>
+<polygon fill="lightgreen" stroke="none" points="763.63,-260.15 763.63,-282.65 794.38,-282.65 794.38,-260.15 763.63,-260.15"/>
+<polygon fill="none" stroke="black" points="763.63,-260.15 763.63,-282.65 794.38,-282.65 794.38,-260.15 763.63,-260.15"/>
+<text text-anchor="start" x="766.63" y="-266.35" font-family="Times New Roman,serif" font-size="14.00">field</text>
+<defs>
+<linearGradient id="l_2" gradientUnits="userSpaceOnUse" x1="800.1" y1="-279.36" x2="827.67" y2="-263.45" >
+<stop offset="0" style="stop-color:lightblue;stop-opacity:1.;"/>
+<stop offset="1" style="stop-color:green;stop-opacity:1.;"/>
+</linearGradient>
+</defs>
+<polygon fill="url(#l_2)" stroke="none" points="794.38,-260.15 794.38,-282.65 833.38,-282.65 833.38,-260.15 794.38,-260.15"/>
+<polygon fill="none" stroke="black" points="794.38,-260.15 794.38,-282.65 833.38,-282.65 833.38,-260.15 794.38,-260.15"/>
+<text text-anchor="start" x="797.38" y="-266.35" font-family="Times New Roman,serif" font-size="14.00">int/ptr</text>
+</g>
+<!-- STRUCT_TOP&#45;&gt;STRUCT_S2 -->
+<g id="edge38" class="edge">
+<title>STRUCT_TOP&#45;&gt;STRUCT_S2</title>
+<path fill="none" stroke="black" d="M751.03,-474.51C759.53,-440.16 778.05,-365.34 789.28,-319.95"/>
+<polygon fill="black" stroke="black" points="792.67,-320.83 791.67,-310.28 785.87,-319.15 792.67,-320.83"/>
+</g>
+<!-- STRUCT_SN -->
+<g id="node28" class="node">
+<title>STRUCT_SN</title>
+<polygon fill="none" stroke="none" points="941.38,-320.4 855.63,-320.4 855.63,-244.9 941.38,-244.9 941.38,-320.4"/>
+<polygon fill="lightgreen" stroke="none" points="863.63,-293.9 863.63,-316.4 894.38,-316.4 894.38,-293.9 863.63,-293.9"/>
+<polygon fill="none" stroke="black" points="863.63,-293.9 863.63,-316.4 894.38,-316.4 894.38,-293.9 863.63,-293.9"/>
+<text text-anchor="start" x="866.63" y="-300.1" font-family="Times New Roman,serif" font-size="14.00">field</text>
+<defs>
+<linearGradient id="l_3" gradientUnits="userSpaceOnUse" x1="900.1" y1="-313.11" x2="927.67" y2="-297.2" >
+<stop offset="0" style="stop-color:lightblue;stop-opacity:1.;"/>
+<stop offset="1" style="stop-color:green;stop-opacity:1.;"/>
+</linearGradient>
+</defs>
+<polygon fill="url(#l_3)" stroke="none" points="894.38,-293.9 894.38,-316.4 933.38,-316.4 933.38,-293.9 894.38,-293.9"/>
+<polygon fill="none" stroke="black" points="894.38,-293.9 894.38,-316.4 933.38,-316.4 933.38,-293.9 894.38,-293.9"/>
+<text text-anchor="start" x="897.38" y="-300.1" font-family="Times New Roman,serif" font-size="14.00">int/ptr</text>
+<polygon fill="lightgreen" stroke="none" points="863.63,-271.4 863.63,-293.9 894.38,-293.9 894.38,-271.4 863.63,-271.4"/>
+<polygon fill="none" stroke="black" points="863.63,-271.4 863.63,-293.9 894.38,-293.9 894.38,-271.4 863.63,-271.4"/>
+<text text-anchor="start" x="869.63" y="-277.6" font-family="Times New Roman,serif" font-size="14.00">... &#160;</text>
+<defs>
+<linearGradient id="l_4" gradientUnits="userSpaceOnUse" x1="900.1" y1="-290.61" x2="927.67" y2="-274.7" >
+<stop offset="0" style="stop-color:lightblue;stop-opacity:1.;"/>
+<stop offset="1" style="stop-color:green;stop-opacity:1.;"/>
+</linearGradient>
+</defs>
+<polygon fill="url(#l_4)" stroke="none" points="894.38,-271.4 894.38,-293.9 933.38,-293.9 933.38,-271.4 894.38,-271.4"/>
+<polygon fill="none" stroke="black" points="894.38,-271.4 894.38,-293.9 933.38,-293.9 933.38,-271.4 894.38,-271.4"/>
+<text text-anchor="start" x="897.38" y="-277.6" font-family="Times New Roman,serif" font-size="14.00">int/ptr</text>
+<polygon fill="lightgreen" stroke="none" points="863.63,-248.9 863.63,-271.4 894.38,-271.4 894.38,-248.9 863.63,-248.9"/>
+<polygon fill="none" stroke="black" points="863.63,-248.9 863.63,-271.4 894.38,-271.4 894.38,-248.9 863.63,-248.9"/>
+<text text-anchor="start" x="866.63" y="-255.1" font-family="Times New Roman,serif" font-size="14.00">field</text>
+<defs>
+<linearGradient id="l_5" gradientUnits="userSpaceOnUse" x1="900.1" y1="-268.11" x2="927.67" y2="-252.2" >
+<stop offset="0" style="stop-color:lightblue;stop-opacity:1.;"/>
+<stop offset="1" style="stop-color:green;stop-opacity:1.;"/>
+</linearGradient>
+</defs>
+<polygon fill="url(#l_5)" stroke="none" points="894.38,-248.9 894.38,-271.4 933.38,-271.4 933.38,-248.9 894.38,-248.9"/>
+<polygon fill="none" stroke="black" points="894.38,-248.9 894.38,-271.4 933.38,-271.4 933.38,-248.9 894.38,-248.9"/>
+<text text-anchor="start" x="897.38" y="-255.1" font-family="Times New Roman,serif" font-size="14.00">int/ptr</text>
+</g>
+<!-- STRUCT_TOP&#45;&gt;STRUCT_SN -->
+<g id="edge39" class="edge">
+<title>STRUCT_TOP&#45;&gt;STRUCT_SN</title>
+<path fill="none" stroke="black" d="M759.21,-475.24C782.17,-443.49 831.3,-375.56 864.63,-329.48"/>
+<polygon fill="black" stroke="black" points="867.37,-331.67 870.4,-321.52 861.7,-327.57 867.37,-331.67"/>
+</g>
+<!-- STRUCT_BOT -->
+<g id="node25" class="node">
+<title>STRUCT_BOT</title>
+<ellipse fill="lightgreen" stroke="black" cx="751.51" cy="-82.75" rx="44.7" ry="18"/>
+<text text-anchor="start" x="726.76" y="-78.7" font-family="Times New Roman,serif" font-weight="bold" font-size="14.00">⊥</text>
+<text text-anchor="start" x="742.51" y="-78.7" font-family="Times New Roman,serif" font-size="14.00">:struct</text>
+</g>
+<!-- STRUCT_BOT&#45;&gt;BOT -->
+<g id="edge43" class="edge">
+<title>STRUCT_BOT&#45;&gt;BOT</title>
+<path fill="none" stroke="black" d="M725.72,-67.77C709,-58.7 687.2,-46.89 669.97,-37.55"/>
+<polygon fill="black" stroke="black" points="671.71,-34.51 661.25,-32.82 668.37,-40.67 671.71,-34.51"/>
+</g>
+<!-- STRUCT_S1&#45;&gt;STRUCT_BOT -->
+<g id="edge40" class="edge">
+<title>STRUCT_S1&#45;&gt;STRUCT_BOT</title>
+<path fill="none" stroke="black" d="M706.86,-264.91C715.3,-230.49 733.97,-154.31 744.29,-112.19"/>
+<polygon fill="black" stroke="black" points="747.69,-113.04 746.67,-102.5 740.89,-111.37 747.69,-113.04"/>
+</g>
+<!-- STRUCT_S1&#45;&gt;STRUCT_S2 -->
+<!-- STRUCT_S2&#45;&gt;STRUCT_BOT -->
+<g id="edge41" class="edge">
+<title>STRUCT_S2&#45;&gt;STRUCT_BOT</title>
+<path fill="none" stroke="black" d="M792.36,-256.52C783.62,-219.3 767.61,-151.25 758.45,-112.26"/>
+<polygon fill="black" stroke="black" points="761.88,-111.57 756.18,-102.64 755.07,-113.17 761.88,-111.57"/>
+</g>
+<!-- STRUCT_S2&#45;&gt;STRUCT_SN -->
+<!-- STRUCT_SN&#45;&gt;STRUCT_BOT -->
+<g id="edge42" class="edge">
+<title>STRUCT_SN&#45;&gt;STRUCT_BOT</title>
+<path fill="none" stroke="black" d="M870.86,-245.05C841.74,-205.46 796.64,-144.13 771.03,-109.3"/>
+<polygon fill="black" stroke="black" points="773.99,-107.42 765.25,-101.44 768.36,-111.57 773.99,-107.42"/>
+</g>
+<!-- MEM_ALIAS1 -->
+<g id="node31" class="node">
+<title>MEM_ALIAS1</title>
+<ellipse fill="blue" stroke="black" cx="975.51" cy="-282.65" rx="20.13" ry="20.13"/>
+<text text-anchor="middle" x="975.51" y="-277.6" font-family="Times New Roman,serif" font-size="14.00" fill="white">#1</text>
+</g>
+<!-- MEM_TOP&#45;&gt;MEM_ALIAS1 -->
+<g id="edge47" class="edge">
+<title>MEM_TOP&#45;&gt;MEM_ALIAS1</title>
+<path fill="none" stroke="black" d="M996.51,-474.51C992.58,-438.66 983.84,-358.73 978.96,-314.18"/>
+<polygon fill="black" stroke="black" points="982.45,-313.94 977.89,-304.38 975.5,-314.7 982.45,-313.94"/>
+</g>
+<!-- MEM_ALIASDOTS -->
+<g id="node32" class="node">
+<title>MEM_ALIASDOTS</title>
+<ellipse fill="blue" stroke="black" cx="1036.51" cy="-282.65" rx="27" ry="18"/>
+<text text-anchor="middle" x="1036.51" y="-277.6" font-family="Times New Roman,serif" font-size="14.00" fill="white">...</text>
+</g>
+<!-- MEM_TOP&#45;&gt;MEM_ALIASDOTS -->
+<g id="edge48" class="edge">
+<title>MEM_TOP&#45;&gt;MEM_ALIASDOTS</title>
+<path fill="none" stroke="black" d="M1001.81,-474.51C1008.41,-438.02 1023.27,-355.86 1031.23,-311.83"/>
+<polygon fill="black" stroke="black" points="1034.63,-312.69 1032.97,-302.23 1027.74,-311.45 1034.63,-312.69"/>
+</g>
+<!-- MEM_ALIASN -->
+<g id="node33" class="node">
+<title>MEM_ALIASN</title>
+<ellipse fill="blue" stroke="black" cx="1097.51" cy="-282.65" rx="20.13" ry="20.13"/>
+<text text-anchor="middle" x="1097.51" y="-277.6" font-family="Times New Roman,serif" font-size="14.00" fill="white">#n</text>
+</g>
+<!-- MEM_TOP&#45;&gt;MEM_ALIASN -->
+<g id="edge49" class="edge">
+<title>MEM_TOP&#45;&gt;MEM_ALIASN</title>
+<path fill="none" stroke="black" d="M1006.95,-474.88C1024.11,-438.46 1063.18,-355.52 1083.95,-311.44"/>
+<polygon fill="black" stroke="black" points="1087.07,-313.03 1088.16,-302.49 1080.73,-310.05 1087.07,-313.03"/>
+</g>
+<!-- MEM_BOT -->
+<g id="node30" class="node">
+<title>MEM_BOT</title>
+<ellipse fill="blue" stroke="black" cx="998.51" cy="-82.75" rx="43.67" ry="18"/>
+<text text-anchor="start" x="974.51" y="-78.7" font-family="Times New Roman,serif" font-weight="bold" font-size="14.00" fill="white">⊥</text>
+<text text-anchor="start" x="990.26" y="-78.7" font-family="Times New Roman,serif" font-size="14.00" fill="white">:mem</text>
+</g>
+<!-- MEM_BOT&#45;&gt;BOT -->
+<g id="edge53" class="edge">
+<title>MEM_BOT&#45;&gt;BOT</title>
+<path fill="none" stroke="black" d="M958.6,-75.08C887.17,-63.15 738.86,-38.38 673.56,-27.48"/>
+<polygon fill="black" stroke="black" points="674.3,-24.05 663.86,-25.86 673.15,-30.96 674.3,-24.05"/>
+</g>
+<!-- MEM_ALIAS1&#45;&gt;MEM_BOT -->
+<g id="edge50" class="edge">
+<title>MEM_ALIAS1&#45;&gt;MEM_BOT</title>
+<path fill="none" stroke="black" d="M977.84,-262.38C981.92,-226.96 990.35,-153.62 995.09,-112.45"/>
+<polygon fill="black" stroke="black" points="998.57,-112.87 996.23,-102.53 991.61,-112.07 998.57,-112.87"/>
+</g>
+<!-- MEM_ALIAS1&#45;&gt;MEM_ALIASDOTS -->
+<!-- MEM_ALIASDOTS&#45;&gt;MEM_BOT -->
+<g id="edge51" class="edge">
+<title>MEM_ALIASDOTS&#45;&gt;MEM_BOT</title>
+<path fill="none" stroke="black" d="M1033.07,-264.56C1026.49,-229.93 1012.06,-154.01 1004.08,-112.06"/>
+<polygon fill="black" stroke="black" points="1007.56,-111.6 1002.25,-102.43 1000.68,-112.91 1007.56,-111.6"/>
+</g>
+<!-- MEM_ALIASDOTS&#45;&gt;MEM_ALIASN -->
+<!-- MEM_ALIASN&#45;&gt;MEM_BOT -->
+<g id="edge52" class="edge">
+<title>MEM_ALIASN&#45;&gt;MEM_BOT</title>
+<path fill="none" stroke="black" d="M1088.37,-264.2C1070.97,-229.07 1032.98,-152.35 1012.41,-110.82"/>
+<polygon fill="black" stroke="black" points="1015.58,-109.34 1008.01,-101.93 1009.31,-112.45 1015.58,-109.34"/>
+</g>
+<!-- TUPLE1 -->
+<g id="node36" class="node">
+<title>TUPLE1</title>
+<polygon fill="none" stroke="none" points="1185.51,-309.15 1131.51,-309.15 1131.51,-256.15 1185.51,-256.15 1185.51,-309.15"/>
+<polygon fill="pink" stroke="none" points="1139.76,-282.65 1139.76,-305.15 1177.26,-305.15 1177.26,-282.65 1139.76,-282.65"/>
+<polygon fill="none" stroke="black" points="1139.76,-282.65 1139.76,-305.15 1177.26,-305.15 1177.26,-282.65 1139.76,-282.65"/>
+<text text-anchor="start" x="1142.76" y="-288.85" font-family="Times New Roman,serif" font-size="14.00">Tuple</text>
+<polygon fill="black" stroke="none" points="1139.76,-260.15 1139.76,-282.65 1177.26,-282.65 1177.26,-260.15 1139.76,-260.15"/>
+<polygon fill="none" stroke="black" points="1139.76,-260.15 1139.76,-282.65 1177.26,-282.65 1177.26,-260.15 1139.76,-260.15"/>
+<text text-anchor="start" x="1146.88" y="-266.35" font-family="Times New Roman,serif" font-size="14.00" fill="white">type</text>
+</g>
+<!-- TUPLE_TOP&#45;&gt;TUPLE1 -->
+<g id="edge57" class="edge">
+<title>TUPLE_TOP&#45;&gt;TUPLE1</title>
+<path fill="none" stroke="black" d="M1180.42,-474.51C1176.51,-440.23 1167.99,-365.66 1162.8,-320.24"/>
+<polygon fill="black" stroke="black" points="1166.28,-319.86 1161.67,-310.32 1159.33,-320.65 1166.28,-319.86"/>
+</g>
+<!-- TUPLE2 -->
+<g id="node37" class="node">
+<title>TUPLE2</title>
+<polygon fill="none" stroke="none" points="1253.51,-320.4 1199.51,-320.4 1199.51,-244.9 1253.51,-244.9 1253.51,-320.4"/>
+<polygon fill="pink" stroke="none" points="1207.76,-293.9 1207.76,-316.4 1245.26,-316.4 1245.26,-293.9 1207.76,-293.9"/>
+<polygon fill="none" stroke="black" points="1207.76,-293.9 1207.76,-316.4 1245.26,-316.4 1245.26,-293.9 1207.76,-293.9"/>
+<text text-anchor="start" x="1210.76" y="-300.1" font-family="Times New Roman,serif" font-size="14.00">Tuple</text>
+<polygon fill="black" stroke="none" points="1207.76,-271.4 1207.76,-293.9 1245.26,-293.9 1245.26,-271.4 1207.76,-271.4"/>
+<polygon fill="none" stroke="black" points="1207.76,-271.4 1207.76,-293.9 1245.26,-293.9 1245.26,-271.4 1207.76,-271.4"/>
+<text text-anchor="start" x="1214.88" y="-277.6" font-family="Times New Roman,serif" font-size="14.00" fill="white">type</text>
+<polygon fill="black" stroke="none" points="1207.76,-248.9 1207.76,-271.4 1245.26,-271.4 1245.26,-248.9 1207.76,-248.9"/>
+<polygon fill="none" stroke="black" points="1207.76,-248.9 1207.76,-271.4 1245.26,-271.4 1245.26,-248.9 1207.76,-248.9"/>
+<text text-anchor="start" x="1214.88" y="-255.1" font-family="Times New Roman,serif" font-size="14.00" fill="white">type</text>
+</g>
+<!-- TUPLE_TOP&#45;&gt;TUPLE2 -->
+<g id="edge58" class="edge">
+<title>TUPLE_TOP&#45;&gt;TUPLE2</title>
+<path fill="none" stroke="black" d="M1186.34,-474.51C1192.95,-442.94 1206.72,-377.19 1216.28,-331.51"/>
+<polygon fill="black" stroke="black" points="1219.68,-332.35 1218.3,-321.85 1212.83,-330.92 1219.68,-332.35"/>
+</g>
+<!-- TUPLEN -->
+<g id="node38" class="node">
+<title>TUPLEN</title>
+<polygon fill="none" stroke="none" points="1321.51,-331.65 1267.51,-331.65 1267.51,-233.65 1321.51,-233.65 1321.51,-331.65"/>
+<polygon fill="pink" stroke="none" points="1275.76,-305.15 1275.76,-327.65 1313.26,-327.65 1313.26,-305.15 1275.76,-305.15"/>
+<polygon fill="none" stroke="black" points="1275.76,-305.15 1275.76,-327.65 1313.26,-327.65 1313.26,-305.15 1275.76,-305.15"/>
+<text text-anchor="start" x="1278.76" y="-311.35" font-family="Times New Roman,serif" font-size="14.00">Tuple</text>
+<polygon fill="black" stroke="none" points="1275.76,-282.65 1275.76,-305.15 1313.26,-305.15 1313.26,-282.65 1275.76,-282.65"/>
+<polygon fill="none" stroke="black" points="1275.76,-282.65 1275.76,-305.15 1313.26,-305.15 1313.26,-282.65 1275.76,-282.65"/>
+<text text-anchor="start" x="1282.88" y="-288.85" font-family="Times New Roman,serif" font-size="14.00" fill="white">type</text>
+<polygon fill="black" stroke="none" points="1275.76,-260.15 1275.76,-282.65 1313.26,-282.65 1313.26,-260.15 1275.76,-260.15"/>
+<polygon fill="none" stroke="black" points="1275.76,-260.15 1275.76,-282.65 1313.26,-282.65 1313.26,-260.15 1275.76,-260.15"/>
+<text text-anchor="start" x="1288.88" y="-266.35" font-family="Times New Roman,serif" font-size="14.00" fill="white">...</text>
+<polygon fill="black" stroke="none" points="1275.76,-237.65 1275.76,-260.15 1313.26,-260.15 1313.26,-237.65 1275.76,-237.65"/>
+<polygon fill="none" stroke="black" points="1275.76,-237.65 1275.76,-260.15 1313.26,-260.15 1313.26,-237.65 1275.76,-237.65"/>
+<text text-anchor="start" x="1282.88" y="-243.85" font-family="Times New Roman,serif" font-size="14.00" fill="white">type</text>
+</g>
+<!-- TUPLE_TOP&#45;&gt;TUPLEN -->
+<g id="edge59" class="edge">
+<title>TUPLE_TOP&#45;&gt;TUPLEN</title>
+<path fill="none" stroke="black" d="M1192.06,-474.88C1207.65,-445.62 1239.24,-386.36 1263.24,-341.32"/>
+<polygon fill="black" stroke="black" points="1266.22,-343.18 1267.83,-332.71 1260.04,-339.88 1266.22,-343.18"/>
+</g>
+<!-- TUPLE_BOT -->
+<g id="node35" class="node">
+<title>TUPLE_BOT</title>
+<ellipse fill="pink" stroke="black" cx="1192.51" cy="-82.75" rx="42.65" ry="18"/>
+<text text-anchor="start" x="1169.26" y="-78.7" font-family="Times New Roman,serif" font-weight="bold" font-size="14.00">⊥</text>
+<text text-anchor="start" x="1185.01" y="-78.7" font-family="Times New Roman,serif" font-size="14.00">:tuple</text>
+</g>
+<!-- TUPLE_BOT&#45;&gt;BOT -->
+<g id="edge63" class="edge">
+<title>TUPLE_BOT&#45;&gt;BOT</title>
+<path fill="none" stroke="black" d="M1151.54,-77.32C1046.53,-65.95 769.32,-35.95 674.14,-25.65"/>
+<polygon fill="black" stroke="black" points="674.52,-22.18 664.2,-24.58 673.77,-29.14 674.52,-22.18"/>
+</g>
+<!-- TUPLE1&#45;&gt;TUPLE_BOT -->
+<g id="edge60" class="edge">
+<title>TUPLE1&#45;&gt;TUPLE_BOT</title>
+<path fill="none" stroke="black" d="M1162.95,-256.52C1169.28,-219.3 1180.86,-151.25 1187.49,-112.26"/>
+<polygon fill="black" stroke="black" points="1190.9,-113.1 1189.12,-102.66 1184,-111.93 1190.9,-113.1"/>
+</g>
+<!-- TUPLE2&#45;&gt;TUPLE_BOT -->
+<g id="edge61" class="edge">
+<title>TUPLE2&#45;&gt;TUPLE_BOT</title>
+<path fill="none" stroke="black" d="M1220.11,-245.05C1213.55,-206.44 1203.46,-147.16 1197.47,-111.94"/>
+<polygon fill="black" stroke="black" points="1200.99,-111.74 1195.86,-102.47 1194.09,-112.92 1200.99,-111.74"/>
+</g>
+<!-- TUPLEN&#45;&gt;TUPLE_BOT -->
+<g id="edge62" class="edge">
+<title>TUPLEN&#45;&gt;TUPLE_BOT</title>
+<path fill="none" stroke="black" d="M1269.56,-233.75C1249.85,-195.12 1223.04,-142.58 1206.8,-110.75"/>
+<polygon fill="black" stroke="black" points="1210.07,-109.46 1202.41,-102.15 1203.83,-112.65 1210.07,-109.46"/>
+</g>
+</g>
+</svg>
diff --git a/docs/chapter19/lattice2.gv b/docs/chapter19/lattice2.gv
new file mode 100644
index 00000000..6aa37fa0
--- /dev/null
+++ b/docs/chapter19/lattice2.gv
@@ -0,0 +1,85 @@
+digraph Lattice {
+ compound = true;
+ // Define nodes
+ splines=false;
+ node [shape=oval, style=filled];
+ nodesep=0.2;
+ ranksep=0.4;
+
+ TOP [label="~scalar\n+int+tmp+0", fillcolor=black, fontcolor=white, group=center];
+
+ // ------------------------
+ PTRHQ [label="~tmp\n+tmp+0", fillcolor=green, fontcolor=white, group=left];
+ TOP -> PTRHQ
+
+ VALH_ [label="~nscalar\n+int+tmp", fillcolor=white, group=center];
+ TOP -> VALH_
+
+ INTHQ [label="~int\n+int+0", fillcolor=lightblue, group=right];
+ TOP -> INTHQ
+
+ LEFTHI [label="junk", style=invis, group=junkleft]
+
+
+ PTRHQ -> LEFTHI-> VALH_ -> INTHQ [style=invis];
+ { rank=same; PTRHQ; LEFTHI; VALH_; INTHQ }
+
+ // ------------------------
+ PTRH_ [label="~ntmp\n+tmp", fillcolor=green, fontcolor=white, group=left];
+ VALH_ -> PTRH_
+ PTRHQ -> PTRH_
+
+ INTH_ [label="~nint\n+int", fillcolor=lightblue, group=right];
+ VALH_ -> INTH_
+ INTHQ -> INTH_
+ { rank=same; PTRH_; INTH_ }
+
+ // ------------------------
+ NILX_ [label="~nil\n[int,tmp]+0", fillcolor=aqua, group=junkleft];
+ INTHQ -> NILX_
+ PTRHQ -> NILX_
+
+ NIL__ [label="nil\n[+int+tmp],0", fillcolor=pink];
+ VALH_ -> NIL__;
+
+ NILX_ -> NIL__ [style=invis];
+ { rank=same; NIL__; NILX_ }
+
+ // ------------------------
+ PTRL_ [label="ntmp\ntmp", fillcolor=green, fontcolor=white, group=left];
+ PTRH_ -> PTRL_
+
+ INTL_ [label="nint\nint", fillcolor=lightblue, group=right];
+ INTH_ -> INTL_
+ { rank=same; PTRL_; INTL_ }
+
+ // ------------------------
+ PTRL0 [label="tmp\ntmp,0", fillcolor=green, fontcolor=white, group=left];
+ PTRL_ -> PTRL0
+ NIL__ -> PTRL0
+
+ VALL_ [label="nscalar\nint,tmp", fillcolor=white, group=center];
+ PTRL_ -> VALL_
+ INTL_ -> VALL_
+ NILX_ -> VALL_
+
+ INTL0 [label="int\nint,0", fillcolor=lightblue, group=right];
+ INTL_ -> INTL0
+ NIL__ -> INTL0
+
+ PTRL0 -> VALL_ -> INTL0 [style=invis];
+ { rank=same; PTRL0; VALL_; INTL0 }
+
+ // ------------------------
+ BOT [label="scalar\nint,tmp,0", fillcolor=black, fontcolor=white, group=center];
+ PTRL0 -> BOT
+ INTL0 -> BOT
+ VALL_ -> BOT
+
+ // ------------------------
+ // Verticle alignment
+ TOP -> VALH_ -> VALL_ -> BOT [style=invis];
+ PTRHQ -> PTRH_ -> PTRL_ -> PTRL0 [style=invis];
+ INTHQ -> INTH_ -> INTL_ -> INTL0 [style=invis];
+ LEFTHI -> NILX_ [style=invis]
+}
diff --git a/docs/chapter19/lattice2.svg b/docs/chapter19/lattice2.svg
new file mode 100644
index 00000000..857bca2d
--- /dev/null
+++ b/docs/chapter19/lattice2.svg
@@ -0,0 +1,260 @@
+<?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 12.2.0 (20241103.1931)
+ -->
+<!-- Title: Lattice Pages: 1 -->
+<svg width="478pt" height="501pt"
+ viewBox="0.00 0.00 477.96 500.90" 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 496.9)">
+<title>Lattice</title>
+<polygon fill="white" stroke="none" points="-4,4 -4,-496.9 473.96,-496.9 473.96,4 -4,4"/>
+<!-- TOP -->
+<g id="node1" class="node">
+<title>TOP</title>
+<ellipse fill="black" stroke="black" cx="240.13" cy="-463.91" rx="59.04" ry="28.99"/>
+<text text-anchor="middle" x="240.13" y="-467.11" font-family="Times New Roman,serif" font-size="14.00" fill="white">~scalar</text>
+<text text-anchor="middle" x="240.13" y="-450.61" font-family="Times New Roman,serif" font-size="14.00" fill="white">+int+tmp+0</text>
+</g>
+<!-- PTRHQ -->
+<g id="node2" class="node">
+<title>PTRHQ</title>
+<ellipse fill="green" stroke="black" cx="43.13" cy="-376.92" rx="43.13" ry="28.99"/>
+<text text-anchor="middle" x="43.13" y="-380.12" font-family="Times New Roman,serif" font-size="14.00" fill="white">~tmp</text>
+<text text-anchor="middle" x="43.13" y="-363.62" font-family="Times New Roman,serif" font-size="14.00" fill="white">+tmp+0</text>
+</g>
+<!-- TOP&#45;&gt;PTRHQ -->
+<g id="edge1" class="edge">
+<title>TOP&#45;&gt;PTRHQ</title>
+<path fill="none" stroke="black" d="M196.41,-444.04C164.74,-430.38 121.99,-411.94 89.68,-398"/>
+<polygon fill="black" stroke="black" points="91.11,-394.81 80.55,-394.06 88.34,-401.24 91.11,-394.81"/>
+</g>
+<!-- VALH_ -->
+<g id="node3" class="node">
+<title>VALH_</title>
+<ellipse fill="white" stroke="black" cx="240.13" cy="-376.92" rx="48.44" ry="28.99"/>
+<text text-anchor="middle" x="240.13" y="-380.12" font-family="Times New Roman,serif" font-size="14.00">~nscalar</text>
+<text text-anchor="middle" x="240.13" y="-363.62" font-family="Times New Roman,serif" font-size="14.00">+int+tmp</text>
+</g>
+<!-- TOP&#45;&gt;VALH_ -->
+<g id="edge2" class="edge">
+<title>TOP&#45;&gt;VALH_</title>
+<path fill="none" stroke="black" d="M235.15,-434.61C234.93,-429.02 234.85,-423.06 234.9,-417.18"/>
+<polygon fill="black" stroke="black" points="238.39,-417.69 235.12,-407.61 231.39,-417.52 238.39,-417.69"/>
+</g>
+<!-- TOP&#45;&gt;VALH_ -->
+<!-- INTHQ -->
+<g id="node4" class="node">
+<title>INTHQ</title>
+<ellipse fill="lightblue" stroke="black" cx="432.13" cy="-376.92" rx="37.83" ry="28.99"/>
+<text text-anchor="middle" x="432.13" y="-380.12" font-family="Times New Roman,serif" font-size="14.00">~int</text>
+<text text-anchor="middle" x="432.13" y="-363.62" font-family="Times New Roman,serif" font-size="14.00">+int+0</text>
+</g>
+<!-- TOP&#45;&gt;INTHQ -->
+<g id="edge3" class="edge">
+<title>TOP&#45;&gt;INTHQ</title>
+<path fill="none" stroke="black" d="M283.22,-443.83C314.91,-429.81 357.78,-410.83 389.39,-396.84"/>
+<polygon fill="black" stroke="black" points="390.53,-400.16 398.26,-392.92 387.7,-393.76 390.53,-400.16"/>
+</g>
+<!-- LEFTHI -->
+<!-- PTRHQ&#45;&gt;LEFTHI -->
+<!-- PTRH_ -->
+<g id="node6" class="node">
+<title>PTRH_</title>
+<ellipse fill="green" stroke="black" cx="43.13" cy="-289.94" rx="36.77" ry="28.99"/>
+<text text-anchor="middle" x="43.13" y="-293.14" font-family="Times New Roman,serif" font-size="14.00" fill="white">~ntmp</text>
+<text text-anchor="middle" x="43.13" y="-276.64" font-family="Times New Roman,serif" font-size="14.00" fill="white">+tmp</text>
+</g>
+<!-- PTRHQ&#45;&gt;PTRH_ -->
+<g id="edge8" class="edge">
+<title>PTRHQ&#45;&gt;PTRH_</title>
+<path fill="none" stroke="black" d="M38.15,-347.62C37.93,-342.03 37.85,-336.07 37.9,-330.2"/>
+<polygon fill="black" stroke="black" points="41.39,-330.71 38.12,-320.63 34.39,-330.54 41.39,-330.71"/>
+</g>
+<!-- PTRHQ&#45;&gt;PTRH_ -->
+<!-- NILX_ -->
+<g id="node8" class="node">
+<title>NILX_</title>
+<ellipse fill="aqua" stroke="black" cx="150.13" cy="-289.94" rx="56.39" ry="28.99"/>
+<text text-anchor="middle" x="150.13" y="-293.14" font-family="Times New Roman,serif" font-size="14.00">~nil</text>
+<text text-anchor="middle" x="150.13" y="-276.64" font-family="Times New Roman,serif" font-size="14.00">[int,tmp]+0</text>
+</g>
+<!-- PTRHQ&#45;&gt;NILX_ -->
+<g id="edge12" class="edge">
+<title>PTRHQ&#45;&gt;NILX_</title>
+<path fill="none" stroke="black" d="M70.41,-354.26C82.91,-344.33 97.94,-332.4 111.56,-321.58"/>
+<polygon fill="black" stroke="black" points="113.43,-324.56 119.09,-315.6 109.08,-319.08 113.43,-324.56"/>
+</g>
+<!-- VALH_&#45;&gt;INTHQ -->
+<!-- VALH_&#45;&gt;PTRH_ -->
+<g id="edge7" class="edge">
+<title>VALH_&#45;&gt;PTRH_</title>
+<path fill="none" stroke="black" d="M201.21,-359.13C167.78,-344.71 119.68,-323.96 85.33,-309.14"/>
+<polygon fill="black" stroke="black" points="87.1,-306.09 76.53,-305.35 84.33,-312.52 87.1,-306.09"/>
+</g>
+<!-- INTH_ -->
+<g id="node7" class="node">
+<title>INTH_</title>
+<ellipse fill="lightblue" stroke="black" cx="432.13" cy="-289.94" rx="31.47" ry="28.99"/>
+<text text-anchor="middle" x="432.13" y="-293.14" font-family="Times New Roman,serif" font-size="14.00">~nint</text>
+<text text-anchor="middle" x="432.13" y="-276.64" font-family="Times New Roman,serif" font-size="14.00">+int</text>
+</g>
+<!-- VALH_&#45;&gt;INTH_ -->
+<g id="edge9" class="edge">
+<title>VALH_&#45;&gt;INTH_</title>
+<path fill="none" stroke="black" d="M278.53,-358.93C311.9,-344.16 360.02,-322.86 393.48,-308.05"/>
+<polygon fill="black" stroke="black" points="394.67,-311.35 402.4,-304.1 391.84,-304.95 394.67,-311.35"/>
+</g>
+<!-- NIL__ -->
+<g id="node9" class="node">
+<title>NIL__</title>
+<ellipse fill="pink" stroke="black" cx="324.13" cy="-289.94" rx="62.23" ry="28.99"/>
+<text text-anchor="middle" x="324.13" y="-293.14" font-family="Times New Roman,serif" font-size="14.00">nil</text>
+<text text-anchor="middle" x="324.13" y="-276.64" font-family="Times New Roman,serif" font-size="14.00">[+int+tmp],0</text>
+</g>
+<!-- VALH_&#45;&gt;NIL__ -->
+<g id="edge13" class="edge">
+<title>VALH_&#45;&gt;NIL__</title>
+<path fill="none" stroke="black" d="M264.43,-351.34C272.67,-343.01 282.01,-333.55 290.83,-324.64"/>
+<polygon fill="black" stroke="black" points="293.12,-327.3 297.66,-317.72 288.14,-322.38 293.12,-327.3"/>
+</g>
+<!-- VALL_ -->
+<g id="node13" class="node">
+<title>VALL_</title>
+<ellipse fill="white" stroke="black" cx="240.13" cy="-115.97" rx="39.42" ry="28.99"/>
+<text text-anchor="middle" x="240.13" y="-119.17" font-family="Times New Roman,serif" font-size="14.00">nscalar</text>
+<text text-anchor="middle" x="240.13" y="-102.67" font-family="Times New Roman,serif" font-size="14.00">int,tmp</text>
+</g>
+<!-- VALH_&#45;&gt;VALL_ -->
+<!-- INTHQ&#45;&gt;INTH_ -->
+<g id="edge10" class="edge">
+<title>INTHQ&#45;&gt;INTH_</title>
+<path fill="none" stroke="black" d="M427.17,-348.1C426.94,-342.37 426.85,-336.25 426.9,-330.21"/>
+<polygon fill="black" stroke="black" points="430.39,-330.42 427.13,-320.34 423.39,-330.26 430.39,-330.42"/>
+</g>
+<!-- INTHQ&#45;&gt;INTH_ -->
+<!-- INTHQ&#45;&gt;NILX_ -->
+<g id="edge11" class="edge">
+<title>INTHQ&#45;&gt;NILX_</title>
+<path fill="none" stroke="black" d="M396.99,-365.33C350,-351.17 266.03,-325.87 209.23,-308.75"/>
+<polygon fill="black" stroke="black" points="210.28,-305.41 199.69,-305.88 208.26,-312.11 210.28,-305.41"/>
+</g>
+<!-- LEFTHI&#45;&gt;VALH_ -->
+<!-- LEFTHI&#45;&gt;NILX_ -->
+<!-- PTRL_ -->
+<g id="node10" class="node">
+<title>PTRL_</title>
+<ellipse fill="green" stroke="black" cx="43.13" cy="-202.96" rx="31.47" ry="28.99"/>
+<text text-anchor="middle" x="43.13" y="-206.16" font-family="Times New Roman,serif" font-size="14.00" fill="white">ntmp</text>
+<text text-anchor="middle" x="43.13" y="-189.66" font-family="Times New Roman,serif" font-size="14.00" fill="white">tmp</text>
+</g>
+<!-- PTRH_&#45;&gt;PTRL_ -->
+<g id="edge15" class="edge">
+<title>PTRH_&#45;&gt;PTRL_</title>
+<path fill="none" stroke="black" d="M38.17,-261.11C37.94,-255.39 37.85,-249.26 37.9,-243.22"/>
+<polygon fill="black" stroke="black" points="41.39,-243.44 38.13,-233.36 34.39,-243.27 41.39,-243.44"/>
+</g>
+<!-- PTRH_&#45;&gt;PTRL_ -->
+<!-- INTL_ -->
+<g id="node11" class="node">
+<title>INTL_</title>
+<ellipse fill="lightblue" stroke="black" cx="432.13" cy="-202.96" rx="27" ry="28.99"/>
+<text text-anchor="middle" x="432.13" y="-206.16" font-family="Times New Roman,serif" font-size="14.00">nint</text>
+<text text-anchor="middle" x="432.13" y="-189.66" font-family="Times New Roman,serif" font-size="14.00">int</text>
+</g>
+<!-- INTH_&#45;&gt;INTL_ -->
+<g id="edge16" class="edge">
+<title>INTH_&#45;&gt;INTL_</title>
+<path fill="none" stroke="black" d="M427.17,-261.11C426.94,-255.39 426.85,-249.26 426.9,-243.22"/>
+<polygon fill="black" stroke="black" points="430.39,-243.44 427.13,-233.36 423.39,-243.27 430.39,-243.44"/>
+</g>
+<!-- INTH_&#45;&gt;INTL_ -->
+<!-- NILX_&#45;&gt;NIL__ -->
+<!-- NILX_&#45;&gt;VALL_ -->
+<g id="edge21" class="edge">
+<title>NILX_&#45;&gt;VALL_</title>
+<path fill="none" stroke="black" d="M164.79,-261.6C180.03,-232.15 203.95,-185.91 220.74,-153.45"/>
+<polygon fill="black" stroke="black" points="223.74,-155.27 225.23,-144.78 217.53,-152.06 223.74,-155.27"/>
+</g>
+<!-- PTRL0 -->
+<g id="node12" class="node">
+<title>PTRL0</title>
+<ellipse fill="green" stroke="black" cx="43.13" cy="-115.97" rx="34.12" ry="28.99"/>
+<text text-anchor="middle" x="43.13" y="-119.17" font-family="Times New Roman,serif" font-size="14.00" fill="white">tmp</text>
+<text text-anchor="middle" x="43.13" y="-102.67" font-family="Times New Roman,serif" font-size="14.00" fill="white">tmp,0</text>
+</g>
+<!-- NIL__&#45;&gt;PTRL0 -->
+<g id="edge18" class="edge">
+<title>NIL__&#45;&gt;PTRL0</title>
+<path fill="none" stroke="black" d="M286.24,-266.48C232.36,-233.13 134.24,-172.38 80.6,-139.17"/>
+<polygon fill="black" stroke="black" points="82.76,-136.39 72.41,-134.1 79.07,-142.34 82.76,-136.39"/>
+</g>
+<!-- INTL0 -->
+<g id="node14" class="node">
+<title>INTL0</title>
+<ellipse fill="lightblue" stroke="black" cx="432.13" cy="-115.97" rx="28.81" ry="28.99"/>
+<text text-anchor="middle" x="432.13" y="-119.17" font-family="Times New Roman,serif" font-size="14.00">int</text>
+<text text-anchor="middle" x="432.13" y="-102.67" font-family="Times New Roman,serif" font-size="14.00">int,0</text>
+</g>
+<!-- NIL__&#45;&gt;INTL0 -->
+<g id="edge23" class="edge">
+<title>NIL__&#45;&gt;INTL0</title>
+<path fill="none" stroke="black" d="M341.49,-261.99C360.34,-231.61 390.49,-183.05 410.79,-150.35"/>
+<polygon fill="black" stroke="black" points="413.65,-152.38 415.95,-142.04 407.7,-148.69 413.65,-152.38"/>
+</g>
+<!-- PTRL_&#45;&gt;PTRL0 -->
+<g id="edge17" class="edge">
+<title>PTRL_&#45;&gt;PTRL0</title>
+<path fill="none" stroke="black" d="M38.17,-174.13C37.94,-168.47 37.85,-162.41 37.9,-156.43"/>
+<polygon fill="black" stroke="black" points="41.39,-156.75 38.12,-146.67 34.39,-156.58 41.39,-156.75"/>
+</g>
+<!-- PTRL_&#45;&gt;PTRL0 -->
+<!-- PTRL_&#45;&gt;VALL_ -->
+<g id="edge19" class="edge">
+<title>PTRL_&#45;&gt;VALL_</title>
+<path fill="none" stroke="black" d="M71.36,-189.78C103.94,-175.73 157.92,-152.44 196.04,-135.99"/>
+<polygon fill="black" stroke="black" points="197.16,-139.32 204.96,-132.15 194.39,-132.9 197.16,-139.32"/>
+</g>
+<!-- INTL_&#45;&gt;VALL_ -->
+<g id="edge20" class="edge">
+<title>INTL_&#45;&gt;VALL_</title>
+<path fill="none" stroke="black" d="M407.43,-191.02C376.23,-177.21 322.01,-153.22 283.77,-136.29"/>
+<polygon fill="black" stroke="black" points="285.39,-133.18 274.83,-132.33 282.55,-139.58 285.39,-133.18"/>
+</g>
+<!-- INTL_&#45;&gt;INTL0 -->
+<g id="edge22" class="edge">
+<title>INTL_&#45;&gt;INTL0</title>
+<path fill="none" stroke="black" d="M427.17,-174.13C426.94,-168.41 426.85,-162.28 426.9,-156.24"/>
+<polygon fill="black" stroke="black" points="430.39,-156.46 427.13,-146.38 423.39,-156.29 430.39,-156.46"/>
+</g>
+<!-- INTL_&#45;&gt;INTL0 -->
+<!-- PTRL0&#45;&gt;VALL_ -->
+<!-- BOT -->
+<g id="node15" class="node">
+<title>BOT</title>
+<ellipse fill="black" stroke="black" cx="240.13" cy="-28.99" rx="46.85" ry="28.99"/>
+<text text-anchor="middle" x="240.13" y="-32.19" font-family="Times New Roman,serif" font-size="14.00" fill="white">scalar</text>
+<text text-anchor="middle" x="240.13" y="-15.69" font-family="Times New Roman,serif" font-size="14.00" fill="white">int,tmp,0</text>
+</g>
+<!-- PTRL0&#45;&gt;BOT -->
+<g id="edge26" class="edge">
+<title>PTRL0&#45;&gt;BOT</title>
+<path fill="none" stroke="black" d="M73.48,-101.88C105.02,-88.28 154.83,-66.79 191.79,-50.85"/>
+<polygon fill="black" stroke="black" points="193.14,-54.08 200.94,-46.9 190.37,-47.65 193.14,-54.08"/>
+</g>
+<!-- VALL_&#45;&gt;INTL0 -->
+<!-- VALL_&#45;&gt;BOT -->
+<g id="edge28" class="edge">
+<title>VALL_&#45;&gt;BOT</title>
+<path fill="none" stroke="black" d="M235.17,-87.15C234.94,-81.48 234.85,-75.42 234.9,-69.44"/>
+<polygon fill="black" stroke="black" points="238.39,-69.76 235.12,-59.68 231.39,-69.6 238.39,-69.76"/>
+</g>
+<!-- VALL_&#45;&gt;BOT -->
+<!-- INTL0&#45;&gt;BOT -->
+<g id="edge27" class="edge">
+<title>INTL0&#45;&gt;BOT</title>
+<path fill="none" stroke="black" d="M405.84,-103.34C375.52,-89.91 325.06,-67.58 287.78,-51.08"/>
+<polygon fill="black" stroke="black" points="289.59,-48.05 279.03,-47.21 286.75,-54.45 289.59,-48.05"/>
+</g>
+</g>
+</svg>
diff --git a/docs/chapter19/webtcp.min.js b/docs/chapter19/webtcp.min.js
new file mode 100644
index 00000000..bf23ee5a
--- /dev/null
+++ b/docs/chapter19/webtcp.min.js
@@ -0,0 +1,65 @@
+function Http(j){this.cID=Math.random().toString(36).substr(2);this.webtcp=j;this.webtcp.httpClients[this.cID]=this;this.createPacket=function(){return{cID:this.cID,type:"http",host:this.remoteAddress,port:this.remotePort,method:null,data:null}};this.ondata=function(){};this.get=function(g,c){this.on("data",c);this.rpc("httpGet",[g])};this.post=function(g,c,j){this.on("data",j);this.rpc("httpPost",[g,c])}}Http.prototype=new WebTCPIO;function Socket(j,g,c,p){this.sID=Math.random().toString(36).substr(2);this.webtcp=j;this.closed=this.ready=!1;this.options=p;this.sockOpts={_updating:!1};this.remoteAddress=g;this.remotePort=c;this.webtcp.sockets[this.sID]=this;this.createPacket=function(){return{id:Math.random().toString(36).substr(2),sID:this.sID,type:"tcp",options:this.options,host:this.remoteAddress,port:this.remotePort,data:null}};this.onconnect=function(){this.webtcp.sockets[sID].ready=!0;this.webtcp.sockets[sID].closed=!1};
+this.ondata=function(){};this.onend=function(){};this.ontimeout=function(){};this.ondrain=function(){};this.onclose=function(){this.webtcp.sockets[sID].ready=!1;this.webtcp.sockets[sID].closed=!0};this.getSockOpts=function(){this.sockOpts._updating=!0;this.rpc("getSockOpts")};this.onSockOptsRcv=function(c){this.sockOpts=c;this.sockOpts._updating=!1}}Socket.prototype=new WebTCPIO;var JSON;JSON||(JSON={});
+(function(){function j(c,p){var q,t,n,x,i=k,e,l=p[c];l&&"object"==typeof l&&"function"==typeof l.toJSON&&(l=l.toJSON(c));"function"==typeof r&&(l=r.call(p,c,l));switch(typeof l){case "string":return g(l);case "number":return isFinite(l)?String(l):"null";case "boolean":case "null":return String(l);case "object":if(!l)return"null";k+=v;e=[];if("[object Array]"===Object.prototype.toString.apply(l)){x=l.length;for(q=0;q<x;q+=1)e[q]=j(q,l)||"null";n=0===e.length?"[]":k?"[\n"+k+e.join(",\n"+k)+"\n"+i+"]":
+"["+e.join(",")+"]";k=i;return n}if(r&&"object"==typeof r){x=r.length;for(q=0;q<x;q+=1)"string"==typeof r[q]&&(t=r[q],n=j(t,l),n&&e.push(g(t)+(k?": ":":")+n))}else for(t in l)Object.prototype.hasOwnProperty.call(l,t)&&(n=j(t,l),n&&e.push(g(t)+(k?": ":":")+n));n=0===e.length?"{}":k?"{\n"+k+e.join(",\n"+k)+"\n"+i+"}":"{"+e.join(",")+"}";k=i;return n}}function g(c){n.lastIndex=0;return n.test(c)?'"'+c.replace(n,function(c){var g=D[c];return"string"==typeof g?g:"\\u"+("0000"+c.charCodeAt(0).toString(16)).slice(-4)})+
+'"':'"'+c+'"'}function c(c){return 10>c?"0"+c:c}"use strict";"function"!=typeof Date.prototype.toJSON&&(Date.prototype.toJSON=function(){return isFinite(this.valueOf())?this.getUTCFullYear()+"-"+c(this.getUTCMonth()+1)+"-"+c(this.getUTCDate())+"T"+c(this.getUTCHours())+":"+c(this.getUTCMinutes())+":"+c(this.getUTCSeconds())+"Z":null},String.prototype.toJSON=Number.prototype.toJSON=Boolean.prototype.toJSON=function(){return this.valueOf()});var p=/[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
+n=/[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,k,v,D={"\b":"\\b","\t":"\\t","\n":"\\n","\f":"\\f","\r":"\\r",'"':'\\"',"\\":"\\\\"},r;"function"!=typeof JSON.stringify&&(JSON.stringify=function(c,g,p){var n;v=k="";if("number"==typeof p)for(n=0;n<p;n+=1)v+=" ";else"string"==typeof p&&(v=p);r=g;if(!g||"function"==typeof g||"object"==typeof g&&"number"==typeof g.length)return j("",{"":c});throw Error("JSON.stringify");});"function"!=
+typeof JSON.parse&&(JSON.parse=function(c,g){function j(c,k){var i,e,l=c[k];if(l&&"object"==typeof l)for(i in l)Object.prototype.hasOwnProperty.call(l,i)&&(e=j(l,i),void 0!==e?l[i]=e:delete l[i]);return g.call(c,k,l)}var k,c=String(c);p.lastIndex=0;p.test(c)&&(c=c.replace(p,function(c){return"\\u"+("0000"+c.charCodeAt(0).toString(16)).slice(-4)}));if(/^[\],:{}\s]*$/.test(c.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,"@").replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,
+"]").replace(/(?:^|:|,)(?:\s*\[)+/g,"")))return k=eval("("+c+")"),"function"==typeof g?j({"":k},""):k;throw new SyntaxError("JSON.parse");})})();
+SockJS=function(){var j=document,g=window,c={},p=function(){};p.prototype.addEventListener=function(a,b){this._listeners||(this._listeners={});a in this._listeners||(this._listeners[a]=[]);var f=this._listeners[a];-1===c.arrIndexOf(f,b)&&f.push(b)};p.prototype.removeEventListener=function(a,b){if(this._listeners&&a in this._listeners){var f=this._listeners[a],d=c.arrIndexOf(f,b);-1!==d&&(1<f.length?this._listeners[a]=f.slice(0,d).concat(f.slice(d+1)):delete this._listeners[a])}};p.prototype.dispatchEvent=
+function(a){var b=a.type,c=Array.prototype.slice.call(arguments,0);this["on"+b]&&this["on"+b].apply(this,c);if(this._listeners&&b in this._listeners)for(var d=0;d<this._listeners[b].length;d++)this._listeners[b][d].apply(this,c)};var n=function(a,b){this.type=a;if("undefined"!=typeof b)for(var c in b)b.hasOwnProperty(c)&&(this[c]=b[c])};n.prototype.toString=function(){var a=[],b;for(b in this)if(this.hasOwnProperty(b)){var c=this[b];"function"==typeof c&&(c="[function]");a.push(b+"="+c)}return"SimpleEvent("+
+a.join(", ")+")"};var k=function(a){this.events=a||[]};k.prototype.emit=function(a){var b=Array.prototype.slice.call(arguments,1);!this.nuked&&this["on"+a]&&this["on"+a].apply(this,b);-1===c.arrIndexOf(this.events,a)&&c.log("Event "+JSON.stringify(a)+" not listed "+JSON.stringify(this.events)+" in "+this)};k.prototype.nuke=function(){this.nuked=!0;for(var a=0;a<this.events.length;a++)delete this[this.events[a]]};c.random_string=function(a,b){var b=b||37,c,d=[];for(c=0;c<a;c++)d.push("abcdefghijklmnopqrstuvwxyz0123456789_".substr(Math.floor(Math.random()*
+b),1));return d.join("")};c.random_number=function(a){return Math.floor(Math.random()*a)};c.random_number_string=function(a){var b=(""+(a-1)).length;return(Array(b+1).join("0")+c.random_number(a)).slice(-b)};c.getOrigin=function(a){return(a+"/").split("/").slice(0,3).join("/")};c.isSameOriginUrl=function(a,b){return b||(b=g.location.href),a.split("/").slice(0,3).join("/")===b.split("/").slice(0,3).join("/")};c.getParentDomain=function(a){return/^[0-9.]*$/.test(a)||/^\[/.test(a)||!/[.]/.test(a)?a:
+a.split(".").slice(1).join(".")};c.objectExtend=function(a,b){for(var c in b)b.hasOwnProperty(c)&&(a[c]=b[c]);return a};c.polluteGlobalNamespace=function(){"_jp"in g||(g._jp={})};c.closeFrame=function(a,b){return"c"+JSON.stringify([a,b])};c.userSetCode=function(a){return 1E3===a||3E3<=a&&4999>=a};c.countRTO=function(a){var b;return 100<a?b=3*a:b=a+200,b};c.log=function(){g.console&&console.log&&console.log.apply&&console.log.apply(console,arguments)};c.bind=function(a,b){return a.bind?a.bind(b):function(){return a.apply(b,
+arguments)}};c.flatUrl=function(a){return-1===a.indexOf("?")&&-1===a.indexOf("#")};c.amendUrl=function(a){var b=j.location;if(!a)throw Error("Wrong url for SockJS");if(!c.flatUrl(a))throw Error("Only basic urls are supported in SockJS");return 0===a.indexOf("//")&&(a=b.protocol+a),0===a.indexOf("/")&&(a=b.protocol+"//"+b.host+a),a=a.replace(/[/]+$/,""),a};c.arrIndexOf=function(a,b){for(var c=0;c<a.length;c++)if(a[c]===b)return c;return-1};c.arrSkip=function(a,b){var f=c.arrIndexOf(a,b);return-1===
+f?a.slice():a.slice(0,f).concat(a.slice(f+1))};c.isArray=Array.isArray||function(a){return 0<={}.toString.call(a).indexOf("Array")};c.delay=function(a,b){return"function"==typeof a&&(b=a,a=0),setTimeout(b,a)};var v=/[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,D={"\x00":"\\u0000","\u0001":"\\u0001","\u0002":"\\u0002","\u0003":"\\u0003","\u0004":"\\u0004","\u0005":"\\u0005","\u0006":"\\u0006","\u0007":"\\u0007","\b":"\\b",
+"\t":"\\t","\n":"\\n","\x0B":"\\u000b","\f":"\\f","\r":"\\r","\u000e":"\\u000e","\u000f":"\\u000f","\u0010":"\\u0010","\u0011":"\\u0011","\u0012":"\\u0012","\u0013":"\\u0013","\u0014":"\\u0014","\u0015":"\\u0015","\u0016":"\\u0016","\u0017":"\\u0017","\u0018":"\\u0018","\u0019":"\\u0019","\u001a":"\\u001a","\u001b":"\\u001b","\u001c":"\\u001c","\u001d":"\\u001d","\u001e":"\\u001e","\u001f":"\\u001f",'"':'\\"',"\\":"\\\\","\u007f":"\\u007f","\u0080":"\\u0080","\u0081":"\\u0081","\u0082":"\\u0082",
+"\u0083":"\\u0083","\u0084":"\\u0084","\u0085":"\\u0085","\u0086":"\\u0086","\u0087":"\\u0087","\u0088":"\\u0088","\u0089":"\\u0089","\u008a":"\\u008a","\u008b":"\\u008b","\u008c":"\\u008c","\u008d":"\\u008d","\u008e":"\\u008e","\u008f":"\\u008f","\u0090":"\\u0090","\u0091":"\\u0091","\u0092":"\\u0092","\u0093":"\\u0093","\u0094":"\\u0094","\u0095":"\\u0095","\u0096":"\\u0096","\u0097":"\\u0097","\u0098":"\\u0098","\u0099":"\\u0099","\u009a":"\\u009a","\u009b":"\\u009b","\u009c":"\\u009c","\u009d":"\\u009d",
+"\u009e":"\\u009e","\u009f":"\\u009f","\u00ad":"\\u00ad","\u0600":"\\u0600","\u0601":"\\u0601","\u0602":"\\u0602","\u0603":"\\u0603","\u0604":"\\u0604","\u070f":"\\u070f","\u17b4":"\\u17b4","\u17b5":"\\u17b5","\u200c":"\\u200c","\u200d":"\\u200d","\u200e":"\\u200e","\u200f":"\\u200f","\u2028":"\\u2028","\u2029":"\\u2029","\u202a":"\\u202a","\u202b":"\\u202b","\u202c":"\\u202c","\u202d":"\\u202d","\u202e":"\\u202e","\u202f":"\\u202f","\u2060":"\\u2060","\u2061":"\\u2061","\u2062":"\\u2062","\u2063":"\\u2063",
+"\u2064":"\\u2064","\u2065":"\\u2065","\u2066":"\\u2066","\u2067":"\\u2067","\u2068":"\\u2068","\u2069":"\\u2069","\u206a":"\\u206a","\u206b":"\\u206b","\u206c":"\\u206c","\u206d":"\\u206d","\u206e":"\\u206e","\u206f":"\\u206f","\ufeff":"\\ufeff","\ufff0":"\\ufff0","\ufff1":"\\ufff1","\ufff2":"\\ufff2","\ufff3":"\\ufff3","\ufff4":"\\ufff4","\ufff5":"\\ufff5","\ufff6":"\\ufff6","\ufff7":"\\ufff7","\ufff8":"\\ufff8","\ufff9":"\\ufff9","\ufffa":"\\ufffa","\ufffb":"\\ufffb","\ufffc":"\\ufffc","\ufffd":"\\ufffd",
+"\ufffe":"\\ufffe","\uffff":"\\uffff"},r=/[\x00-\x1f\ud800-\udfff\ufffe\uffff\u0300-\u0333\u033d-\u0346\u034a-\u034c\u0350-\u0352\u0357-\u0358\u035c-\u0362\u0374\u037e\u0387\u0591-\u05af\u05c4\u0610-\u0617\u0653-\u0654\u0657-\u065b\u065d-\u065e\u06df-\u06e2\u06eb-\u06ec\u0730\u0732-\u0733\u0735-\u0736\u073a\u073d\u073f-\u0741\u0743\u0745\u0747\u07eb-\u07f1\u0951\u0958-\u095f\u09dc-\u09dd\u09df\u0a33\u0a36\u0a59-\u0a5b\u0a5e\u0b5c-\u0b5d\u0e38-\u0e39\u0f43\u0f4d\u0f52\u0f57\u0f5c\u0f69\u0f72-\u0f76\u0f78\u0f80-\u0f83\u0f93\u0f9d\u0fa2\u0fa7\u0fac\u0fb9\u1939-\u193a\u1a17\u1b6b\u1cda-\u1cdb\u1dc0-\u1dcf\u1dfc\u1dfe\u1f71\u1f73\u1f75\u1f77\u1f79\u1f7b\u1f7d\u1fbb\u1fbe\u1fc9\u1fcb\u1fd3\u1fdb\u1fe3\u1feb\u1fee-\u1fef\u1ff9\u1ffb\u1ffd\u2000-\u2001\u20d0-\u20d1\u20d4-\u20d7\u20e7-\u20e9\u2126\u212a-\u212b\u2329-\u232a\u2adc\u302b-\u302c\uaab2-\uaab3\uf900-\ufa0d\ufa10\ufa12\ufa15-\ufa1e\ufa20\ufa22\ufa25-\ufa26\ufa2a-\ufa2d\ufa30-\ufa6d\ufa70-\ufad9\ufb1d\ufb1f\ufb2a-\ufb36\ufb38-\ufb3c\ufb3e\ufb40-\ufb41\ufb43-\ufb44\ufb46-\ufb4e\ufff0-\uffff]/g,
+B,M=JSON&&JSON.stringify||function(a){return v.lastIndex=0,v.test(a)&&(a=a.replace(v,function(a){return D[a]})),'"'+a+'"'};c.quote=function(a){a=M(a);r.lastIndex=0;if(r.test(a)){if(!B){var b,c={},d=[];for(b=0;65536>b;b++)d.push(String.fromCharCode(b));B=(r.lastIndex=0,d.join("").replace(r,function(a){return c[a]="\\u"+("0000"+a.charCodeAt(0).toString(16)).slice(-4),""}),r.lastIndex=0,c)}a=a.replace(r,function(a){return B[a]})}return a};var q="websocket xdr-streaming xhr-streaming iframe-eventsource iframe-htmlfile xdr-polling xhr-polling iframe-xhr-polling jsonp-polling".split(" ");
+c.probeProtocols=function(){for(var a={},b=0;b<q.length;b++){var c=q[b];a[c]=e[c]&&e[c].enabled()}return a};c.detectProtocols=function(a,b,c){var d={},h=[];b||(b=q);for(var m=0;m<b.length;m++){var g=b[m];d[g]=a[g]}var e=function(a){var b=a.shift();d[b]?h.push(b):0<a.length&&e(a)};return!1!==c.websocket&&e(["websocket"]),d["xhr-streaming"]&&!c.null_origin?h.push("xhr-streaming"):d["xdr-streaming"]&&!c.cookie_needed&&!c.null_origin?h.push("xdr-streaming"):e(["iframe-eventsource","iframe-htmlfile"]),
+d["xhr-polling"]&&!c.null_origin?h.push("xhr-polling"):d["xdr-polling"]&&!c.cookie_needed&&!c.null_origin?h.push("xdr-polling"):e(["iframe-xhr-polling","jsonp-polling"]),h};c.createHook=function(){var a="a"+c.random_string(8);if(!("_sockjs_global"in g)){var b={};g._sockjs_global=function(a){return a in b||(b[a]={id:a,del:function(){delete b[a]}}),b[a]}}return g._sockjs_global(a)};c.attachMessage=function(a){c.attachEvent("message",a)};c.attachEvent=function(a,b){"undefined"!=typeof g.addEventListener?
+g.addEventListener(a,b,!1):(j.attachEvent("on"+a,b),g.attachEvent("on"+a,b))};c.detachMessage=function(a){c.detachEvent("message",a)};c.detachEvent=function(a,b){"undefined"!=typeof g.addEventListener?g.removeEventListener(a,b,!1):(j.detachEvent("on"+a,b),g.detachEvent("on"+a,b))};var t={},C=!1,x=function(){for(var a in t)t[a](),delete t[a]},i=function(){C||(C=!0,x())};c.attachEvent("beforeunload",i);c.attachEvent("unload",i);c.unload_add=function(a){var b=c.random_string(8);return t[b]=a,C&&c.delay(x),
+b};c.unload_del=function(a){a in t&&delete t[a]};c.createIframe=function(a,b){var f=j.createElement("iframe"),d,h,m=function(){clearTimeout(d);try{f.onload=null}catch(a){}f.onerror=null},g=function(){f&&(m(),setTimeout(function(){f&&f.parentNode.removeChild(f);f=null},0),c.unload_del(h))};return f.src=a,f.style.display="none",f.style.position="absolute",f.onerror=function(){f&&(g(),b("onerror"))},f.onload=function(){clearTimeout(d);d=setTimeout(function(){f&&(g(),b("onload timeout"))},2E3)},j.body.appendChild(f),
+d=setTimeout(function(){f&&(g(),b("timeout"))},15E3),h=c.unload_add(g),{post:function(a,b){try{f&&f.contentWindow&&f.contentWindow.postMessage(a,b)}catch(c){}},cleanup:g,loaded:m}};c.createHtmlfile=function(a,b){var f=new ActiveXObject("htmlfile"),d,h,m,e=function(){clearTimeout(d)},j=function(){f&&(e(),c.unload_del(h),m.parentNode.removeChild(m),m=f=null,CollectGarbage())};f.open();f.write('<html><script>document.domain="'+document.domain+'";<\/script></html>');f.close();f.parentWindow._jp=g._jp;
+var i=f.createElement("div");return f.body.appendChild(i),m=f.createElement("iframe"),i.appendChild(m),m.src=a,d=setTimeout(function(){f&&(j(),b("timeout"))},15E3),h=c.unload_add(j),{post:function(a,b){try{m&&m.contentWindow&&m.contentWindow.postMessage(a,b)}catch(c){}},cleanup:j,loaded:e}};i=function(){};i.prototype=new k(["chunk","finish"]);i.prototype._start=function(a,b,f,d){var h=this;try{h.xhr=new XMLHttpRequest}catch(m){}if(!h.xhr)try{h.xhr=new g.ActiveXObject("Microsoft.XMLHTTP")}catch(e){}if(g.ActiveXObject||
+g.XDomainRequest)b+=(-1===b.indexOf("?")?"?":"&")+"t="+ +new Date;h.unload_ref=c.unload_add(function(){h._cleanup(!0)});try{h.xhr.open(a,b,!0)}catch(j){h.emit("finish",0,"");h._cleanup();return}if(!d||!d.no_credentials)h.xhr.withCredentials="true";if(d&&d.headers)for(var i in d.headers)h.xhr.setRequestHeader(i,d.headers[i]);h.xhr.onreadystatechange=function(){if(h.xhr){var a=h.xhr;switch(a.readyState){case 3:try{var b=a.status,c=a.responseText}catch(d){}c&&0<c.length&&h.emit("chunk",b,c);break;case 4:h.emit("finish",
+a.status,a.responseText),h._cleanup(!1)}}};h.xhr.send(f)};i.prototype._cleanup=function(a){if(this.xhr){c.unload_del(this.unload_ref);this.xhr.onreadystatechange=function(){};if(a)try{this.xhr.abort()}catch(b){}this.unload_ref=this.xhr=null}};i.prototype.close=function(){this.nuke();this._cleanup(!0)};(c.XHRCorsObject=function(){var a=this,b=arguments;c.delay(function(){a._start.apply(a,b)})}).prototype=new i;(c.XHRLocalObject=function(a,b,f){var d=this;c.delay(function(){d._start(a,b,f,{no_credentials:!0})})}).prototype=
+new i;i=c.XDRObject=function(a,b,f){var d=this;c.delay(function(){d._start(a,b,f)})};i.prototype=new k(["chunk","finish"]);i.prototype._start=function(a,b,f){var d=this,h=new XDomainRequest,b=b+((-1===b.indexOf("?")?"?":"&")+"t="+ +new Date),g=h.ontimeout=h.onerror=function(){d.emit("finish",0,"");d._cleanup(!1)};h.onprogress=function(){d.emit("chunk",200,h.responseText)};h.onload=function(){d.emit("finish",200,h.responseText);d._cleanup(!1)};d.xdr=h;d.unload_ref=c.unload_add(function(){d._cleanup(!0)});
+try{d.xdr.open(a,b),d.xdr.send(f)}catch(e){g()}};i.prototype._cleanup=function(a){if(this.xdr){c.unload_del(this.unload_ref);this.xdr.ontimeout=this.xdr.onerror=this.xdr.onprogress=this.xdr.onload=null;if(a)try{this.xdr.abort()}catch(b){}this.unload_ref=this.xdr=null}};i.prototype.close=function(){this.nuke();this._cleanup(!0)};c.isXHRCorsCapable=function(){return g.XMLHttpRequest&&"withCredentials"in new XMLHttpRequest?1:g.XDomainRequest&&j.domain?2:s.enabled()?3:4};var e=function(a,b,f){var d=this,
+h;d._options={devel:!1,debug:!1,protocols_whitelist:[],info:void 0,rtt:void 0};f&&c.objectExtend(d._options,f);d._base_url=c.amendUrl(a);d._server=d._options.server||c.random_number_string(1E3);d._options.protocols_whitelist&&d._options.protocols_whitelist.length?h=d._options.protocols_whitelist:("string"==typeof b&&0<b.length?h=[b]:c.isArray(b)?h=b:h=null,h&&d._debug('Deprecated API: Use "protocols_whitelist" option instead of supplying protocol list as a second parameter to SockJS constructor.'));
+d._protocols=[];d.protocol=null;d.readyState=e.CONNECTING;d._ir=N(d._base_url);d._ir.onfinish=function(a,b){d._ir=null;a?(d._options.info&&(a=c.objectExtend(a,d._options.info)),d._options.rtt&&(b=d._options.rtt),d._applyInfo(a,b,h),d._didClose()):d._didClose(1002,"Can't connect to server",!0)}};e.prototype=new p;e.version="0.3.2";e.CONNECTING=0;e.OPEN=1;e.CLOSING=2;e.CLOSED=3;e.prototype._debug=function(){this._options.debug&&c.log.apply(c,arguments)};e.prototype._dispatchOpen=function(){this.readyState===
+e.CONNECTING?(this._transport_tref&&(clearTimeout(this._transport_tref),this._transport_tref=null),this.readyState=e.OPEN,this.dispatchEvent(new n("open"))):this._didClose(1006,"Server lost session")};e.prototype._dispatchMessage=function(a){this.readyState===e.OPEN&&this.dispatchEvent(new n("message",{data:a}))};e.prototype._dispatchHeartbeat=function(){this.readyState===e.OPEN&&this.dispatchEvent(new n("heartbeat",{}))};e.prototype._didClose=function(a,b,f){var d=this;if(d.readyState!==e.CONNECTING&&
+d.readyState!==e.OPEN&&d.readyState!==e.CLOSING)throw Error("INVALID_STATE_ERR");d._ir&&(d._ir.nuke(),d._ir=null);d._transport&&(d._transport.doCleanup(),d._transport=null);var h=new n("close",{code:a,reason:b,wasClean:c.userSetCode(a)});if(!c.userSetCode(a)&&d.readyState===e.CONNECTING&&!f){if(d._try_next_protocol(h))return;h=new n("close",{code:2E3,reason:"All transports failed",wasClean:!1,last_event:h})}d.readyState=e.CLOSED;c.delay(function(){d.dispatchEvent(h)})};e.prototype._didMessage=function(a){switch(a.slice(0,
+1)){case "o":this._dispatchOpen();break;case "a":for(var a=JSON.parse(a.slice(1)||"[]"),b=0;b<a.length;b++)this._dispatchMessage(a[b]);break;case "m":a=JSON.parse(a.slice(1)||"null");this._dispatchMessage(a);break;case "c":a=JSON.parse(a.slice(1)||"[]");this._didClose(a[0],a[1]);break;case "h":this._dispatchHeartbeat()}};e.prototype._try_next_protocol=function(a){var b=this;b.protocol&&(b._debug("Closed transport:",b.protocol,""+a),b.protocol=null);for(b._transport_tref&&(clearTimeout(b._transport_tref),
+b._transport_tref=null);;){a=b.protocol=b._protocols.shift();if(!a)return!1;if(e[a]&&!0===e[a].need_body&&(!j.body||"undefined"!=typeof j.readyState&&"complete"!==j.readyState))return b._protocols.unshift(a),b.protocol="waiting-for-load",c.attachEvent("load",function(){b._try_next_protocol()}),!0;if(e[a]&&e[a].enabled(b._options)){b._transport_tref=c.delay((b._options.rto||0)*(e[a].roundTrips||1)||5E3,function(){b.readyState===e.CONNECTING&&b._didClose(2007,"Transport timeouted")});var f=c.random_string(8),
+f=b._base_url+"/"+b._server+"/"+f;return b._debug("Opening transport:",a," url:"+f," RTO:"+b._options.rto),b._transport=new e[a](b,f,b._base_url),!0}b._debug("Skipping transport:",a)}};e.prototype.close=function(a,b){if(a&&!c.userSetCode(a))throw Error("INVALID_ACCESS_ERR");return this.readyState!==e.CONNECTING&&this.readyState!==e.OPEN?!1:(this.readyState=e.CLOSING,this._didClose(a||1E3,b||"Normal closure"),!0)};e.prototype.send=function(a){if(this.readyState===e.CONNECTING)throw Error("INVALID_STATE_ERR");
+return this.readyState===e.OPEN&&this._transport.doSend(c.quote(""+a)),!0};e.prototype._applyInfo=function(a,b,f){this._options.info=a;this._options.rtt=b;this._options.rto=c.countRTO(b);this._options.info.null_origin=!j.domain;b=c.probeProtocols();this._protocols=c.detectProtocols(b,f,a)};i=e.websocket=function(a,b){var f=this,d=b+"/websocket";"https"===d.slice(0,5)?d="wss"+d.slice(5):d="ws"+d.slice(4);f.ri=a;f.url=d;f.ws=new (g.WebSocket||g.MozWebSocket)(f.url);f.ws.onmessage=function(a){f.ri._didMessage(a.data)};
+f.unload_ref=c.unload_add(function(){f.ws.close()});f.ws.onclose=function(){f.ri._didMessage(c.closeFrame(1006,"WebSocket connection broken"))}};i.prototype.doSend=function(a){this.ws.send("["+a+"]")};i.prototype.doCleanup=function(){var a=this.ws;a&&(a.onmessage=a.onclose=null,a.close(),c.unload_del(this.unload_ref),this.unload_ref=this.ri=this.ws=null)};i.enabled=function(){return!!g.WebSocket||!!g.MozWebSocket};i.roundTrips=2;var l=function(){};l.prototype.send_constructor=function(a){this.send_buffer=
+[];this.sender=a};l.prototype.doSend=function(a){this.send_buffer.push(a);this.send_stop||this.send_schedule()};l.prototype.send_schedule_wait=function(){var a=this,b;a.send_stop=function(){a.send_stop=null;clearTimeout(b)};b=c.delay(25,function(){a.send_stop=null;a.send_schedule()})};l.prototype.send_schedule=function(){var a=this;if(0<a.send_buffer.length){var b="["+a.send_buffer.join(",")+"]";a.send_stop=a.sender(a.trans_url,b,function(){a.send_stop=null;a.send_schedule_wait()});a.send_buffer=
+[]}};l.prototype.send_destructor=function(){this._send_stop&&this._send_stop();this._send_stop=null};var O=function(a,b,f){if(!("_send_form"in this)){var d=this._send_form=j.createElement("form"),h=this._send_area=j.createElement("textarea");h.name="d";d.style.display="none";d.style.position="absolute";d.method="POST";d.enctype="application/x-www-form-urlencoded";d.acceptCharset="UTF-8";d.appendChild(h);j.body.appendChild(d)}var d=this._send_form,h=this._send_area,g="a"+c.random_string(8);d.target=
+g;d.action=a+"/jsonp_send?i="+g;var e;try{e=j.createElement('<iframe name="'+g+'">')}catch(i){e=j.createElement("iframe"),e.name=g}e.id=g;d.appendChild(e);e.style.display="none";try{h.value=b}catch(k){c.log("Your browser is seriously broken. Go home! "+k.message)}d.submit();var l=function(){e.onerror&&(e.onreadystatechange=e.onerror=e.onload=null,c.delay(500,function(){e.parentNode.removeChild(e);e=null}),h.value="",f())};return e.onerror=e.onload=l,e.onreadystatechange=function(){"complete"==e.readyState&&
+l()},l},P=function(a){return function(b,c,d){return(new a("POST",b+"/xhr_send",c)).onfinish=function(a){d(a)},function(a){d(0,a)}}},i=e["jsonp-polling"]=function(a,b){c.polluteGlobalNamespace();this.ri=a;this.trans_url=b;this.send_constructor(O);this._schedule_recv()};i.prototype=new l;i.prototype._schedule_recv=function(){var a=this,b=a.trans_url+"/jsonp",f="a"+c.random_string(6),b=b+"?c="+escape("_jp."+f),d=function(b){delete g._jp[f];a._recv_stop=null;b&&(a._is_closing||a.ri._didMessage(b));a._is_closing||
+a._schedule_recv()},e,m=j.createElement("script"),i,k=function(a){i&&(i.parentNode.removeChild(i),i=null);m&&(clearTimeout(e),m.parentNode.removeChild(m),m.onreadystatechange=m.onerror=m.onload=m.onclick=null,m=null,d(a),d=null)},l=!1,p=null;m.id="a"+c.random_string(8);m.src=b;m.type="text/javascript";m.charset="UTF-8";m.onerror=function(){p||(p=setTimeout(function(){l||k(c.closeFrame(1006,"JSONP script loaded abnormally (onerror)"))},1E3))};m.onload=function(){k(c.closeFrame(1006,"JSONP script loaded abnormally (onload)"))};
+m.onreadystatechange=function(){if(/loaded|closed/.test(m.readyState)){if(m&&m.htmlFor&&m.onclick){l=!0;try{m.onclick()}catch(a){}}m&&k(c.closeFrame(1006,"JSONP script loaded abnormally (onreadystatechange)"))}};if("undefined"==typeof m.async&&j.attachEvent)if(/opera/i.test(navigator.userAgent))i=j.createElement("script"),i.text="try{var a = document.getElementById('"+m.id+"'); if(a)a.onerror();}catch(x){};",m.async=i.async=!1;else{try{m.htmlFor=m.id,m.event="onclick"}catch(n){}m.async=!0}"undefined"!=
+typeof m.async&&(m.async=!0);e=setTimeout(function(){k(c.closeFrame(1006,"JSONP script loaded abnormally (timeout)"))},35E3);b=j.getElementsByTagName("head")[0];b=(b.insertBefore(m,b.firstChild),i&&b.insertBefore(i,b.firstChild),k);g._jp[f]=b;a._recv_stop=function(){g._jp[f]&&g._jp[f](c.closeFrame(1E3,"JSONP user aborted read"))}};i.enabled=function(){return!0};i.need_body=!0;i.prototype.doCleanup=function(){this._is_closing=!0;this._recv_stop&&this._recv_stop();this.ri=this._recv_stop=null;this.send_destructor()};
+i=function(){};i.prototype=new l;i.prototype.run=function(a,b,c,d,e){this.ri=a;this.trans_url=b;this.send_constructor(P(e));this.poll=new E(a,d,b+c,e)};i.prototype.doCleanup=function(){this.poll&&(this.poll.abort(),this.poll=null)};var w=e["xhr-streaming"]=function(a,b){this.run(a,b,"/xhr_streaming",y,c.XHRCorsObject)};w.prototype=new i;w.enabled=function(){return g.XMLHttpRequest&&"withCredentials"in new XMLHttpRequest&&!/opera/i.test(navigator.userAgent)};w.roundTrips=2;w.need_body=!0;l=e["xdr-streaming"]=
+function(a,b){this.run(a,b,"/xhr_streaming",y,c.XDRObject)};l.prototype=new i;l.enabled=function(){return!!g.XDomainRequest};l.roundTrips=2;var F=e["xhr-polling"]=function(a,b){this.run(a,b,"/xhr",y,c.XHRCorsObject)};F.prototype=new i;F.enabled=w.enabled;F.roundTrips=2;w=e["xdr-polling"]=function(a,b){this.run(a,b,"/xhr",y,c.XDRObject)};w.prototype=new i;w.enabled=l.enabled;w.roundTrips=2;var s=function(){};s.prototype.i_constructor=function(a,b,f){var d=this;d.ri=a;d.origin=c.getOrigin(f);d.base_url=
+f;d.trans_url=b;a=f+"/iframe.html";d.ri._options.devel&&(a+="?t="+ +new Date);d.window_id=c.random_string(8);a+="#"+d.window_id;d.iframeObj=c.createIframe(a,function(a){d.ri._didClose(1006,"Unable to load an iframe ("+a+")")});d.onmessage_cb=c.bind(d.onmessage,d);c.attachMessage(d.onmessage_cb)};s.prototype.doCleanup=function(){if(this.iframeObj){c.detachMessage(this.onmessage_cb);try{this.iframeObj.iframe.contentWindow&&this.postMessage("c")}catch(a){}this.iframeObj.cleanup();this.onmessage_cb=this.iframeObj=
+this.iframeObj=null}};s.prototype.onmessage=function(a){if(a.origin===this.origin){var b=a.data.slice(0,8),c=a.data.slice(8,9),a=a.data.slice(9);if(b===this.window_id)switch(c){case "s":this.iframeObj.loaded();this.postMessage("s",JSON.stringify([e.version,this.protocol,this.trans_url,this.base_url]));break;case "t":this.ri._didMessage(a)}}};s.prototype.postMessage=function(a,b){this.iframeObj.post(this.window_id+a+(b||""),this.origin)};s.prototype.doSend=function(a){this.postMessage("m",a)};s.enabled=
+function(){var a=navigator&&navigator.userAgent&&-1!==navigator.userAgent.indexOf("Konqueror");return("function"==typeof g.postMessage||"object"==typeof g.postMessage)&&!a};var G,H=function(a,b){parent!==g?parent.postMessage(G+a+(b||""),"*"):c.log("Can't postMessage, no parent window.",a,b)},u=function(){};u.prototype._didClose=function(a,b){H("t",c.closeFrame(a,b))};u.prototype._didMessage=function(a){H("t",a)};u.prototype._doSend=function(a){this._transport.doSend(a)};u.prototype._doCleanup=function(){this._transport.doCleanup()};
+c.parent_origin=void 0;e.bootstrap_iframe=function(){var a;G=j.location.hash.slice(1);c.attachMessage(function(b){if(b.source===parent&&("undefined"==typeof c.parent_origin&&(c.parent_origin=b.origin),b.origin===c.parent_origin)){var f=b.data.slice(0,8),d=b.data.slice(8,9),b=b.data.slice(9);if(f===G)switch(d){case "s":var h=JSON.parse(b),f=h[0],d=h[1],b=h[2],h=h[3];f!==e.version&&c.log('Incompatibile SockJS! Main site uses: "'+f+'", the iframe: "'+e.version+'".');if(!c.flatUrl(b)||!c.flatUrl(h)){c.log("Only basic urls are supported in SockJS");
+break}if(!c.isSameOriginUrl(b)||!c.isSameOriginUrl(h)){c.log("Can't connect to different domain from within an iframe. ("+JSON.stringify([g.location.href,b,h])+")");break}a=new u;a._transport=new u[d](a,b,h);break;case "m":a._doSend(b);break;case "c":a&&a._doCleanup(),a=null}}});H("s")};var z=function(a,b){var f=this;c.delay(function(){f.doXhr(a,b)})};z.prototype=new k(["finish"]);z.prototype.doXhr=function(a,b){var f=this,d=(new Date).getTime(),e=new b("GET",a+"/info"),g=c.delay(8E3,function(){e.ontimeout()});
+e.onfinish=function(a,b){clearTimeout(g);g=null;if(200===a){var c=(new Date).getTime()-d,e=JSON.parse(b);"object"!=typeof e&&(e={});f.emit("finish",e,c)}else f.emit("finish")};e.ontimeout=function(){e.close();f.emit("finish")}};var K=function(a){var b=this,f=function(){var c=new s;c.protocol="w-iframe-info-receiver";var f=function(a){"string"==typeof a&&"m"===a.substr(0,1)?(a=JSON.parse(a.substr(1)),b.emit("finish",a[0],a[1])):b.emit("finish");c.doCleanup();c=null};c.i_constructor({_options:{},_didClose:f,
+_didMessage:f},a,a)};j.body?f():c.attachEvent("load",f)};K.prototype=new k(["finish"]);var L=function(){var a=this;c.delay(function(){a.emit("finish",{},2E3)})};L.prototype=new k(["finish"]);var N=function(a){if(c.isSameOriginUrl(a))return new z(a,c.XHRLocalObject);switch(c.isXHRCorsCapable()){case 1:return new z(a,c.XHRLocalObject);case 2:return new z(a,c.XDRObject);case 3:return new K(a);default:return new L}};(u["w-iframe-info-receiver"]=function(a,b,f){(new z(f,c.XHRLocalObject)).onfinish=function(b,
+c){a._didMessage("m"+JSON.stringify([b,c]));a._didClose()}}).prototype.doCleanup=function(){};k=e["iframe-eventsource"]=function(){this.protocol="w-iframe-eventsource";this.i_constructor.apply(this,arguments)};k.prototype=new s;k.enabled=function(){return"EventSource"in g&&s.enabled()};k.need_body=!0;k.roundTrips=3;(u["w-iframe-eventsource"]=function(a,b){this.run(a,b,"/eventsource",I,c.XHRLocalObject)}).prototype=new i;k=e["iframe-xhr-polling"]=function(){this.protocol="w-iframe-xhr-polling";this.i_constructor.apply(this,
+arguments)};k.prototype=new s;k.enabled=function(){return g.XMLHttpRequest&&s.enabled()};k.need_body=!0;k.roundTrips=3;(u["w-iframe-xhr-polling"]=function(a,b){this.run(a,b,"/xhr",y,c.XHRLocalObject)}).prototype=new i;k=e["iframe-htmlfile"]=function(){this.protocol="w-iframe-htmlfile";this.i_constructor.apply(this,arguments)};k.prototype=new s;k.enabled=function(){return s.enabled()};k.need_body=!0;k.roundTrips=3;(u["w-iframe-htmlfile"]=function(a,b){this.run(a,b,"/htmlfile",J,c.XHRLocalObject)}).prototype=
+new i;var E=function(a,b,c,d){this.ri=a;this.Receiver=b;this.recv_url=c;this.AjaxObject=d;this._scheduleRecv()};E.prototype._scheduleRecv=function(){var a=this,b=a.poll=new a.Receiver(a.recv_url,a.AjaxObject);b.onmessage=function(b){a.ri._didMessage(b.data)};b.onclose=function(c){a.poll=b=b.onmessage=b.onclose=null;a.poll_is_closing||("permanent"===c.reason?a.ri._didClose(1006,"Polling error ("+c.reason+")"):a._scheduleRecv())}};E.prototype.abort=function(){this.poll_is_closing=!0;this.poll&&this.poll.abort()};
+var I=function(a){var b=this,f=new EventSource(a);f.onmessage=function(a){b.dispatchEvent(new n("message",{data:unescape(a.data)}))};b.es_close=f.onerror=function(a,e){var g=e?"user":2!==f.readyState?"network":"permanent";b.es_close=f.onmessage=f.onerror=null;f.close();f=null;c.delay(200,function(){b.dispatchEvent(new n("close",{reason:g}))})}};I.prototype=new p;I.prototype.abort=function(){this.es_close&&this.es_close({},!0)};var A,J=function(a){var b=this;c.polluteGlobalNamespace();b.id="a"+c.random_string(6,
+26);a+=(-1===a.indexOf("?")?"?":"&")+"c="+escape("_jp."+b.id);if(void 0===A)if("ActiveXObject"in g)try{A=!!new ActiveXObject("htmlfile")}catch(f){}else A=!1;var d=A?c.createHtmlfile:c.createIframe,e;g._jp[b.id]={start:function(){e.loaded()},message:function(a){b.dispatchEvent(new n("message",{data:a}))},stop:function(){b.iframe_close({},"network")}};b.iframe_close=function(a,c){e.cleanup();b.iframe_close=e=null;delete g._jp[b.id];b.dispatchEvent(new n("close",{reason:c}))};e=d(a,function(){b.iframe_close({},
+"permanent")})};J.prototype=new p;J.prototype.abort=function(){this.iframe_close&&this.iframe_close({},"user")};var y=function(a,b){var c=this,d=0;c.xo=new b("POST",a,null);c.xo.onchunk=function(a,b){if(200===a)for(;;){var e=b.slice(d),g=e.indexOf("\n");if(-1===g)break;d+=g+1;e=e.slice(0,g);c.dispatchEvent(new n("message",{data:e}))}};c.xo.onfinish=function(a,b){c.xo.onchunk(a,b);c.xo=null;c.dispatchEvent(new n("close",{reason:200===a?"network":"permanent"}))}};return y.prototype=new p,y.prototype.abort=
+function(){this.xo&&(this.xo.close(),this.dispatchEvent(new n("close",{reason:"user"})),this.xo=null)},e.getUtils=function(){return c},e.getIframeTransport=function(){return s},e}();"_sockjs_onload"in window&&setTimeout(_sockjs_onload,1);"function"==typeof define&&define.amd&&define("sockjs",[],function(){return SockJS});function WebTCP(j,g){this.sockets={};this.httpClients={};this.sock=new SockJS("http://"+j+":"+g+"/bridge");this.ready=!1;this.outputBuffer=[];var c=this;this.sock.onopen=function(){c.ready=!0;c.processBuffer()};this.processBuffer=function(){if(0<this.outputBuffer.length&&this.ready)for(var c in this.outputBuffer)this.sock.send(this.outputBuffer[c])};this.sock.onmessage=function(g){g.data=JSON.parse(g.data);g.data.sID?(sID=g.data.sID,c.sockets[sID].onEvent(g.data.eventName,g.data.data)):g.data.cID&&
+(cID=g.data.cID,c.httpClients[cID].onEvent(g.data.eventName,g.data.data))};this.sock.onclose=function(){console.log("close")};this.createSocket=function(c,g,j){return new Socket(this,c,g,j)};this.createHTTPClient=function(){return new Http(this)}};function WebTCPIO(){this.onEvent=function(j,g){this["on"+j](g);if(this["on"+j+"Custom"])this["on"+j+"Custom"](g)};this.on=function(j,g){this["on"+j+"Custom"]=g};this.rpc=function(j,g){var c=this.createPacket();c.command=j;c.args=g;this.send(JSON.stringify(c));return c.id};this.write=function(j){var g=this.createPacket();g.data=j;this.send(JSON.stringify(g));return g.id};this.send=function(j){if(this.webtcp.ready)this.webtcp.sock.send(j);else{if(this.webtcp.outputBuffer>this.webtcp.BUFF_SIZE)throw"Output buffer is already full, but sockJS connection is not ready yet";
+this.webtcp.outputBuffer.push(j)}};this.onerror=function(j){throw j;}}; \ No newline at end of file
diff --git a/src/main/java/com/seaofnodes/simple/ASMPrinter.java b/src/main/java/com/seaofnodes/simple/ASMPrinter.java
new file mode 100644
index 00000000..3c4162ea
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/ASMPrinter.java
@@ -0,0 +1,51 @@
+package com.seaofnodes.simple;
+
+import com.seaofnodes.print.AssemblyAdapter;
+import com.seaofnodes.print.ASMPrinter.Data;
+import com.seaofnodes.print.ASMPrinter.Pool;
+import com.seaofnodes.simple.CodeGen;
+import com.seaofnodes.simple.SB;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.type.*;
+import java.util.ArrayList;
+
+/** Machine-specific facts; traversal, columns and byte display live in print/. */
+public final class ASMPrinter extends AssemblyAdapter<Node> {
+ private final CodeGen _code;
+ private ASMPrinter(CodeGen code) {
+ super(new IRPrinter());
+ _code=code;
+ }
+ public static SB print(SB sb, CodeGen code) {
+ return sb.p(new com.seaofnodes.print.ASMPrinter<>(new ASMPrinter(code)).print());
+ }
+ @Override public ArrayList<Node> blocks() {
+ var blocks=new ArrayList<Node>();
+ var cfg=_code._cfg;
+ if( cfg!=null ) for( Node n : cfg ) blocks.add(n);
+ return blocks;
+ }
+ @Override public String op(Node n) { return n instanceof MachNode m ? m.op() : n.label(); }
+ @Override public String register(Node n) { return _code.reg(n); }
+ @Override public String comment(Node n) { return n.comment(); }
+ @Override public String phiName(Node n) { return ((PhiNode)n)._label; }
+ @Override public String operands(Node n) {
+ SB sb=new SB();
+ if( n instanceof MachNode m ) m.asm(_code,sb);
+ else if( !(n._type instanceof TypeMem) ) {
+ sb.p(n._nid).p(": ");
+ for( Node def : n._inputs ) sb.p(def==null ? "___" : Integer.toString(def._nid)).p(' ');
+ }
+ return sb.toString();
+ }
+ @Override public boolean labelBlock(Node n) {
+ return !(n instanceof FunNode) && !(n instanceof IfNode) && !(n instanceof CallNode) &&
+ !(n instanceof CallEndNode) && !(n instanceof CProjNode && ir.input0(n) instanceof CallEndNode);
+ }
+ @Override public boolean hidden(Node n) {
+ if( n instanceof PhiNode || n instanceof ProjNode )
+ if( n._type instanceof TypeMem || n._type instanceof TypeRPC ) return true;
+ if( n instanceof MemMergeNode ) return true;
+ return n.getClass()==ConstantNode.class;
+ }
+}
diff --git a/src/main/java/com/seaofnodes/simple/Ary.java b/src/main/java/com/seaofnodes/simple/Ary.java
index b5184948..73ada8e0 100644
--- a/src/main/java/com/seaofnodes/simple/Ary.java
+++ b/src/main/java/com/seaofnodes/simple/Ary.java
@@ -42,8 +42,7 @@ public class Ary<E> extends AbstractList<E> implements List<E> {
}
/** @return remove and return last element */
public E pop( ) {
- range_check(0);
- return _es[--_len];
+ return _len==0 ? null : _es[--_len];
}
@Override public E removeLast() { return pop(); }
@@ -93,6 +92,22 @@ public class Ary<E> extends AbstractList<E> implements List<E> {
return old;
}
+ /** Set existing element
+ * @param i element to set
+ * @param e value to set
+ * @return old value
+ * @exception AIOOBE if !(0 <= i < _len)
+ */
+ public E setX( int i, E e ) {
+ if( i >= _len ) {
+ while( i >= _es.length ) _es = Arrays.copyOf( _es, _es.length << 1 );
+ _len = i+1;
+ }
+ E old = _es[i];
+ _es[i] = e;
+ return old;
+ }
+
public Ary<E> setLen( int len ) {
if( len > _len )
while( len>= _es.length ) _es = Arrays.copyOf(_es,_es.length<<1);
@@ -103,13 +118,14 @@ public class Ary<E> extends AbstractList<E> implements List<E> {
return this;
}
- public void swap( int i, int j ) {
+ public E swap( int i, int j ) {
range_check(i);
range_check(j);
- if( i==j ) return;
E tmp = _es[i];
+ if( i==j ) return tmp;
_es[i] = _es[j];
_es[j] = tmp;
+ return tmp;
}
@@ -139,12 +155,12 @@ public class Ary<E> extends AbstractList<E> implements List<E> {
return this;
}
- /** @return compact array version */
- public E[] asAry() { return Arrays.copyOf(_es,_len); }
+ /** @return compact array version */
+ public E[] asAry() { return Arrays.copyOf(_es,_len); }
- /** Sorts in-place
- * @param c Comparator to sort by */
- public void sort_update(Comparator<? super E> c ) { Arrays.sort(_es, 0, _len, c); }
+ /** Sorts in-place
+ * @param c Comparator to sort by */
+ public void sort_update(Comparator<? super E> c ) { Arrays.sort(_es, 0, _len, c); }
/** Find the first matching element using ==, or -1 if none. Note that
* most del calls shuffle the list, so the first element might be random.
diff --git a/src/main/java/com/seaofnodes/simple/CodeGen.java b/src/main/java/com/seaofnodes/simple/CodeGen.java
index 864b3a1a..0a3a60e8 100644
--- a/src/main/java/com/seaofnodes/simple/CodeGen.java
+++ b/src/main/java/com/seaofnodes/simple/CodeGen.java
@@ -3,8 +3,11 @@
import com.seaofnodes.graph.GraphObserver;
import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.node.cpus.x86_64_v2.MemOpX86;
import com.seaofnodes.simple.type.*;
+import java.util.BitSet;
import java.util.HashMap;
+import java.util.IdentityHashMap;
public class CodeGen {
public GraphObserver<Node> _obs;
@@ -18,66 +21,154 @@ public class CodeGen {
}
+ // Last created CodeGen as a global; used all over to avoid passing about a
+ // "context".
+ public static CodeGen CODE;
public enum Phase {
- Parse, Opto, TypeCheck, Schedule, LocalSched, RegAlloc;
+ Parse, // Parse ASCII text into Sea-of-Nodes IR
+ Opto, // Run ideal optimizations
+ TypeCheck, // Last check for bad programs
+ InstructionSelection, // Convert to target hardware nodes
+ Schedule, // Global schedule (code motion) nodes
+ LocalSched, // Local schedule
+ RegAlloc; // Register allocation
+ }
+ public enum CallingConv {
+ Win64,
+ SystemV
}
public Phase _phase;
+ // ---------------------------
+ // Compilation source code
public final String _src;
// Compile-time known initial argument type
public final TypeInteger _arg;
+ public CallingConv _callingConv;
+
+ // ---------------------------
+ public CodeGen( String src ) { this(src, TypeInteger.BOT, 123L ); }
+ public CodeGen( String src, TypeInteger arg, long workListSeed ) {
+ CODE = this;
+ _phase = null;
+ _callingConv = null;
+ _start = new StartNode(arg);
+ _stop = new StopNode(src);
+ _src = src;
+ _arg = arg;
+ _iter = new IterPeeps(workListSeed);
+ P = new Parser(this,arg);
+ }
+
+
+ // ---------------------------
+ /**
+ * A counter, for unique node id generation. Starting with value 1, to
+ * avoid bugs confusing node ID 0 with uninitialized values.
+ */
+ private int _uid = 1;
+ public int UID() { return _uid; }
+ public int getUID() { return _uid++; }
+
+ // Next available memory alias number
+ private int _alias = 2; // 0 is for control, 1 for memory
+ public int getALIAS() { return _alias++; }
- public Parser P;
+
+ // Popular visit bitset, declared here, so it gets reused all over
+ public final BitSet _visit = new BitSet();
+ public BitSet visit() { assert _visit.isEmpty(); return _visit; }
+
+ // Start and stop; end points of the generated IR
public StartNode _start;
- public StopNode _stop ;
+ public StopNode _stop;
+
+ // Global Value Numbering. Hash over opcode and inputs; hits in this table
+ // are structurally equal.
+ public final HashMap<Node,Node> _gvn = new HashMap<>();
+
+ // Compute "function indices": FIDX.
+ // Each new request at the same signature gets a new FIDX.
+ private final HashMap<TypeTuple,Integer> FIDXS = new HashMap<>();
+ public TypeFunPtr makeFun( TypeTuple sig, Type ret ) {
+ Integer i = FIDXS.get(sig);
+ int fidx = i==null ? 0 : i;
+ FIDXS.put(sig,fidx+1); // Track count per sig
+ assert fidx<64; // TODO: need a larger FIDX space
+ return TypeFunPtr.make((byte)2,sig,ret, 1L<<fidx );
+ }
+ // Signature for MAIN
+ public TypeFunPtr _main = makeFun(TypeTuple.MAIN,Type.BOTTOM);
+ // Reverse from a constant function pointer to the IR function being called
+ public FunNode link( TypeFunPtr tfp ) {
+ assert tfp.isConstant();
+ return _linker.get(tfp.makeFrom(Type.BOTTOM));
+ }
+ // Insert linker mapping from constant function signature to the function
+ // being called.
+ public void link(FunNode fun) {
+ _linker.put(fun.sig().makeFrom(Type.BOTTOM),fun);
+ }
// "Linker" mapping from constant TypeFunPtrs to heads of function. These
// TFPs all have exact single fidxs and their return is wiped to BOTTOM (so
// the return is not part of the match).
private final HashMap<TypeFunPtr,FunNode> _linker = new HashMap<>();
+ // Parser object
+ public final Parser P;
- // Last created CodeGen as a global, for easier debugging prints
- public static CodeGen CODE;
-
- CodeGen( String src, TypeInteger arg ) { _phase = null; _src = src; _arg=arg; CODE=this; }
- public CodeGen( String src ) { this(src,TypeInteger.BOT); }
-
+ // Parse ASCII text into Sea-of-Nodes IR
public CodeGen parse() {
assert _phase == null;
_phase = Phase.Parse;
- _linker.clear();
- P = new Parser(_src,_arg);
- _stop = P.parse();
- _start = Parser.START; // Capture global start
+ P.parse();
if( _obs != null ) _obs.phase("Parse");
- P = null; // No longer parsing
return this;
}
+
+ // ---------------------------
+ // Iterator peepholes.
+ public final IterPeeps _iter;
+ // Stop tracking deps while assertin
+ public boolean _midAssert;
+ // Statistics on peepholes
+ public int _iter_cnt, _iter_nop_cnt;
+ // Stat gathering
+ public void iterCnt() { if( !_midAssert ) _iter_cnt++; }
+ public void iterNop() { if( !_midAssert ) _iter_nop_cnt++; }
+
+ // Run ideal optimizations
public CodeGen opto() {
assert _phase == Phase.Parse;
_phase = Phase.Opto;
- // Optimization
- IterPeeps.iterate(_stop);
+ // Pessimistic peephole optimization on a worklist
+ _iter.iterate(this);
// TODO:
// Optimistic
+ // TODO:
+ // loop unroll, peel, RCE, etc
if( _obs != null ) _obs.phase("Opto");
return this;
}
+ public <N extends Node> N add( N n ) { return (N)_iter.add(n); }
+ public void addAll( Ary<Node> ary ) { _iter.addAll(ary); }
+
+ // ---------------------------
+ // Last check for bad programs
public CodeGen typeCheck() {
// Demand phase Opto for cleaning up dead control flow at least,
// required for the following GCM.
- assert _phase == Phase.Opto;
+ assert _phase.ordinal() <= Phase.Opto.ordinal();
_phase = Phase.TypeCheck;
- // Type check
Parser.ParseException err = _stop.walk(n -> {
Parser.ParseException msg = n.err();
if( msg != null && _obs != null ) _obs.error(n, msg.getMessage());
@@ -90,20 +181,96 @@ public class CodeGen {
}
+ // ---------------------------
+ // Code generation CPU target
+ public Machine _mach;
+
+ // Convert to target hardware nodes
+ public CodeGen instSelect( String cpu, String callingConv ) {
+ assert _phase.ordinal() <= Phase.TypeCheck.ordinal();
+ _phase = Phase.InstructionSelection;
+
+ _callingConv = switch(callingConv) {
+ case "SystemV" -> CallingConv.SystemV;
+ case "Win64" -> CallingConv.Win64;
+ default -> throw Utils.TODO(); // Unknown calling convention
+ };
+
+ // Look for CPU in fixed named place:
+ // com.seaofnodes.simple.node.cpus."cpu"."cpu.class"
+ String clzFile = "com.seaofnodes.simple.node.cpus."+cpu+"."+cpu;
+ try { _mach = ((Class<Machine>) Class.forName( clzFile )).getDeclaredConstructor().newInstance(); }
+ catch( Exception e ) { throw new RuntimeException(e); }
+
+ // Convert to machine ops
+ _uid = 1; // All new machine nodes reset numbering
+ var map = new IdentityHashMap<Node,Node>();
+ _instSelect( _stop, map );
+ _stop = ( StopNode)map.get(_stop );
+ _start = (StartNode)map.get(_start);
+ _instOuts(_stop,visit());
+ _visit.clear();
+ return this;
+ }
+
+ // Walk all ideal nodes, recursively mapping ideal to machine nodes, then
+ // make a machine node for "this".
+ private Node _instSelect( Node n, IdentityHashMap<Node,Node> map ) {
+ if( n==null ) return null;
+ Node x = map.get(n);
+ if( x !=null ) return x; // Been there, done that
+
+ // If n is a MachConcrete, then its part of a multi-node expansion.
+ // It does not need instruction selection (already selected!)
+ // but it does need its inputs walked.
+ if( n instanceof MachConcreteNode || n instanceof MemOpX86) {
+ for( int i=0; i < n.nIns(); i++ )
+ n._inputs.set(i, _instSelect(n.in(i),map) );
+ return n;
+ }
+
+ // Produce a machine node from n; map it to flag as done so stops cycles.
+ map.put(n, x=_mach.instSelect(n) );
+ // Walk machine op and replace inputs with mapped inputs
+ for( int i=0; i < x.nIns(); i++ )
+ x._inputs.set(i, _instSelect(x.in(i),map) );
+ if( x instanceof MachNode mach )
+ mach.postSelect(); // Post selection action
+
+ // Updates forward edges only.
+ n._outputs.clear();
+ return x;
+ }
+
+ // Walk all machine Nodes, and set their output edges
+ private void _instOuts( Node n, BitSet visit ) {
+ if( visit.get(n._nid) ) return;
+ visit.set(n._nid);
+ for( Node in : n._inputs )
+ if( in!=null ) {
+ in._outputs.push(n);
+ _instOuts(in,visit);
+ }
+ }
+
+
+ // ---------------------------
+ // Control Flow Graph in RPO order.
+ Ary<CFGNode> _cfg = new Ary<>(CFGNode.class);
+
+ // Global schedule (code motion) nodes
public CodeGen GCM() {
- assert _phase == Phase.TypeCheck;
+ assert _phase.ordinal() <= Phase.InstructionSelection.ordinal();
_phase = Phase.Schedule;
// Build the loop tree, fix never-exit loops
_start.buildLoopTree(_stop);
+ GlobalCodeMotion.buildCFG(this);
-
- // TODO:
- // loop unroll, peel, RCE, etc
- GlobalCodeMotion.buildCFG(_start,_stop);
return this;
}
+ // ---------------------------
// Local (basic block) scheduler phase, a classic list scheduler
public CodeGen localSched() {
assert _phase == Phase.Schedule;
@@ -112,25 +279,26 @@ public class CodeGen {
return this;
}
- // Reverse from a constant function pointer to the IR function being called
- public FunNode link( TypeFunPtr tfp ) {
- assert tfp.isConstant();
- return _linker.get(tfp.makeFrom(Type.BOTTOM));
- }
- public void link(FunNode fun) {
- _linker.put(fun.sig().makeFrom(Type.BOTTOM),fun);
+ // ---------------------------
+ public String reg(Node n) {
+ if( _phase == Phase.RegAlloc ) throw Utils.TODO();
+ return "N"+ n._nid;
}
+
+ // ---------------------------
+ SB asm(SB sb) { return ASMPrinter.print(sb,this); }
+ String asm() { return asm(new SB()).toString(); }
+
+
// Testing shortcuts
Node ctrl() { return _stop.ret().ctrl(); }
Node expr() { return _stop.ret().expr(); }
String print() { return _stop.print(); }
// Debugging helper
- @Override public String toString() {
- return IRPrinter.prettyPrint(this);
- }
+ @Override public String toString() { return IRPrinter.prettyPrint(this); }
// Debugging helper
public Node f(int idx) { return _stop.find(idx); }
diff --git a/src/main/java/com/seaofnodes/simple/GlobalCodeMotion.java b/src/main/java/com/seaofnodes/simple/GlobalCodeMotion.java
index 8221ede1..95ada83d 100644
--- a/src/main/java/com/seaofnodes/simple/GlobalCodeMotion.java
+++ b/src/main/java/com/seaofnodes/simple/GlobalCodeMotion.java
@@ -10,12 +10,36 @@ public abstract class GlobalCodeMotion {
// Arrange that the existing isCFG() Nodes form a valid CFG. The
// Node.use(0) is always a block tail (either IfNode or head of the
// following block). There are no unreachable infinite loops.
- public static void buildCFG( StartNode start, StopNode stop ) {
- schedEarly(start);
- breakUpGlobalConstants(start);
- schedLate(stop);
+ public static void buildCFG( CodeGen code ) {
+ Ary<CFGNode> rpo = new Ary<>(CFGNode.class);
+ _rpo_cfg(null, code._start, code.visit(), rpo);
+ // Reverse in-place
+ for( int i=0; i< rpo.size()>>1; i++ ) {
+ int j = rpo.size()-1-i;
+ CFGNode tmp = rpo.get(i);
+ rpo.set(i, rpo.get(j));
+ rpo.set(j, tmp);
+ }
+ // Set global CFG
+ code._cfg = rpo;
+
+ schedEarly(code);
+ breakUpGlobalConstants(code._start);
+ code._visit.clear();
+ schedLate (code);
}
+ // Post-Order of CFG
+ private static void _rpo_cfg(CFGNode def, Node use, BitSet visit, Ary<CFGNode> rpo) {
+ if( !(use instanceof CFGNode cfg) || visit.get(cfg._nid) )
+ return; // Been there, done that
+ if( def instanceof CallNode && cfg instanceof FunNode )
+ return; // Ignore linked function calls
+ visit.set(cfg._nid);
+ for( Node useuse : cfg._outputs )
+ _rpo_cfg(cfg,useuse,visit,rpo);
+ rpo.add(cfg);
+ }
// After early scheduling, every global chain member has Start in slot 0.
// Give each function one private copy of the entire dependency graph.
@@ -24,7 +48,7 @@ public abstract class GlobalCodeMotion {
var cons = new ArrayList<Node>();
for( Node con : start._outputs )
if( con!=null && !(con instanceof CFGNode) &&
- con.isConst() ) {
+ (con.isConst() || (con instanceof MachNode mach && mach.outregmap()!=null)) ) {
globals.put(con,true);
cons.add(con.keep()); // Preserve original inputs while rewiring users.
}
@@ -72,38 +96,22 @@ public abstract class GlobalCodeMotion {
// (except at loops). Since defs are visited first - and hoisted as early
// as possible, when we come to a use we place it just after its deepest
// input.
- private static void schedEarly(StartNode start) {
- ArrayList<CFGNode> rpo = new ArrayList<>();
- BitSet visit = new BitSet();
- _rpo_cfg(null, start, visit, rpo);
+ private static void schedEarly(CodeGen code) {
// Reverse Post-Order on CFG
- for( int j=rpo.size()-1; j>=0; j-- ) {
- CFGNode cfg = rpo.get(j);
+ for( CFGNode cfg : code._cfg ) {
cfg.loopDepth();
for( Node n : cfg._inputs )
- _schedEarly(n,visit);
+ _schedEarly(n,code._visit );
// In dead infinite loops, entire code blocks may be unreachable
// from below. Reach down from the CFG to their Phis so their
// inputs are scheduled too.
if( cfg instanceof RegionNode )
for( Node phi : cfg._outputs )
if( phi instanceof PhiNode )
- _schedEarly(phi,visit);
+ _schedEarly(phi,code._visit );
}
}
- // Post-Order of CFG
- private static void _rpo_cfg(CFGNode def, Node use, BitSet visit, ArrayList<CFGNode> rpo) {
- if( !(use instanceof CFGNode cfg) || visit.get(cfg._nid) )
- return; // Been there, done that
- if( def instanceof CallNode && cfg instanceof FunNode )
- return; // Ignore linked function calls
- visit.set(cfg._nid);
- for( Node useuse : cfg._outputs )
- _rpo_cfg(cfg,useuse,visit,rpo);
- rpo.add(cfg);
- }
-
private static void _schedEarly(Node n, BitSet visit) {
if( n==null || visit.get(n._nid) ) return; // Been there, done that
assert !(n instanceof CFGNode);
@@ -113,26 +121,27 @@ public abstract class GlobalCodeMotion {
for( Node def : n._inputs )
if( def!=null && !(def instanceof PhiNode) )
_schedEarly(def,visit);
+
// An existing edge 0 already supplies control (or a Phi/Proj binding).
if( n.in(0)==null ) {
// Schedule at deepest input
- CFGNode early = Parser.START; // Maximally early, lowest idepth
+ CFGNode early = CodeGen.CODE._start; // Maximally early, lowest idepth
for( int i=1; i<n.nIns(); i++ )
if( n.in(i)!=null && n.in(i).cfg0().idepth() > early.idepth() )
early = n.in(i).cfg0(); // Latest/deepest input
- n.setDef(0,early); // First place this can go
+ n.setDef(0,early);
}
}
// ------------------------------------------------------------------------
- private static void schedLate( StopNode stop) {
- CFGNode[] late = new CFGNode[Node.UID()];
- Node[] ns = new Node[Node.UID()];
+ private static void schedLate( CodeGen code ) {
+ CFGNode[] late = new CFGNode[code.UID()];
+ Node[] ns = new Node[code.UID()];
// Record Load NIDs at all their CFG block choices, then check against
// Store block choices to force a Load above an anti-dependent Store.
- int[] anti = new int[Node.UID()];
+ int[] anti = new int[code.UID()];
// Breadth-first scheduling
- breadth(stop,ns,late,anti);
+ breadth(code._stop,ns,late,anti);
// Copy the best placement choice into the control slot
for( int i=0; i<late.length; i++ )
@@ -160,7 +169,7 @@ public abstract class GlobalCodeMotion {
continue outer; // Nope, await all uses done
// Loads need their memory inputs' uses also done
- if( n instanceof LoadNode ld )
+ if( n instanceof MemOpNode ld && !ld.isMem() )
for( Node memuse : ld.antiDeps() )
if( late[memuse._nid]==null )
continue outer;
@@ -193,7 +202,7 @@ public abstract class GlobalCodeMotion {
lca = use_block(n,use, late).domLCA(lca,null);
// Loads may need anti-dependencies, raising their LCA
- if( n instanceof LoadNode load )
+ if( n instanceof MemOpNode load && !load.isMem() )
lca = find_anti_dep(lca,load,early,late,anti);
// Walk up from the LCA to the early, looking for best place. This is
@@ -237,13 +246,13 @@ public abstract class GlobalCodeMotion {
if( visit.get(def._nid) ) return;
visit.set(def._nid);
for( Node out : def._outputs )
- if( out instanceof LoadNode ld && late[ld._nid]==null ) work.push(ld);
+ if( out instanceof MemOpNode ld && !ld.isMem() && late[ld._nid]==null ) work.push(ld);
if( def instanceof MemMergeNode )
for( int i=1; i<def.nIns(); i++ )
if( def.in(i)!=null ) wakeLoads(def.in(i),late,work,visit);
}
- private static CFGNode find_anti_dep(CFGNode lca, LoadNode load, CFGNode early, CFGNode[] late, int[] anti) {
+ private static CFGNode find_anti_dep(CFGNode lca, MemOpNode load, CFGNode early, CFGNode[] late, int[] anti) {
// We could skip final-field loads here.
// Walk LCA->early, flagging Load's block location choices
for( CFGNode cfg=lca; early!=null && cfg!=early.idom(); cfg = cfg.idom() )
@@ -251,7 +260,7 @@ public abstract class GlobalCodeMotion {
// Walk load->mem uses, looking for Stores causing an anti-dep
for( Node mem : load.antiDeps() ) {
switch( mem ) {
- case StoreNode st:
+ case MemOpNode st:
assert late[st._nid]!=null;
lca = anti_dep(load,late[st._nid],st.cfg0(),lca,st,anti);
break;
@@ -277,7 +286,7 @@ public abstract class GlobalCodeMotion {
}
//
- private static CFGNode anti_dep( LoadNode load, CFGNode stblk, CFGNode defblk, CFGNode lca, Node st, int[] anti ) {
+ private static CFGNode anti_dep( MemOpNode load, CFGNode stblk, CFGNode defblk, CFGNode lca, Node st, int[] anti ) {
for( ; stblk != defblk.idom(); stblk = stblk.idom() ) {
// Store and Load overlap, need anti-dependence
if( anti[stblk._nid]==load._nid ) {
diff --git a/src/main/java/com/seaofnodes/simple/IRPrinter.java b/src/main/java/com/seaofnodes/simple/IRPrinter.java
index c7ca8cf4..f0ac717a 100644
--- a/src/main/java/com/seaofnodes/simple/IRPrinter.java
+++ b/src/main/java/com/seaofnodes/simple/IRPrinter.java
@@ -35,10 +35,10 @@ public final class IRPrinter extends IRAdapter<Node> {
if( n instanceof CProjNode p ) return p._idx;
return n._nid;
}
- @Override public String functionName(Node n) { return n.label(); }
+ @Override public String functionName(Node n) { return ((FunNode)n)._name==null ? "" : ((FunNode)n)._name; }
@Override public String signature(Node n) {
FunNode fun=(FunNode)n;
- return fun.sig().str();
+ return (fun._name==null ? "" : fun._name)+" "+fun.sig().str();
}
public static String prettyPrint(Node n, int depth) { return PRINT.prettyPrint(n,depth); }
diff --git a/src/main/java/com/seaofnodes/simple/IterPeeps.java b/src/main/java/com/seaofnodes/simple/IterPeeps.java
index 1792dada..087a990b 100644
--- a/src/main/java/com/seaofnodes/simple/IterPeeps.java
+++ b/src/main/java/com/seaofnodes/simple/IterPeeps.java
@@ -42,30 +42,28 @@ import java.util.Random;
* progressOnList(stop);`</li>
* </ul>
*/
-public abstract class IterPeeps {
+public class IterPeeps {
- private static final WorkList<Node> WORK = new WorkList<>();
+ private final WorkList<Node> _work;
- public static <N extends Node> N add( N n ) {
- return (N)WORK.push(n);
- }
+ IterPeeps( long seed ) { _work = new WorkList<>(seed); }
- public static void addAll( Ary<Node> ary ) {
- WORK.addAll(ary);
- }
+ public <N extends Node> N add( N n ) { return (N)_work.push(n); }
+
+ public void addAll( Ary<Node> ary ) { _work.addAll(ary); }
/**
* Iterate peepholes to a fixed point
*/
- public static void iterate( StopNode stop) {
- assert progressOnList(stop);
+ public void iterate( CodeGen code ) {
+ assert progressOnList(code);
int cnt=0;
Node n;
- while( (n=WORK.pop()) != null ) {
+ while( (n=_work.pop()) != null ) {
if( n.isDead() ) continue;
cnt++; // Useful for debugging, searching which peephole broke things
- var obs = CodeGen.CODE == null ? null : CodeGen.CODE._obs;
+ var obs = CodeGen.CODE._obs;
if( obs != null ) obs.before(n);
Node x = n.peepholeOpt();
if( x != null ) {
@@ -78,20 +76,20 @@ public abstract class IterPeeps {
// Changes require neighbors onto the worklist
if( x != n || !(x instanceof ConstantNode) ) {
// All outputs of n (changing node) not x (prior existing node).
- for( Node z : n._outputs ) WORK.push(z);
+ for( Node z : n._outputs ) _work.push(z);
// Everybody gets a free "go again" in case they didn't get
// made in their final form.
- WORK.push(x);
+ _work.push(x);
// If the result is not self, revisit all inputs (because
// there's a new user), and replace in the graph.
if( x != n ) {
- for( Node z : n. _inputs ) WORK.push(z);
+ for( Node z : n. _inputs ) _work.push(z);
n.subsume(x);
}
}
// If there are distant neighbors, move to worklist
n.moveDepsToWorklist();
- assert progressOnList(stop); // Very expensive assert
+ assert progressOnList(code); // Very expensive assert
}
if( n.isUnused() && !(n instanceof StopNode) )
n.kill(); // Just plain dead
@@ -101,7 +99,7 @@ public abstract class IterPeeps {
}
// Visit ALL nodes and confirm the invariant:
- // Either you are on the WORK worklist OR running `iter()` makes no progress.
+ // Either you are on the _work worklist OR running `iter()` makes no progress.
// This invariant ensures that no progress is missed, i.e., when the
// worklist is empty we have indeed done all that can be done. To help
@@ -112,30 +110,22 @@ public abstract class IterPeeps {
// neighbors and these should fail, but will then try to add dependencies
// {@link #Node.addDep} which is a side effect in an assert. The {@link
// #midAssert} is used to stop this side effect.
- private static boolean MID_ASSERT;
- public static boolean midAssert() { return MID_ASSERT; }
- private static boolean progressOnList(Node stop) {
- MID_ASSERT = true;
- int old_cnt = Node.ITER_CNT, old_nop = Node.ITER_NOP_CNT;
- Node changed = stop.walk( n -> {
+ private boolean progressOnList(CodeGen code) {
+ code._midAssert = true;
+ Node changed = code._stop.walk( n -> {
Node m = n;
if( n.compute().isa(n._type) && (!n.iskeep() || n._nid<=6) ) { // Types must be forwards, even if on worklist
- if( WORK.on(n) ) return null;
+ if( _work.on(n) ) return null;
m = n.peepholeOpt();
if( m==null ) return null;
}
System.err.println("BREAK HERE FOR BUG");
return m;
});
- Node.ITER_CNT = old_cnt; Node.ITER_NOP_CNT = old_nop;
- MID_ASSERT = false;
+ code._midAssert = false;
return changed==null;
}
- public static void reset() {
- WORK.clear();
- }
-
/**
* Classic WorkList, with a fast add/remove, dup removal, random pull.
* The Node's nid is used to check membership in the worklist.
diff --git a/src/main/java/com/seaofnodes/simple/ListScheduler.java b/src/main/java/com/seaofnodes/simple/ListScheduler.java
index 252a8f8e..efca945a 100644
--- a/src/main/java/com/seaofnodes/simple/ListScheduler.java
+++ b/src/main/java/com/seaofnodes/simple/ListScheduler.java
@@ -7,91 +7,157 @@ import java.util.*;
public abstract class ListScheduler {
- private static final BitSet VISIT = new BitSet();
- private static int[] BCNTS;
- public static void sched( CodeGen code ) {
- assert VISIT.isEmpty();
- BCNTS = new int[Node.UID()];
- walk(code._start,VISIT);
- VISIT.clear();
- }
+ // eXtra per-node stuff for scheduling.
+ private static final IdentityHashMap<Node,XSched> XS = new IdentityHashMap<>();
+ private static class XSched {
+ static final Ary<XSched> FREE = new Ary<>(XSched.class);
+ static void alloc(CFGNode bb, Node n) {
+ XSched x = FREE.pop();
+ XS.put(n, (x==null ? new XSched() : x).init(bb,n));
+ }
+ void free() { _n = null; FREE.push(this); }
- // Visit all CFG nodes
- static void walk( CFGNode cfg, BitSet visit ) {
- if( visit.get(cfg._nid) ) return;
- visit.set(cfg._nid);
- local(cfg);
- for( Node n : cfg._outputs )
- if( n instanceof CFGNode c )
- walk(c,visit);
- }
+ static XSched get(Node n) { return n==null ? null : XS.get(n); }
- // Local schedule this one CFG
- static void local( CFGNode bb ) {
- if( !bb.blockHead() || bb instanceof StopNode ) return;
- Node[] uses = bb._outputs._es;
- int ulen = bb._outputs._len;
-
- // Count block-locals
- for( int i=0; i<ulen; i++ ) {
- Node use = uses[i];
- if( use.in(0)==bb && !(use instanceof PhiNode) ) {
- // Normal basic block member: in(0)==bb and not a Phi
- for( Node def : use._inputs )
- // Has defs from same block? Then block-local inputs
- if( def != null && def.cfg0()==bb ) // Same block?
- BCNTS[use._nid]++; // Raise block-local input count
+ private XSched init(CFGNode bb, Node n) {
+ _n = n;
+ _bcnt = 0;
+ _ruse = _rdef = _single = false;
+
+ // Count block-locals and remote inputs
+ if( !(n instanceof PhiNode) )
+ for( Node def : n._inputs )
+ if( def != null && def != bb ) {
+ if( def.cfg0()==bb ) _bcnt++; // Raise block-local input count
+ else _ruse = true;
+ }
+ // Count remote outputs and allowed registers
+ if( n instanceof MultiNode )
+ for( Node use : n._inputs )
+ computeSingleRDef(bb,use);
+ else
+ computeSingleRDef(bb,n);
+ return this;
+ }
+
+ private void computeSingleRDef(CFGNode bb, Node n) {
+ RegMaskRW rmask = n instanceof MachNode mach && mach.outregmap()!=null ? mach.outregmap().copy() : null;
+ for( Node use : n._outputs ) {
+ // Remote use, so this is a remote def
+ if( use!=null && use.cfg0()!=bb ) _rdef = true;
+ // And mask between n's def mask and all n's users masks'
+ if( use instanceof MachNode mach && rmask != null )
+ for( int i=1; i<use.nIns(); i++ )
+ if( use.in(i)==n )
+ rmask.and(mach.regmap(i));
}
+ // Defines in a single register
+ _single = rmask!=null && rmask.size1();
+ }
+
+ // If _bcnt==0, declare ready; move user bcnts into rcnts.
+ boolean isReady() {
+ if( _bcnt > 0 || _rcnt > 0 ) return false;
+ if( _n instanceof MultiNode )
+ for( Node use : _n._outputs )
+ if( use instanceof ProjNode ) readyUp(use);
+ // anti-dep from a multireg directly
+ else _readyUp(use);
+ else
+ readyUp(_n);
+ return true;
+ }
+ private static void readyUp(Node n) {
+ for( Node use : n._outputs )
+ _readyUp(use);
+ }
+ private static void _readyUp(Node use) {
+ XSched xs = get(use);
+ if( xs!=null && !(use instanceof PhiNode) )
+ { assert xs._bcnt>0; xs._bcnt--; xs._rcnt++; }
+ }
+
+ boolean decIsReady() {
+ assert _rcnt > 0;
+ _rcnt--;
+ return isReady();
}
+ Node _n; // Node this extra is for
+ int _bcnt; // Not-ready not-scheduled inputs
+ int _rcnt; // Ready not-scheduled inputs
+ boolean _ruse; // Node IS a "remote use", uses a other-block value
+ boolean _rdef; // Node IS a "remote def" of a value used in other block
+ boolean _single; // Defines a single register, likely to conflict
+ }
+
+
+ // List schedule every block
+ public static void sched( CodeGen code ) {
+ XS.clear();
+ for( CFGNode bb : code._cfg )
+ if( bb.blockHead() )
+ local(bb);
+ }
+
+ // Classic list scheduler
+ private static void local( CFGNode bb ) {
+ assert XS.isEmpty();
+ int len = bb.nOuts();
+
+ // Count block-locals, remote inputs and outputs
+ for( int i=0; i<len; i++ )
+ XSched.alloc(bb,bb.out(i));
+
// Nodes are ready if they are not used by other nodes in this block.
- // Move ready up front. We will never look at remaining nodes, later
- // nodes are found by looking at users of ready nodes.
+ // Move ready up front.
int ready = 0;
- for( int i=0; i<ulen; i++ )
- if( BCNTS[uses[i]._nid]==0 ) // No block-local inputs?
- uses[ready++] = uses[i]; // Move into ready set
- assert ready > 0;
+ for( int i=0; i<len; i++ )
+ if( XSched.get(bb.out(i)).isReady() ) // No block-local inputs?
+ bb._outputs.swap(ready++,i); // Move into ready set
+ assert len==0 || ready > 0;
// Classic list scheduler. Behind sched is scheduled. Between sched
// and ready have zero counts and are ready to schedule. Ahead of
// ready is undefined.
//
- // As nodes are scheduled, the nodes they use decrement their _bcnt. Once
+ // As nodes are scheduled, the nodes they use decrement their bcnts[]. Once
// that gets to 0, all the nodes using it are scheduled, so it's ready.
int sched = 0;
- while( sched < ulen ) {
- assert sched < ready : "Scheduling nodes that are not ready. Probably due to duplicate nodes in the user list.";
- int pick = best(uses, sched,ready); // Pick best
- Node best = uses[pick]; // Swap best to head of ready
- uses[pick] = uses[sched];
- uses[sched++] = best; // And move it into the scheduled set
- // Lower ready count of users
+ while( sched < len ) {
+ assert sched < ready;
+ int pick = best(bb._outputs,sched,ready); // Pick best
+ Node best = bb._outputs.swap(pick,sched++); // Swap best to head of ready and move into the scheduled set
+ // Lower ready count of users, and since projections are ready as
+ // soon as their multi is ready, lower those now too.
for( Node use : best._outputs )
- if( use instanceof ProjNode ) {
+ if( use instanceof ProjNode )
for( Node useuse : use._outputs )
ready = ready(bb,useuse,ready);
- } else
- ready = ready(bb, use,ready);
+ else
+ ready = ready(bb,use,ready);
}
+
+ // Reset for next block
+ for( XSched x : XS.values() )
+ x.free();
+ XS.clear();
}
private static int ready(CFGNode bb, Node use, int ready) {
- if( use != null && use.in(0)==bb && !(use instanceof PhiNode) ) {
- assert BCNTS[use._nid] >= 1;
- if( --BCNTS[use._nid] == 0 )
- bb._outputs._es[ready++] = use; // Became ready, move into ready set
- }
+ if( use!=null && !(use instanceof PhiNode) && use.in(0)==bb && XSched.get(use).decIsReady() )
+ bb._outputs.set(ready++, use); // Became ready, move into ready set
+ //bb._outputs.swap(ready++,use); // Became ready, move into ready set
return ready;
}
// Pick best between sched and ready.
- private static int best( Node[] uses, int sched, int ready ) {
+ private static int best( Ary<Node> blk, int sched, int ready ) {
int pick = sched;
- int score = score(uses[pick]);
+ int score = score(blk.at(pick));
for( int i=sched+1; i<ready; i++ ) {
- int nscore = score(uses[i]);
+ int nscore = score(blk.at(i));
if( nscore > score )
{ score = nscore; pick = i; }
}
@@ -99,12 +165,67 @@ public abstract class ListScheduler {
}
// Highest score wins. Max at 1000, min at 10, except specials.
- private static int score( Node n ) {
- if( n.isMultiTail() ) return 1001; // Pinned just behind the multi-head
- if( n instanceof CFGNode ) return n instanceof ReturnNode ? 2 : 1; // Pinned at block exit
+ private static final int[] CNT = new int[3];
+ static int score( Node n ) {
+ if( n instanceof ProjNode ) return 1001; // Pinned at block entry
+ if( n instanceof CProjNode ) return 1001; // Pinned at block entry
+ if( n instanceof PhiNode ) return 1000;
+ if( n instanceof CFGNode ) return 1; // Pinned at block exit
+
+ int score = 500;
+ // Ideal nodes ignore register pressure scheduling
+ if( !(n instanceof MachNode) )
+ return score;
+
+ // Register pressure local scheduling: avoid overlapping specialized
+ // registers usages.
+
+ // Subtract 10 if this op forces a live range to exist that cannot be
+ // resolved immediately; i.e., live count goes up. Scale to 20 if
+ // multi-def.
+
+ // Subtract 100 if this op forces a single-def live range to exist
+ // which might conflict on the same register with other live ranges.
+ // Defines a single register based on def & uses, and the output is not
+ // ready. Scale to 200 for multi-def.
+ CNT[1]=CNT[2]=0;
+ XSched xn = XSched.get(n);
+ if( xn._rdef ) score = 200; // If defining a remote value, just generically stall alot
+ if( n instanceof MultiNode ) {
+ for( Node use : n._outputs )
+ singleUseNotReady( use, xn._single );
+ } else if( n.nOuts() == 1 )
+ singleUseNotReady( n, xn._single );
+ score += -10 * Math.min( CNT[1], 2 );
+ score += -100 * Math.min( CNT[2], 2 );
+
+ // Add 10 if this op ends a single-def live range, add 20 for 2 or more.
+ // Scale to 100,200 if the single-def is also single-register.
+ CNT[1]=CNT[2]=0;
+ for( int i=1; i<n.nIns(); i++ ) {
+ XSched xd = XSched.get( n.in(i) );
+ if( xd != null && xd._n.nOuts()==1 )
+ CNT[xd._single || ((MachNode)n).regmap(i)!=null && ((MachNode)n).regmap(i).size1() ? 2 : 1]++;
+ }
+ score += 10 * Math.min( CNT[1], 2 );
+ score += 100 * Math.min( CNT[2], 2 );
+
+ //// Nothing special
+ //if( n instanceof Pe ) return 500; // RISC want to go after PE
+ //if( n instanceof Risc ) return 400; // RISC want to go after PE
+ assert 10 <= score && score <= 990;
+ return score;
+ }
- // Nothing special
- return 500;
+ // Single use, and that use is not ready to fire once this one does.
+ // If true, stalling 'n' might reduce 'n's lifetime.
+ // Return 0 for false, 1 if true, 10 if also single register
+ private static void singleUseNotReady( Node n, boolean single ) {
+ if( n.nOuts() != 1 ) return;
+ XSched xu = XSched.get(n.out(0));
+ if( xu !=null && xu._bcnt==0 )
+ return;
+ CNT[single ? 2 : 1]++;
}
}
diff --git a/src/main/java/com/seaofnodes/simple/Machine.java b/src/main/java/com/seaofnodes/simple/Machine.java
new file mode 100644
index 00000000..b29e9aba
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/Machine.java
@@ -0,0 +1,21 @@
+package com.seaofnodes.simple;
+
+import com.seaofnodes.simple.RegMask;
+import com.seaofnodes.simple.node.CFGNode;
+import com.seaofnodes.simple.node.IfNode;
+import com.seaofnodes.simple.node.Node;
+
+abstract public class Machine {
+ // Human readable machine name. Something like "x86-64" or "arm" or "risc5"
+ public abstract String name();
+ // Human-readable name for a register number, e.g. "RAX" or "R0"
+ public abstract String reg( int reg );
+ // Create a split op; any register to any register, including stack slots
+ public abstract Node split();
+ // Return a MachNode unconditional branch
+ public abstract CFGNode jump();
+ // Break an infinite loop
+ public abstract IfNode never( CFGNode ctrl );
+ // Instruction select from ideal nodes
+ public abstract Node instSelect( Node n );
+}
diff --git a/src/main/java/com/seaofnodes/simple/Parser.java b/src/main/java/com/seaofnodes/simple/Parser.java
index 82ad8d04..00aa117e 100644
--- a/src/main/java/com/seaofnodes/simple/Parser.java
+++ b/src/main/java/com/seaofnodes/simple/Parser.java
@@ -2,6 +2,7 @@ package com.seaofnodes.simple;
import com.seaofnodes.simple.node.*;
import com.seaofnodes.simple.type.*;
+import java.text.ParseException;
import java.util.*;
/**
@@ -13,23 +14,12 @@ 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
- * through the entire parser and optimizer.
- * <p>
- * To make the compiler multithreaded, this field will have to move into a TLS.
- */
- public static StartNode START;
-
public static ConstantNode ZERO; // Very common node, cached here
public static ConstantNode NIL; // Very common node, cached here
public static XCtrlNode XCTRL; // Very common node, cached here
- // Next available memory alias number
- static int ALIAS;
-
- public StopNode STOP;
+ // Compile driver
+ public final CodeGen _code;
// The Lexer. Thin wrapper over a byte[] buffer with a cursor.
private final Lexer _lexer;
@@ -51,7 +41,7 @@ public class Parser {
/**
* List of keywords disallowed as identifiers
*/
- private static final HashSet<String> KEYWORDS = new HashSet<>(){{
+ public static final HashSet<String> KEYWORDS = new HashSet<>(){{
add("bool");
add("break");
add("byte");
@@ -98,21 +88,14 @@ public class Parser {
public final HashMap<String, StructNode> INITS;
- public Parser(String source) { this(source, TypeInteger.BOT); }
-
- public Parser(String source, TypeInteger arg) {
- Node.reset();
- IterPeeps.reset();
- Type.reset();
- _lexer = new Lexer(source);
+ public Parser(CodeGen code, TypeInteger arg) {
+ _code = code;
+ _lexer = new Lexer(code._src);
_scope = new ScopeNode();
_continueScope = _breakScope = null;
- START = new StartNode(arg);
- STOP = new StopNode(source);
ZERO = con(TypeInteger.ZERO).keep();
NIL = con(Type.NIL).keep();
XCTRL= new XCtrlNode().peephole().keep();
- ALIAS = 2; // alias 0 for the control, 1 for memory
TYPES = defaultTypes();
INITS = new HashMap<>();
}
@@ -142,12 +125,12 @@ public class Parser {
}
// Debugging utility to find a Node by index
- public static Node find(int nid) { return START.find(nid); }
+ public Node f(int nid) { return _code.f(nid); }
private Node ctrl() { return _scope.ctrl(); }
private <N extends Node> N ctrl(N n) { return _scope.ctrl(n); }
- public StopNode parse() {
+ public void parse() {
_xScopes.push(_scope);
_scope.define(ScopeNode.CTRL, Type.CONTROL , false, null, _lexer);
_scope.define(ScopeNode.MEM0, TypeMem.BOT , false, null, _lexer);
@@ -157,7 +140,8 @@ public class Parser {
_scope.mem(con(TypeMem.BOT));
// Parse whole program, as-if function header "{ int arg -> body }"
- parseFunctionBody(TypeFunPtr.MAIN,loc(),"arg");
+ parseFunctionBody(_code._main,loc(),"arg");
+ _code.link(_code._main)._name = "main";
if( !_lexer.isEOF() ) throw _errorSyntax("unexpected");
@@ -167,8 +151,7 @@ public class Parser {
for( StructNode init : INITS.values() )
init.unkeep().kill();
INITS.clear();
- STOP.peephole();
- return STOP;
+ _code._stop.peephole();
}
/**
@@ -186,24 +169,24 @@ public class Parser {
ScopeNode breakScope = _breakScope; _breakScope = null;
ScopeNode continueScope = _continueScope; _continueScope = null;
- FunNode fun = _fun = (FunNode)peep(new FunNode(loc(),START,sig));
+ FunNode fun = _fun = (FunNode)peep(new FunNode(loc(),sig,null,_code._start));
// Once the function header is available, install in linker table -
// allowing recursive functions. Linker matches on declared args and
// exact fidx, and ignores the return (because the fidx will only match
// the exact single function).
- CodeGen.CODE.link(fun);
+ _code.link(fun);
Node rpc = new ParmNode("$rpc",0,TypeRPC.BOT,fun,con(TypeRPC.BOT)).peephole();
// Build a multi-exit return point for all function returns
- RegionNode r = new RegionNode((Node)null,null).init();
+ RegionNode r = new RegionNode((Lexer)null,null,null).init();
assert r.inProgress();
PhiNode rmem = new BulkMemPhiNode(ScopeNode.MEM0,r,null).init();
PhiNode rrez = new PhiNode(ScopeNode.ARG0,Type.BOTTOM,r,null).init();
ReturnNode ret = new ReturnNode(r, rmem, rrez, rpc, fun).init();
fun.setRet(ret);
assert ret.inProgress();
- STOP.addDef(ret);
+ _code._stop.addDef(ret);
// Pre-call the function from Start, with worse-case arguments. This
// represents all the future, yet-to-be-parsed functions calls and
@@ -398,7 +381,7 @@ public class Parser {
// Our current scope is the body Scope
ctrl(ifT.unkeep()); // set ctrl token to ifTrue projection
_scope.addGuards(ifT,pred.unkeep(),false); // Up-cast predicate
- Node expr = parseStatement().keep(); // Parse loop body
+ parseStatement(); // Parse loop body
_scope.removeGuards(ifT);
// Merge the loop bottom into other continue statements
@@ -438,7 +421,7 @@ public class Parser {
_xScopes.pop();
_xScopes.push(exit);
_scope = exit;
- return peep(expr.unkeep());
+ return ZERO;
}
private ScopeNode jumpTo(ScopeNode toScope) {
@@ -696,7 +679,6 @@ public class Parser {
boolean xfinal = bindingFinal(t);
Lexer loc = loc();
String name = requireId();
-
// Optional initializing expression follows
Node expr;
if( match("=") ) {
@@ -709,7 +691,7 @@ public class Parser {
if( !xfinal ) t = inferredType(t); // Widen if not final
}
if( t instanceof TypeFunPtr && expr._type instanceof TypeFunPtr tfp && tfp.isConstant() )
- tfp.setName(name);
+ _code.link(tfp)._name = name;
} else {
// Need an expression to infer the type.
@@ -774,7 +756,7 @@ public class Parser {
s.addDef(_scope.in(i));
Var v = _scope._vars.at(i);
if( !v.isFRef() ) // Promote to outer scope, not defined here
- fs.push(Field.make(v._name,v.type(),ALIAS++,v._final));
+ fs.push(Field.make(v._name,v.type(),_code.getALIAS(),v._final));
}
TypeStruct ts = s._ts = TypeStruct.make(typeName, fs.asAry());
TYPES.put(typeName, TypeMemPtr.make(ts));
@@ -842,7 +824,7 @@ public class Parser {
int old2 = pos();
if( !match("!") ) match("~");
String id = _lexer.matchId();
- if( !(peek(',') || peek(';')) )
+ if( !(peek(',') || peek(';') || match("->")) )
return posT(old1);
pos(old2); // Reset lexer to reparse
if( id==null )
@@ -865,10 +847,10 @@ public class Parser {
if( t instanceof TypeMemPtr ptr && (ptr._obj._fields==null || !ptr._obj.isAry()) &&
ary._fields[1]._type instanceof TypeMemPtr old )
t = old.withAccess(ptr._ro);
- return TypeMemPtr.make(TypeStruct.makeAry(TypeInteger.BOT,ary._fields[0]._alias,t,ary._fields[1]._alias));
+ return TypeMemPtr.make(TypeStruct.makeAry(TypeInteger.U32,ary._fields[0]._alias,t,ary._fields[1]._alias));
}
// Need make an array type.
- TypeStruct ts = TypeStruct.makeAry(TypeInteger.BOT,ALIAS++,t,ALIAS++);
+ TypeStruct ts = TypeStruct.makeAry(TypeInteger.U32,_code.getALIAS(),t,_code.getALIAS());
assert ts.str().equals(tname);
TypeMemPtr tary = TypeMemPtr.make(ts);
TYPES.put(tname,tary);
@@ -1225,14 +1207,13 @@ public class Parser {
if( fs==null )
throw error("Unknown struct type '" + obj._name + "'");
Node prior = mem().keep();
- MemMergeNode input = new MemMergeNode(null);
+ MemMergeNode input = new MemMergeNode((Node)null);
for( Field f : fs ) input.alias(f._alias,prior);
- // Constructor values follow control, memory, and allocation size.
- Node[] ns = new Node[3+fs.length];
+ // Allocation zeroes its fields; nonzero initializers become explicit Stores.
+ Node[] ns = new Node[3];
ns[0] = ctrl();
ns[1] = input.peephole();
ns[2] = size;
- for( int i=0; i<fs.length; i++ ) ns[3+i] = init.get(idx+i);
Node nnn = new NewNode(TypeMemPtr.make(obj),ns).peephole().keep();
Node ptr = new ProjNode(nnn,0,obj._name).peephole().keep();
Node out = new ProjNode(nnn,1,"$mem").peephole().keep();
@@ -1242,6 +1223,14 @@ public class Parser {
out.unkeep();
nnn.unkeep();
prior.unkill();
+ for( int i=0; i<fs.length; i++ ) {
+ Node val = init.get(idx+i);
+ if( val._type != val._type.makeZero() ) {
+ Field f = fs[i];
+ Node st = new StoreNode(loc(),f._fname,f._alias,f._type,mem(),ptr,con(obj.offset(i)),val,true).peephole();
+ mem(new MemMergeNode(mem(),f._alias,st).peephole());
+ }
+ }
return ptr.unkeep();
}
@@ -1293,8 +1282,9 @@ public class Parser {
throw error("Accessing unknown field '" + name + "' from 'null'");
// Sanity check expr for being a reference
- if( !(expr._type instanceof TypeMemPtr ptr) )
- throw error("Expected reference but got " + expr._type.str());
+ if( !(expr._type instanceof TypeMemPtr ptr) ) {
+ throw error( "Expected "+(name=="#" || name=="[]" ? "array" : "reference")+" but found " + expr._type.str() );
+ }
// Sanity check field name for existing
TypeMemPtr tmp = (TypeMemPtr)TYPES.get(ptr._obj._name);
@@ -1330,7 +1320,7 @@ public class Parser {
// Arrays include control, as a proxy for a safety range check
// Structs don't need this; they only need a NPE check which is
// done via the type system.
- if( base.isAry() ) load.setDef(0,ctrl());
+ if( base.isAry() && !name.equals("#") ) load.setDef(0,ctrl());
load = peep(load);
// Check for assign-update, "ptr.fld += expr" or "ary[idx]++"
@@ -1393,7 +1383,7 @@ public class Parser {
}
require("->");
// Make a concrete function type, with a fidx
- TypeFunPtr tfp = TypeFunPtr.makeFun(TypeTuple.make(ts.asAry()),Type.BOTTOM);
+ TypeFunPtr tfp = _code.makeFun(TypeTuple.make(ts.asAry()),Type.BOTTOM);
ReturnNode ret = parseFunctionBody(tfp,loc,finals,ids.asAry());
return con(ret._fun.sig());
}
@@ -1427,6 +1417,14 @@ public class Parser {
// Unkeep them all
for( Node arg : args )
arg.unkeep();
+ // Dead into the call? Skip all the node gen
+ if( ctrl()._type == Type.XCONTROL ) {
+ for( Node arg : args )
+ if( arg.isUnused() )
+ arg.kill();
+ return con(Type.TOP);
+ }
+
// Into the call
CallNode call = (CallNode)new CallNode(loc(), args.asAry()).peephole();
@@ -1680,7 +1678,6 @@ public class Parser {
private String parseNumberString() {
int old = _position;
int len = Math.abs(isLongOrDouble());
- _position += len;
return new String(_input,old,len);
}
diff --git a/src/main/java/com/seaofnodes/simple/RegMask.java b/src/main/java/com/seaofnodes/simple/RegMask.java
new file mode 100644
index 00000000..073b472b
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/RegMask.java
@@ -0,0 +1,47 @@
+package com.seaofnodes.simple;
+
+import java.util.BitSet;
+
+// A "register mask" - 1 bit set for each allowed register. In addition "stack
+// slot" registers may be allowed, effectively making the set infinite.
+public class RegMask {
+
+ public static final RegMask EMPTY = new RegMask();
+
+ final BitSet _bits;
+
+ public RegMask(long x ) { _bits = BitSet.valueOf(new long[]{x}); }
+ public RegMask(long[] xs) { _bits = BitSet.valueOf( xs); }
+
+ public RegMask or(RegMask other) {
+ BitSet resultBits = (BitSet)this._bits.clone();
+ resultBits.or(other._bits);
+ return new RegMask(resultBits);
+ }
+
+ // Internal constructor
+ RegMask() { _bits = new BitSet(); }
+ RegMask(BitSet bs) { _bits = bs; }
+
+ public boolean get(int r) { return _bits.get(r); }
+
+ // Defensive writable copy
+ public RegMaskRW copy() { return new RegMaskRW( (BitSet)_bits.clone() ); }
+
+ boolean size1() { return _bits.cardinality()==1; }
+}
+
+class RegMaskRW extends RegMask {
+ public RegMaskRW() { super(); }
+ public RegMaskRW(long x) { super(new long[]{x}); }
+ public RegMaskRW(long[] xs) { super(xs); }
+ public RegMaskRW(BitSet bs) { super(bs); }
+ public void set(int r) { _bits.set(r); }
+ public void clr(int r) { _bits.clear(r); }
+ public void set(int r, boolean b) { _bits.set(r,b); }
+ public RegMaskRW and( RegMask r ) {
+ if( r==null ) return this;
+ _bits.and(r._bits);
+ return this;
+ }
+}
diff --git a/src/main/java/com/seaofnodes/simple/SB.java b/src/main/java/com/seaofnodes/simple/SB.java
index 6f5a29ee..7d580c21 100644
--- a/src/main/java/com/seaofnodes/simple/SB.java
+++ b/src/main/java/com/seaofnodes/simple/SB.java
@@ -29,6 +29,23 @@ public final class SB {
public SB p( int s ) { _sb.append(s); return this; }
public SB p( long s ) { _sb.append(s); return this; }
public SB p( boolean s) { _sb.append(s); return this; }
+ // 4 hex digits
+ public SB hex4(int s) {
+ assert (s>>4*4)==0; // Fits in 16 bits
+ for( int i=0; i<4; i++ ) {
+ int digit = (s>>((3-i)*4)) & 0xf;
+ _sb.append((char)((digit <= 9 ? '0' : ('A'-10))+digit));
+ }
+ return this;
+ }
+ // Fixed width field
+ public SB fix( int sz, String s ) {
+ assert sz >=0;
+ for( int i=0; i<sz; i++ )
+ _sb.append( i < s.length() ? s.charAt(i) : ' ');
+ return this;
+ }
+
// Not spelled "p" on purpose: too easy to accidentally say "p(1.0)" and
// suddenly call the autoboxed version.
public SB pobj( Object s ) { _sb.append(s.toString()); return this; }
diff --git a/src/main/java/com/seaofnodes/simple/node/AddNode.java b/src/main/java/com/seaofnodes/simple/node/AddNode.java
index f88c2175..2dfedbb5 100644
--- a/src/main/java/com/seaofnodes/simple/node/AddNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/AddNode.java
@@ -47,7 +47,7 @@ public class AddNode extends Node {
// Add of same to a multiply by 2
if( lhs==rhs )
- return new MulNode(lhs,new ConstantNode(TypeInteger.constant(2)).peephole());
+ return new ShlNode(null,lhs,con(1));
// Goal: a left-spine set of adds, with constants on the rhs (which then fold).
@@ -120,6 +120,7 @@ public class AddNode extends Node {
lphi = pcon(lhs.in(2),op); // Will rotate with the Phi push
}
if( lphi==null ) return null;
+ if( lphi.region().nIns() <=2 ) return null; // Phi is collapsing
// RHS is a constant or a Phi of constants
if( !(rhs instanceof ConstantNode) && pcon(rhs,op)==null )
diff --git a/src/main/java/com/seaofnodes/simple/node/BoolNode.java b/src/main/java/com/seaofnodes/simple/node/BoolNode.java
index aa577f61..c0ccfd9b 100644
--- a/src/main/java/com/seaofnodes/simple/node/BoolNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/BoolNode.java
@@ -5,7 +5,6 @@ import com.seaofnodes.print.ExprPrinter;
import com.seaofnodes.simple.Parser;
import com.seaofnodes.simple.Utils;
import com.seaofnodes.simple.type.*;
-
import static com.seaofnodes.simple.type.TypeInteger.*;
abstract public class BoolNode extends Node {
@@ -14,7 +13,7 @@ abstract public class BoolNode extends Node {
super(null, lhs, rhs);
}
- abstract String op(); // String opcode name
+ abstract public String op(); // String opcode name
@Override
public String label() { return getClass().getSimpleName(); }
@@ -32,14 +31,14 @@ abstract public class BoolNode extends Node {
return BOOL.dual();
if( in(1)==in(2) )
// LT fails, both EQ and LE succeed
- return this instanceof LT ? TypeInteger.FALSE : TypeInteger.TRUE;
+ return this instanceof LT ? FALSE : TRUE;
if( t1 instanceof TypeInteger i1 &&
t2 instanceof TypeInteger i2 )
return doOp(i1,i2);
if( t1 instanceof TypeFloat f1 &&
t2 instanceof TypeFloat f2 &&
f1.isConstant() && f2.isConstant() )
- return doOp(f1.value(), f2.value()) ? TypeInteger.TRUE : TypeInteger.FALSE;
+ return doOp(f1.value(), f2.value()) ? TRUE : FALSE;
return BOOL;
}
@@ -50,7 +49,7 @@ abstract public class BoolNode extends Node {
public Node idealize() {
// Compare of same
if( in(1)==in(2) )
- return this instanceof LT ? Parser.ZERO : new ConstantNode(TypeInteger.TRUE);
+ return this instanceof LT ? Parser.ZERO : new ConstantNode(TRUE);
// Equals pushes constant to the right; 5==X becomes X==5.
if( this instanceof EQ ) {
@@ -76,7 +75,7 @@ abstract public class BoolNode extends Node {
public static class EQ extends BoolNode {
public EQ(Node lhs, Node rhs) { super(lhs,rhs); }
- String op() { return "=="; }
+ public String op() { return "=="; }
TypeInteger doOp(TypeInteger i1, TypeInteger i2) {
if( i1==i2 && i1.isConstant() ) return TRUE;
if( i1._max < i2._min || i1._min > i2._max ) return FALSE;
@@ -87,7 +86,7 @@ abstract public class BoolNode extends Node {
}
public static class LT extends BoolNode {
public LT(Node lhs, Node rhs) { super(lhs,rhs); }
- String op() { return "<" ; }
+ public String op() { return "<" ; }
TypeInteger doOp(TypeInteger i1, TypeInteger i2) {
if( i1._max < i2._min ) return TRUE;
if( i1._min >= i2._max ) return FALSE;
@@ -98,7 +97,7 @@ abstract public class BoolNode extends Node {
}
public static class LE extends BoolNode {
public LE(Node lhs, Node rhs) { super(lhs,rhs); }
- String op() { return "<="; }
+ public String op() { return "<="; }
TypeInteger doOp(TypeInteger i1, TypeInteger i2) {
if( i1._max <= i2._min ) return TRUE;
if( i1._min > i2._max ) return FALSE;
@@ -112,4 +111,15 @@ abstract public class BoolNode extends Node {
public static class LTF extends LT { public LTF(Node lhs, Node rhs) { super(lhs,rhs); } boolean doOp(double lhs, double rhs) { return lhs < rhs; } }
public static class LEF extends LE { public LEF(Node lhs, Node rhs) { super(lhs,rhs); } boolean doOp(double lhs, double rhs) { return lhs <= rhs; } }
+ // Unsigned less that, for range checks. Not directly user writable.
+ public static class ULT extends BoolNode {
+ public ULT(Node lhs, Node rhs) { super(lhs,rhs); }
+ public String op() { return "u<" ; }
+ TypeInteger doOp(TypeInteger i1, TypeInteger i2) {
+ if( Long.compareUnsigned(i1._max,i2._min) < 0 ) return TRUE;
+ if( Long.compareUnsigned(i1._min,i2._max) >= 0 ) return FALSE;
+ return BOOL;
+ }
+ Node copy(Node lhs, Node rhs) { return new ULT(lhs,rhs); }
+ }
}
diff --git a/src/main/java/com/seaofnodes/simple/node/BulkMemPhiNode.java b/src/main/java/com/seaofnodes/simple/node/BulkMemPhiNode.java
index de5f8b4c..5d8a4aba 100644
--- a/src/main/java/com/seaofnodes/simple/node/BulkMemPhiNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/BulkMemPhiNode.java
@@ -1,6 +1,6 @@
package com.seaofnodes.simple.node;
-import com.seaofnodes.simple.IterPeeps;
+import com.seaofnodes.simple.CodeGen;
import com.seaofnodes.simple.type.Type;
import com.seaofnodes.simple.type.TypeMem;
import java.util.BitSet;
@@ -29,6 +29,8 @@ public class BulkMemPhiNode extends PhiNode {
public boolean isSplit(int alias) { return _aliases.get(alias); }
+ public BulkMemPhiNode(BulkMemPhiNode phi) { super(phi); _aliases = phi._aliases; }
+
@Override public String label() { return "BulkPhi"+_aliases; }
@Override
@@ -49,7 +51,7 @@ public class BulkMemPhiNode extends PhiNode {
// "Peek through" a MemMerge that covers this alias set on its default
for( int i=1; i<nIns(); i++ )
if( in(i) instanceof MemMergeNode mmm && canPeek(mmm) ) {
- setDef(i,IterPeeps.add(mmm.in(1)));
+ setDef(i,CodeGen.CODE.add(mmm.in(1)));
return this;
}
@@ -168,14 +170,14 @@ public class BulkMemPhiNode extends PhiNode {
// set. Queue the original memory inputs before bphi itself can peep
// away and hide those neighbor relationships.
for( int i=1; i<bphi.nIns(); i++ )
- IterPeeps.add(bphi.in(i));
+ CodeGen.CODE.add(bphi.in(i));
// Do not peephole bphi here, as BulkMemPhi can recursively start a
// second bulk rewrite before this one has finished. Let the worklist
// discover any further splits
bphi.setType(TypeMem.BOT);
Node bulk = bphi;
- IterPeeps.add(bulk);
- IterPeeps.add(mem);
+ CodeGen.CODE.add(bulk);
+ CodeGen.CODE.add(mem);
return aggregate(bulk,alias,mem);
}
@@ -201,8 +203,8 @@ public class BulkMemPhiNode extends PhiNode {
// Due to cycles, must set before calling peephole
mphi.setType(TypeMem.make(alias,Type.BOTTOM));
for( int i=1; i<nIns(); i++ )
- mphi.addDef(IterPeeps.add(preciseInput(in(i),alias)));
- return IterPeeps.add(mphi);
+ mphi.addDef(CodeGen.CODE.add(preciseInput(in(i),alias)));
+ return CodeGen.CODE.add(mphi);
}
MemPhiNode _findPhi(int alias) {
diff --git a/src/main/java/com/seaofnodes/simple/node/CFGNode.java b/src/main/java/com/seaofnodes/simple/node/CFGNode.java
index 49398838..383f18d0 100644
--- a/src/main/java/com/seaofnodes/simple/node/CFGNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/CFGNode.java
@@ -1,13 +1,13 @@
package com.seaofnodes.simple.node;
import com.seaofnodes.simple.CodeGen;
+import com.seaofnodes.simple.Ary;
import com.seaofnodes.simple.Parser;
-import com.seaofnodes.simple.type.*;
import com.seaofnodes.simple.Utils;
-
+import com.seaofnodes.simple.type.*;
import java.util.BitSet;
-import java.util.HashSet;
import java.util.HashMap;
+import java.util.HashSet;
/** Control Flow Graph Nodes
* <p>
@@ -22,7 +22,14 @@ import java.util.HashMap;
*/
public abstract class CFGNode extends Node {
- public CFGNode(Node... nodes) { super(nodes); }
+ public CFGNode(Node... nodes) { super(nodes); }
+ public CFGNode(CFGNode cfg) {
+ super(cfg);
+ _idepth = cfg._idepth;
+ _idepthVersion = cfg._idepthVersion;
+ _ltree = cfg._ltree;
+ _pre = cfg._pre;
+ }
public CFGNode cfg(int idx) { return (CFGNode)in(idx); }
@@ -118,8 +125,8 @@ public abstract class CFGNode extends Node {
}
// Pre-walk
for( Node use : _outputs )
- if( use instanceof CFGNode usecfg && !skip(usecfg) )
- pre = usecfg._bltWalk(pre,use instanceof FunNode fuse ? fuse : fun,stop,post);
+ if( use instanceof CFGNode usecfg && !skip( usecfg ) )
+ pre = usecfg._bltWalk( pre, use instanceof FunNode fuse ? fuse : fun, stop, post );
// Post-order work: find innermost loop
LoopTree inner = null, ltree;
diff --git a/src/main/java/com/seaofnodes/simple/node/CProjNode.java b/src/main/java/com/seaofnodes/simple/node/CProjNode.java
index 70238f8c..c338ff4c 100644
--- a/src/main/java/com/seaofnodes/simple/node/CProjNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/CProjNode.java
@@ -7,7 +7,7 @@ import com.seaofnodes.simple.type.Type;
import com.seaofnodes.simple.type.TypeMem;
import com.seaofnodes.simple.type.TypeTuple;
-public class CProjNode extends CFGNode implements MultiUse {
+public class CProjNode extends CFGNode {
// Which slice of the incoming multipart value
public final int _idx;
@@ -20,9 +20,9 @@ public class CProjNode extends CFGNode implements MultiUse {
_idx = idx;
_label = label;
}
+ public CProjNode(CProjNode c) { super(c); _idx = c._idx; _label = c._label; }
@Override public String label() { return _label; }
- @Override public int idx() { return _idx; }
@Override protected ExprPrinter<Node> _print1(ExprPrinter<Node> p) { return p.p(_label); }
diff --git a/src/main/java/com/seaofnodes/simple/node/CallEndNode.java b/src/main/java/com/seaofnodes/simple/node/CallEndNode.java
index 2da09cfc..83c98b97 100644
--- a/src/main/java/com/seaofnodes/simple/node/CallEndNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/CallEndNode.java
@@ -2,10 +2,7 @@ package com.seaofnodes.simple.node;
import com.seaofnodes.print.ExprPrinter;
-import com.seaofnodes.simple.CodeGen;
-import com.seaofnodes.simple.IterPeeps;
-import com.seaofnodes.simple.Parser;
-import com.seaofnodes.simple.Utils;
+import com.seaofnodes.simple.*;
import com.seaofnodes.simple.type.*;
/**
@@ -17,14 +14,15 @@ public class CallEndNode extends CFGNode implements MultiNode {
private boolean _folding;
public boolean folding() { return _folding; }
- public CallEndNode(CallNode call) { super(call); }
+ public CallEndNode(CallNode call) { super(new Node[]{call}); }
+ public CallEndNode(CallEndNode cend) { super(cend); }
@Override
public String label() { return "CallEnd"; }
@Override public boolean isMultiHead() { return true; }
@Override public boolean blockHead() { return true; }
- CallNode call() { return (CallNode)in(0); }
+ public CallNode call() { return (CallNode)in(0); }
@Override
protected ExprPrinter<Node> _print1(ExprPrinter<Node> p) {
@@ -48,8 +46,7 @@ public class CallEndNode extends CFGNode implements MultiNode {
// Trivial inlining: call site calls a single function; single function
// is only called by this call site.
- if( !_folding && nIns()==2 ) {
- CallNode call = call();
+ if( !_folding && nIns()==2 && in(0) instanceof CallNode call ) {
Node fptr = call.fptr();
if( fptr.nOuts() == 1 && // Only user is this call
fptr instanceof ConstantNode && // We have an immediate call
@@ -74,7 +71,7 @@ public class CallEndNode extends CFGNode implements MultiNode {
fun.setDef(1,Parser.XCTRL); // No default/unknown StartNode caller
fun.setDef(2,call.ctrl()); // Bypass the Call;
fun.ret().setDef(3,null); // Return is folding also
- IterPeeps.addAll(fun._outputs);
+ CodeGen.CODE.addAll(fun._outputs);
CodeGen.CODE.invalidateIDepthCaches();
return this;
}
diff --git a/src/main/java/com/seaofnodes/simple/node/CallNode.java b/src/main/java/com/seaofnodes/simple/node/CallNode.java
index 10b04b38..22cef22d 100644
--- a/src/main/java/com/seaofnodes/simple/node/CallNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/CallNode.java
@@ -18,23 +18,29 @@ public class CallNode extends CFGNode {
public final Parser.Lexer _loc;
public CallNode(Parser.Lexer loc, Node... nodes) { super(nodes); _loc = loc; }
+ public CallNode(CallNode call) { super(call); _loc = call._loc; }
@Override
public String label() { return "Call"; }
- @Override
- protected ExprPrinter<Node> _print1(ExprPrinter<Node> p) {
- String fname = null;
- Node fptr = fptr();
- if( fptr._type instanceof TypeFunPtr tfp && tfp.isConstant() )
- fname = tfp._name;
- if( fname==null ) p.n(fptr);
+ @Override protected ExprPrinter<Node> _print1(ExprPrinter<Node> p) {
+ String fname = name();
+ if( fname == null ) p.n(fptr());
else p.p(fname);
p.p("( ");
for( int i=2; i<nIns()-1; i++ )
p.n(in(i)).p(",");
return p.unchar(',').unchar(' ').close();
}
+ public String name() {
+ if( fptr()._type instanceof TypeFunPtr tfp && tfp.isConstant() && tfp.notNull() ) {
+ FunNode fun = CodeGen.CODE.link(tfp);
+ return fun==null ? null : fun._name;
+ }
+ return null;
+ }
+
+
Node ctrl() { return in(0); }
Node mem () { return in(1); }
@@ -44,6 +50,8 @@ public class CallNode extends CFGNode {
int nargs() { return nIns()-3; } // Minus control, memory, fptr
// args from input 2 to last; last is function input
public Node fptr() { return _inputs.last(); }
+ // Error if not a TFP
+ public TypeFunPtr tfp() { return (TypeFunPtr)fptr()._type; }
// Find the Call End from the Call
public CallEndNode cend() {
@@ -118,7 +126,7 @@ public class CallNode extends CFGNode {
if( use instanceof ParmNode parm )
parm.addDef(parm._idx==0 ? cend() : arg(parm._idx));
// Call end points to function return
- IterPeeps.add(cend()).addDef(fun.ret());
+ CodeGen.CODE.add(cend()).addDef(fun.ret());
assert linked(fun);
}
diff --git a/src/main/java/com/seaofnodes/simple/node/ConstantNode.java b/src/main/java/com/seaofnodes/simple/node/ConstantNode.java
index 02d0289b..89014205 100644
--- a/src/main/java/com/seaofnodes/simple/node/ConstantNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/ConstantNode.java
@@ -2,10 +2,11 @@ package com.seaofnodes.simple.node;
import com.seaofnodes.print.ExprPrinter;
+import com.seaofnodes.simple.CodeGen;
import com.seaofnodes.simple.Parser;
import com.seaofnodes.simple.SB;
import com.seaofnodes.simple.type.Type;
-
+import com.seaofnodes.simple.type.TypeFunPtr;
/**
* A Constant node represents a constant value. At present, the only constants
@@ -19,12 +20,14 @@ import com.seaofnodes.simple.type.Type;
* <p>
* The Constant's value is the value stored in it.
*/
+
public class ConstantNode extends Node {
public final Type _con;
public ConstantNode( Type type ) {
- super(Parser.START);
- _con = type;
+ super(new Node[]{CodeGen.CODE._start});
+ _con = _type = type;
}
+ public ConstantNode( ConstantNode con ) { super(con); _con = con._type; }
public static Node make( Type type ) {
if( type==Type. CONTROL ) return new CtrlNode();
@@ -38,7 +41,13 @@ public class ConstantNode extends Node {
@Override public String label() { return "Con"; }
- @Override protected ExprPrinter<Node> _print1(ExprPrinter<Node> p) {
+ @Override
+ protected ExprPrinter<Node> _print1(ExprPrinter<Node> p) {
+ if( _con instanceof TypeFunPtr tfp && tfp.isConstant() && tfp.notNull() ) {
+ FunNode fun = CodeGen.CODE.link(tfp);
+ if( fun!=null && fun._name != null )
+ return p.p("{ ").p(fun._name).p("}");
+ }
return p.p(_con.print(new SB()));
}
diff --git a/src/main/java/com/seaofnodes/simple/node/CtrlNode.java b/src/main/java/com/seaofnodes/simple/node/CtrlNode.java
index 9268381a..e647a68a 100644
--- a/src/main/java/com/seaofnodes/simple/node/CtrlNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/CtrlNode.java
@@ -2,11 +2,12 @@ package com.seaofnodes.simple.node;
import com.seaofnodes.print.ExprPrinter;
+import com.seaofnodes.simple.CodeGen;
import com.seaofnodes.simple.Parser;
import com.seaofnodes.simple.type.Type;
public class CtrlNode extends CFGNode {
- public CtrlNode() { super(Parser.START); }
+ public CtrlNode() { super(CodeGen.CODE._start); }
@Override public String label() { return "Ctrl"; }
@Override protected ExprPrinter<Node> _print1(ExprPrinter<Node> p) { return p.p("Cctrl"); }
@Override public boolean isConst() { return true; }
diff --git a/src/main/java/com/seaofnodes/simple/node/FunNode.java b/src/main/java/com/seaofnodes/simple/node/FunNode.java
index ec60f215..d24f4829 100644
--- a/src/main/java/com/seaofnodes/simple/node/FunNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/FunNode.java
@@ -4,6 +4,7 @@ import com.seaofnodes.simple.*;
import com.seaofnodes.simple.type.Type;
import com.seaofnodes.simple.type.TypeFunPtr;
import java.util.BitSet;
+import static com.seaofnodes.simple.CodeGen.CODE;
public class FunNode extends RegionNode {
@@ -14,10 +15,13 @@ public class FunNode extends RegionNode {
private TypeFunPtr _sig; // Initial signature
private ReturnNode _ret; // Return pointer
- public FunNode( Parser.Lexer loc, StartNode start, TypeFunPtr sig ) { super(loc,null,start); _sig=sig; }
+ public String _name; // Debug name
+
+ public FunNode( Parser.Lexer loc, TypeFunPtr sig, Node... nodes ) { super(loc,nodes); _sig = sig; }
+ public FunNode( FunNode fun ) { super( fun, fun._loc ); _sig = fun.sig(); _name = fun._name; }
@Override
- public String label() { return _sig._name == null ? "$fun" : _sig._name; }
+ public String label() { return _name == null ? "$fun" : _name; }
// Find the one CFG user from Fun. It's not always the Return, but always
// the Return *is* a CFG user of Fun.
@@ -46,9 +50,10 @@ public class FunNode extends RegionNode {
public TypeFunPtr sig() { return _sig; }
void setSig( TypeFunPtr sig ) {
assert sig.isa(_sig);
- if( _sig != sig )
- IterPeeps.add(this);
- _sig = sig;
+ if( _sig != sig ) {
+ CODE.add(this);
+ _sig = sig;
+ }
}
@Override
@@ -59,6 +64,15 @@ public class FunNode extends RegionNode {
@Override
public Node idealize() {
+
+ // Some linked path dies
+ Node progress = deadPath();
+ if( progress!=null ) {
+ if( nIns()==3 && in(2) instanceof CallNode call )
+ CODE.add(call.cend()); // If Start and one call, check for inline
+ return progress;
+ }
+
// When can we assume no callers? Or no other callers (except main)?
// In a partial compilation, we assume Start gets access to any/all
// top-level public structures and recursively what they point to.
@@ -67,16 +81,18 @@ public class FunNode extends RegionNode {
// In a total compilation, we can start from Start and keep things
// more contained.
-
// If no default/unknown caller, use the normal RegionNode ideal rules
// to collapse
if( unknownCallers() ) return null;
- Node progress = super.idealize();
- if( progress!=null ) {
- IterPeeps.add( CodeGen.CODE._stop);
- IterPeeps.add( _ret );
+
+ // If down to a single input, become that input
+ if( nIns()==2 && !hasPhi() ) {
+ CODE.add( CODE._stop ); // Stop will remove dead path
+ CODE.add( _ret ); // Return will compute to TOP control
+ return in(1); // Collapse if no Phis; 1-input Phis will collapse on their own
}
- return progress;
+
+ return null;
}
// Unfolded functions are roots; inlining reconnects their depth to the caller.
diff --git a/src/main/java/com/seaofnodes/simple/node/IfNode.java b/src/main/java/com/seaofnodes/simple/node/IfNode.java
index a3061b63..a01eb8bd 100644
--- a/src/main/java/com/seaofnodes/simple/node/IfNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/IfNode.java
@@ -1,6 +1,7 @@
package com.seaofnodes.simple.node;
+import com.seaofnodes.simple.CodeGen;
import com.seaofnodes.simple.IterPeeps;
import com.seaofnodes.simple.type.*;
import java.util.HashSet;
@@ -9,8 +10,9 @@ public class IfNode extends CFGNode implements MultiNode {
public IfNode(Node ctrl, Node pred) {
super(ctrl, pred);
- IterPeeps.add(this); // Because idoms are complex, just add it
+ CodeGen.CODE.add(this); // Because idoms are complex, just add it
}
+ public IfNode(IfNode iff) { super(iff); }
@Override
public String label() { return "If"; }
@@ -54,7 +56,7 @@ public class IfNode extends CFGNode implements MultiNode {
if( !pred()._type.isHighOrConst() )
for( CFGNode dom = idom(), prior=this; dom!=null; prior = dom, dom = dom.idom() )
if( dom.addDep(this) instanceof IfNode iff && iff.pred().addDep(this)==pred() && prior instanceof CProjNode prj ) {
- setDef(1,new ConstantNode(TypeInteger.constant(prj._idx==0?1:0)).peephole());
+ setDef(1,con( prj._idx==0 ? 1 : 0 ));
return this;
}
return null;
diff --git a/src/main/java/com/seaofnodes/simple/node/LoadNode.java b/src/main/java/com/seaofnodes/simple/node/LoadNode.java
index 5190bf08..a06d1291 100644
--- a/src/main/java/com/seaofnodes/simple/node/LoadNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/LoadNode.java
@@ -2,12 +2,10 @@ package com.seaofnodes.simple.node;
import com.seaofnodes.print.ExprPrinter;
+import com.seaofnodes.simple.CodeGen;
import com.seaofnodes.simple.Parser;
import com.seaofnodes.simple.Utils;
-import com.seaofnodes.simple.IterPeeps;
import com.seaofnodes.simple.type.*;
-import java.util.BitSet;
-import java.util.ArrayList;
/**
* Load represents extracting a value from inside a memory object,
@@ -65,29 +63,36 @@ public class LoadNode extends MemOpNode {
@Override
public Node idealize() {
if( mem() instanceof MemMergeNode merge ) {
- setDef(1,IterPeeps.add(merge.alias(_alias)));
+ setDef(1,CodeGen.CODE.add(merge.alias(_alias)));
return this;
}
Node ptr = ptr();
+ Node mem = mem();
// Simple Load-after-Store on same address.
if( mem() instanceof StoreNode st &&
_alias==st._alias && ptr == st.ptr() && off() == st.off() ) { // Must check same object
assert _name.equals(st._name); // Equiv class aliasing is perfect
- return castRO(st.val());
+ return extend(st.val());
}
// Simple Load-after-New on same address.
- if( mem() instanceof ProjNode p && p.in(0) instanceof NewNode nnn &&
+ if( mem instanceof ProjNode p && p.in(0) instanceof NewNode nnn &&
ptr == nnn.proj(0) ) // Must check same object
- return castRO(nnn.in(nnn.findAlias(_alias))); // Load from New init
+ return zero(nnn); // Load zero from new
+
+ // Uplift control to a prior dominating load.
+ for( Node memuse : mem._outputs )
+ // Find a prior load, has same mem,ptr,off but higher ctrl
+ if( memuse != this && memuse instanceof LoadNode ld && ptr==ld.ptr() && off()==ld.off() &&
+ cfg0()!=null && cfg0().domLCA(ld.cfg0(),this) == ld.cfg0() ) // Higher control means load is legal earlier
+ return ld;
// Load-after-Store on same address, but bypassing provably unrelated
// stores. This is a more complex superset of the above two peeps.
// "Provably unrelated" is really weak.
if( ptr instanceof ReadOnlyNode ro )
ptr = ro.in(1);
- Node mem = mem();
outer:
while( true ) {
mem.addDep(this);
@@ -97,7 +102,7 @@ public class LoadNode extends MemOpNode {
break;
case StoreNode st:
if( _alias==st._alias && ptr == st.ptr().addDep(this) && off() == st.off() )
- return castRO(st.val()); // Proved equal
+ return extend(castRO(st.val())); // Proved equal
// Can we prove unequal? Offsets do not overlap?
if( _alias==st._alias && !off()._type.join(st.off()._type).isHigh() && // Offsets overlap
!neverAlias(ptr,st.ptr()) ) // And might alias
@@ -114,8 +119,8 @@ public class LoadNode extends MemOpNode {
switch( mproj.in(0) ) {
case NewNode nnn1:
if( ptr instanceof ProjNode pproj && pproj.in(0) == mproj.in(0) )
- return castRO(nnn1.in(nnn1.findAlias(_alias))); // Load from New init
- if( !(ptr instanceof ProjNode pproj && pproj.in(0) instanceof NewNode nnn2) )
+ return zero(nnn1);
+ if( !(ptr instanceof ProjNode pproj && pproj.in(0) instanceof NewNode) )
break outer; // Cannot tell, ptr not related to New
mem = nnn1.mem();// Bypass unrelated New
break;
@@ -156,6 +161,14 @@ public class LoadNode extends MemOpNode {
return null;
}
+ // Load a flavored zero from a New
+ private Node zero(NewNode nnn) {
+ TypeStruct ts = nnn._ptr._obj;
+ Type zero = ts._fields[ts.findAlias(_alias)]._type.makeZero();
+ assert zero.isa(_type); // Catch an uninitialized non-null field
+ return castRO(new ConstantNode(zero).peephole());
+ }
+
private Node ld( int idx ) {
Node mem = mem(), ptr = ptr();
LoadNode ld = new LoadNode(_loc,_name,_alias,_declaredType,mem.in(idx),ptr instanceof PhiNode && ptr.in(0)==mem.in(0) ? ptr.in(idx) : ptr,off());
@@ -206,29 +219,19 @@ public class LoadNode extends MemOpNode {
return rez;
}
- // Memory writers which must follow this read. A packaging node is not a
- // write, but can hide New's input, so follow this alias through aggregates.
- public ArrayList<Node> antiDeps() {
- ArrayList<Node> deps = new ArrayList<>();
- antiDeps(mem(),deps,new BitSet());
- return deps;
+ // When a load bypasses a store, the store might truncate bits - and the
+ // load will need to zero/sign-extend.
+ private Node extend(Node val) {
+ if( !(_declaredType instanceof TypeInteger ti) ) return val;
+ if( ti._min==0 ) // Unsigned
+ return new AndNode(null,val,con(ti._max));
+ // Signed extension
+ int shift = Long.numberOfLeadingZeros(ti._max)-1;
+ if( shift==0 )
+ return val;
+ Node shf = con(shift);
+ Node shl = new ShlNode(null,val,shf.keep()).peephole();
+ return new SarNode(null,shl,shf.unkeep());
}
- private void antiDeps(Node mem, ArrayList<Node> deps, BitSet visit) {
- if( visit.get(mem._nid) ) return;
- visit.set(mem._nid);
- for( Node use : mem._outputs ) {
- if( use instanceof MemMergeNode merge ) {
- Node slice = _alias<merge.nIns() ? merge.in(_alias) : null;
- if( (slice==mem) || (slice==null && merge.in(1)==mem) )
- antiDeps(merge,deps,visit);
- } else if( use instanceof CallNode ||
- (use instanceof StoreNode st && st._alias==_alias) ||
- (use instanceof NewNode nnn && nnn.field(_alias)!=null) ||
- (use instanceof MemPhiNode phi && phi._alias==_alias) ||
- (use instanceof BulkMemPhiNode phi && !phi.isSplit(_alias)) ) {
- if( !deps.contains(use) ) deps.add(use);
- }
- }
- }
}
diff --git a/src/main/java/com/seaofnodes/simple/node/LoopNode.java b/src/main/java/com/seaofnodes/simple/node/LoopNode.java
index f26df076..db353c1d 100644
--- a/src/main/java/com/seaofnodes/simple/node/LoopNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/LoopNode.java
@@ -10,6 +10,7 @@ import java.util.HashSet;
public class LoopNode extends RegionNode {
public LoopNode( Parser.Lexer loc, Node entry ) { super(loc,null,entry,null); }
+ public LoopNode( LoopNode loop ) { super(loop); }
public CFGNode entry() { return cfg(1); }
public CFGNode back () { return cfg(2); }
diff --git a/src/main/java/com/seaofnodes/simple/node/MachConcreteNode.java b/src/main/java/com/seaofnodes/simple/node/MachConcreteNode.java
new file mode 100644
index 00000000..c1f007e8
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/MachConcreteNode.java
@@ -0,0 +1,27 @@
+package com.seaofnodes.simple.node;
+
+import com.seaofnodes.print.ExprPrinter;
+
+import com.seaofnodes.simple.type.Type;
+import com.seaofnodes.simple.Utils;
+import java.io.ByteArrayOutputStream;
+
+// Generic machine-specific class, has a few Node implementations that have to
+// exist (abstract) but are not useful past the optimizer.
+public abstract class MachConcreteNode extends Node implements MachNode{
+
+ public MachConcreteNode(Node node) { super(node); }
+ public MachConcreteNode(Node[]nodes) { super(nodes); }
+
+ @Override public String label() { return op(); }
+ @Override public Type compute () { throw Utils.TODO(); }
+ @Override public Node idealize() { throw Utils.TODO(); }
+
+ @Override protected ExprPrinter<Node> _print1(ExprPrinter<Node> p) {
+ p.open().p(op()).p(",");
+ for( int i=1; i<nIns(); i++ )
+ (in(i)==null ? p.p(" ---") : p.n(in(i))).p(",");
+ return p.unchar(',').close();
+ }
+
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/MachNode.java b/src/main/java/com/seaofnodes/simple/node/MachNode.java
new file mode 100644
index 00000000..9f2fee85
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/MachNode.java
@@ -0,0 +1,49 @@
+package com.seaofnodes.simple.node;
+
+import com.seaofnodes.simple.*;
+import java.io.ByteArrayOutputStream;
+
+public interface MachNode {
+
+ // Easy access to the abstract Machine
+ default Machine machine() { return CodeGen.CODE._mach; }
+
+ // Run a post-instruction-selection action
+ default void postSelect() { }
+
+ // Register mask allowed on input i. 0 for no register.
+ abstract public RegMask regmap(int i);
+ // Register mask allowed as a result. 0 for no register.
+ abstract public RegMask outregmap();
+ // Multi-reg-defining machine op; idx comes from Proj.
+ // Sometimes these Projs have no uses, and just exist to kill a register.
+ default RegMask outregmap(int idx) { throw Utils.TODO(); }
+
+ // If this op updates the same register as input 'idx', e.g. many X86 "R1 +=
+ // R2" style ops. This forces the output live range to equal the input live
+ // range, so they both get the same register.
+
+ // This can hold true for projections, if the instruction updates many
+ // registers that it also takes in. Note that ops also use the same
+ // registers for both inputs and outputs but these are pinned and without any
+ // choices (e.g. always RCX for both an input and output) and are better
+ // served with just declaring as a fixed register.
+
+ // Returns either 0 for not-two-adr or the updated input; for projections
+ // this is their Multi's updated input.
+ default int twoAddress( ) { return 0; }
+
+ // Instructions cheaper to recreate than to spill, such as loading small constants
+ default boolean isClone() { return false; }
+ // Encoding is appended into the byte array; size is returned
+ abstract public int encoding(ByteArrayOutputStream bytes);
+
+ // Human-readable form appended to the SB. Things like the encoding,
+ // indentation, leading address or block labels not printed here.
+ // Just something like "R17=[R18+12] // Load array base".
+ // General form: "dst=src+src".
+ default void asm(CodeGen code, SB sb) { throw Utils.TODO(); }
+
+ // Human readable opcode; e.g. "ld4" or "call".
+ default String op() { throw Utils.TODO(); }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/MemMergeNode.java b/src/main/java/com/seaofnodes/simple/node/MemMergeNode.java
index 5cb908bb..5f4cf128 100644
--- a/src/main/java/com/seaofnodes/simple/node/MemMergeNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/MemMergeNode.java
@@ -2,7 +2,7 @@ package com.seaofnodes.simple.node;
import com.seaofnodes.print.ExprPrinter;
-import com.seaofnodes.simple.IterPeeps;
+import com.seaofnodes.simple.CodeGen;
import com.seaofnodes.simple.type.Type;
import com.seaofnodes.simple.type.TypeMem;
@@ -10,6 +10,8 @@ import com.seaofnodes.simple.type.TypeMem;
* memory: absent aliases are not covered, rather than having unknown contents.
*/
public class MemMergeNode extends Node {
+ public MemMergeNode(MemMergeNode mem) { super(mem); }
+
public MemMergeNode(Node bulk) { super(null,bulk); _type = TypeMem.BOT; }
public MemMergeNode(Node bulk, int alias, Node precise) {
this(bulk);
@@ -30,7 +32,7 @@ public class MemMergeNode extends Node {
setDef(alias,mem);
}
- // Scalar contents of one alias. New's field inputs supply the initialized type;
+ // Scalar contents of one alias. New zeroes the allocated fields;
// join it with the incoming contents for all previously allocated objects.
// Phis use their cached types, so this query does not recurse around loops.
static Type contents(Node mem, int alias, Node dep) {
@@ -40,8 +42,7 @@ public class MemMergeNode extends Node {
if( mem instanceof ProjNode proj && proj.in(0) instanceof NewNode nnn ) {
assert proj._idx==1 && nnn.field(alias)!=null;
nnn.addDep(dep);
- Node init = nnn.in(nnn.findAlias(alias)).addDep(dep);
- return contents(nnn.mem(),alias,dep).meet(init._type);
+ return contents(nnn.mem(),alias,dep).meet(nnn.field(alias)._type.makeZero());
}
// Function parameters and call results may contain arbitrary heap values.
// Their bulk memory types do not imply empty or zero-filled storage.
@@ -68,7 +69,7 @@ public class MemMergeNode extends Node {
for( int i=2; i<nIns(); i++ ) {
if( in(i)!=null && in(i)==in(1) ) { setDef(i,null); progress=true; }
if( in(i) instanceof MemMergeNode mem ) {
- setDef(i,IterPeeps.add(mem.alias(i)));
+ setDef(i,CodeGen.CODE.add(mem.alias(i)));
progress=true;
}
if( in(i)!=null ) allDefault=false;
@@ -79,7 +80,7 @@ public class MemMergeNode extends Node {
for( int i=2; i<mem.nIns(); i++ )
if( mem.in(i)!=null && (i>=nIns() || in(i)==null) )
alias(i,mem.in(i));
- IterPeeps.add(mem.in(1));
+ CodeGen.CODE.add(mem.in(1));
setDef(1,mem.in(1));
return this;
}
diff --git a/src/main/java/com/seaofnodes/simple/node/MemOpNode.java b/src/main/java/com/seaofnodes/simple/node/MemOpNode.java
index c09f2725..e9fccc74 100644
--- a/src/main/java/com/seaofnodes/simple/node/MemOpNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/MemOpNode.java
@@ -1,9 +1,14 @@
package com.seaofnodes.simple.node;
+import com.seaofnodes.print.ExprPrinter;
+
import com.seaofnodes.simple.Parser;
import com.seaofnodes.simple.Utils;
import com.seaofnodes.simple.type.Type;
import com.seaofnodes.simple.type.TypeMemPtr;
+import java.lang.StringBuilder;
+import java.util.BitSet;
+import java.util.ArrayList;
/**
* Convenience common base for Load and Store.
@@ -29,6 +34,13 @@ public abstract class MemOpNode extends Node {
this(loc,name, alias, glb, mem, ptr, off);
addDef(value);
}
+ public MemOpNode( Node mach, MemOpNode mop ) {
+ super(mach);
+ _name = mop._name;
+ _alias = mop._alias;
+ _declaredType = mop._declaredType;
+ _loc = mop._loc;
+ }
//
static String mlabel(String name) { return name.equals("[]") ? "ary" : (name.equals("#") ? "len" : name); }
@@ -38,6 +50,10 @@ public abstract class MemOpNode extends Node {
public Node ptr() { return in(2); }
public Node off() { return in(3); }
+ @Override protected ExprPrinter<Node> _print1(ExprPrinter<Node> p) { return _printMach(p); }
+ protected ExprPrinter<Node> _printMach(ExprPrinter<Node> p) { throw Utils.TODO(); }
+
+
// Extra conditions for factoring matching memory operations through a Phi.
// The caller has already checked the opcode, input shape and control.
boolean canDrop(MemOpNode other, Node dep) {
@@ -65,4 +81,29 @@ public abstract class MemOpNode extends Node {
return null;
return Parser.error( "Might be null accessing '" + _name + "'",_loc);
}
+ // Memory writers which must follow this read. A packaging node is not a
+ // write, but can hide New's input, so follow this alias through aggregates.
+ public ArrayList<Node> antiDeps() {
+ ArrayList<Node> deps = new ArrayList<>();
+ antiDeps(mem(),deps,new BitSet());
+ return deps;
+ }
+
+ private void antiDeps(Node mem, ArrayList<Node> deps, BitSet visit) {
+ if( visit.get(mem._nid) ) return;
+ visit.set(mem._nid);
+ for( Node use : mem._outputs ) {
+ if( use instanceof MemMergeNode merge ) {
+ Node slice = _alias<merge.nIns() ? merge.in(_alias) : null;
+ if( (slice==mem) || (slice==null && merge.in(1)==mem) )
+ antiDeps(merge,deps,visit);
+ } else if( use instanceof CallNode ||
+ (use instanceof MemOpNode st && st.isMem() && st._alias==_alias) ||
+ (use instanceof NewNode nnn && nnn.field(_alias)!=null) ||
+ (use instanceof MemPhiNode phi && phi._alias==_alias) ||
+ (use instanceof BulkMemPhiNode phi && !phi.isSplit(_alias)) ) {
+ if( !deps.contains(use) ) deps.add(use);
+ }
+ }
+ }
}
diff --git a/src/main/java/com/seaofnodes/simple/node/MemPhiNode.java b/src/main/java/com/seaofnodes/simple/node/MemPhiNode.java
index 693ad944..38cbcd5b 100644
--- a/src/main/java/com/seaofnodes/simple/node/MemPhiNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/MemPhiNode.java
@@ -1,6 +1,6 @@
package com.seaofnodes.simple.node;
-import com.seaofnodes.simple.IterPeeps;
+import com.seaofnodes.simple.CodeGen;
import com.seaofnodes.simple.type.Type;
import com.seaofnodes.simple.type.TypeMem;
@@ -14,6 +14,8 @@ public class MemPhiNode extends PhiNode {
_alias = alias;
}
+ public MemPhiNode(MemPhiNode phi) { super(phi); _alias = phi._alias; }
+
@Override public String label() { return "MemPhi_"+_alias; }
@Override public Type compute() {
@@ -31,7 +33,7 @@ public class MemPhiNode extends PhiNode {
@Override public Node idealize() {
for( int i=1; i<nIns(); i++ )
if( in(i) instanceof MemMergeNode mem ) {
- setDef(i,IterPeeps.add(mem.alias(_alias)));
+ setDef(i,CodeGen.CODE.add(mem.alias(_alias)));
return this;
}
if( nIns()==3 && region() instanceof LoopNode loop && !loop.inProgress() &&
diff --git a/src/main/java/com/seaofnodes/simple/node/MulNode.java b/src/main/java/com/seaofnodes/simple/node/MulNode.java
index 2c1ba3de..720f15d7 100644
--- a/src/main/java/com/seaofnodes/simple/node/MulNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/MulNode.java
@@ -34,15 +34,33 @@ public class MulNode extends Node {
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 && lhs._type instanceof TypeInteger )
- return lhs;
-
// Move constants to RHS: con*arg becomes arg*con
if ( t1.isConstant() && !t2.isConstant() )
return swap12();
+ // Multiply by constant
+ if ( t2.isConstant() && t2 instanceof TypeInteger i ) {
+ // Mul of 1. We do not check for (1*x) because this will already
+ // canonicalize to (x*1)
+ long c = i.value();
+ if( c==1 ) return lhs._type instanceof TypeInteger ? lhs : null;
+ if( c==0 ) return Parser.ZERO;
+ // Mul by a power of 2, +/-1. Bit patterns more complex than this
+ // are unlikely to win on an X86 vs the normal "imul", and so
+ // become machine-specific.
+ if( (c & (c-1)) == 0 )
+ return new ShlNode(null,lhs,con(Long.numberOfTrailingZeros(c)));
+ // 2**n + 1, e.g. 9
+ long d = c-1;
+ if( (d & (d-1)) == 0 )
+ return new AddNode(new ShlNode(null,lhs,con(Long.numberOfTrailingZeros(d))).peephole(),lhs);
+ // 2**n - 1, e.g. 31
+ long e = c+1;
+ if( (e & (e-1)) == 0 )
+ return new SubNode(new ShlNode(null,lhs,con(Long.numberOfTrailingZeros(e))).peephole(),lhs);
+
+ }
+
// Do we have ((x * (phi cons)) * con) ?
// Do we have ((x * (phi cons)) * (phi cons)) ?
// Push constant up through the phi: x * (phi con0*con0 con1*con1...)
diff --git a/src/main/java/com/seaofnodes/simple/node/MultiUse.java b/src/main/java/com/seaofnodes/simple/node/MultiUse.java
deleted file mode 100644
index 436d3432..00000000
--- a/src/main/java/com/seaofnodes/simple/node/MultiUse.java
+++ /dev/null
@@ -1,5 +0,0 @@
-package com.seaofnodes.simple.node;
-
-public interface MultiUse {
- abstract int idx();
-}
diff --git a/src/main/java/com/seaofnodes/simple/node/NewNode.java b/src/main/java/com/seaofnodes/simple/node/NewNode.java
index cd639d7e..68871918 100644
--- a/src/main/java/com/seaofnodes/simple/node/NewNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/NewNode.java
@@ -4,7 +4,7 @@ import com.seaofnodes.print.ExprPrinter;
import com.seaofnodes.simple.type.*;
-/** Allocate and initialize an object. Inputs {ctrl, $mem, size, fields...};
+/** Allocate a zeroed object. Inputs {ctrl, $mem, size};
* results {ptr, $mem}.
* The memory input and result cover only the aliases in the allocated struct.
*/
@@ -16,11 +16,10 @@ public class NewNode extends Node implements MultiNode {
super(nodes);
assert !ptr.nullable();
_ptr = ptr;
- assert nodes.length==3+ptr._obj._fields.length;
+ assert nodes.length==3;
assert nodes[0]._type==Type.CONTROL || nodes[0]._type==Type.XCONTROL;
assert nodes[1]._type instanceof TypeMem;
assert nodes[2]._type instanceof TypeInteger || nodes[2]._type==Type.NIL;
- for( int i=3; i<nodes.length; i++ ) assert nodes[i]._type!=null;
}
public Node mem() { return in(1); }
@@ -32,18 +31,14 @@ public class NewNode extends Node implements MultiNode {
return null;
}
- @Override public String label() {
- return "new_"+(_ptr._obj.isAry() ? "ary_"+_ptr._obj._fields[1]._type.str() : _ptr._obj.str());
- }
+ public NewNode(NewNode nnn) { super(nnn); _ptr = nnn._ptr; }
+
+
+ @Override public String label() { return "new_"+(_ptr._obj.isAry() ? "ary_"+_ptr._obj._fields[1]._type.str() : _ptr._obj.str()); }
@Override protected ExprPrinter<Node> _print1(ExprPrinter<Node> p) {
return p.p("new ").p(_ptr._obj.str());
}
- // Find matching alias input
- int findAlias(int alias) {
- return 3+_ptr._obj.findAlias(alias);
- }
-
@Override
public TypeTuple compute() {
diff --git a/src/main/java/com/seaofnodes/simple/node/Node.java b/src/main/java/com/seaofnodes/simple/node/Node.java
index 114a652a..dd37b051 100644
--- a/src/main/java/com/seaofnodes/simple/node/Node.java
+++ b/src/main/java/com/seaofnodes/simple/node/Node.java
@@ -3,12 +3,13 @@ package com.seaofnodes.simple.node;
import com.seaofnodes.print.BaseNode;
import com.seaofnodes.simple.CodeGen;
-
import com.seaofnodes.simple.*;
import com.seaofnodes.simple.type.Type;
import com.seaofnodes.simple.type.TypeFloat;
+import com.seaofnodes.simple.type.TypeInteger;
import java.util.*;
import java.util.function.Function;
+import static com.seaofnodes.simple.CodeGen.CODE;
/**
* All Nodes in the Sea of Nodes IR inherit from the Node class.
@@ -43,16 +44,8 @@ public abstract class Node extends BaseNode<Node> implements Cloneable {
*/
public Type _type;
- /**
- * A private Global Static mutable counter, for unique node id generation.
- * To make the compiler multithreaded, this field will have to move into a TLS.
- * Starting with value 1, to avoid bugs confusing node ID 0 with uninitialized values.
- * */
- private static int UNIQUE_ID = 1;
- public static int UID() { return UNIQUE_ID; }
-
- protected Node(Node... inputs) {
- super(UNIQUE_ID++); // allocate unique dense ID
+ Node(Node... inputs) {
+ super(CODE.getUID()); // allocate unique dense ID
_inputs = new Ary<>(Node.class);
Collections.addAll(_inputs,inputs);
_outputs = new Ary<>(Node.class);
@@ -61,6 +54,18 @@ public abstract class Node extends BaseNode<Node> implements Cloneable {
n.addUse( this );
}
+ // Make a Node using the existing arrays of nodes.
+ // Used by any pass rewriting all Node classes but not the edges.
+ Node( Node n ) {
+ super(CODE.getUID()); // allocate unique dense ID
+ assert CodeGen.CODE._phase == CodeGen.Phase.InstructionSelection;
+ _inputs = new Ary<>(n._inputs.asAry());
+ _outputs = new Ary<>(Node.class);
+ _type = n._type;
+ _deps = null;
+ _hash = 0;
+ }
+
// Easy reading label for debugger, e.g. "Add" or "Region" or "EQ"
public abstract String label();
@@ -119,10 +124,10 @@ public abstract class Node extends BaseNode<Node> implements Cloneable {
new_def.addUse(this);
// Set the new_def over the old (killed) edge
_inputs.set(idx,new_def);
- if( old_def != null ) { // If the old def exists, remove a def->use edge
- if( old_def.delUse(this) ) // If we removed the last use, the old def is now dead
- old_def.kill(); // Kill old def
- else IterPeeps.add(old_def);// Else old lost a use, so onto worklist
+ if( old_def != null ) { // If the old def exists, remove a def->use edge
+ if( old_def.delUse(this) ) // If we removed the last use, the old def is now dead
+ old_def.kill(); // Kill old def
+ else CODE.add(old_def); // Else old lost a use, so onto worklist
}
moveDepsToWorklist();
// Return new_def for easy flow-coding
@@ -202,11 +207,9 @@ public abstract class Node extends BaseNode<Node> implements Cloneable {
_type=null; // Flag as dead
while( nIns()>0 ) { // Set all inputs to null, recursively killing unused Nodes
Node old_def = _inputs.removeLast();
- if( old_def != null ) {
- IterPeeps.add(old_def);// Revisit neighbor because removed use
- if( old_def.delUse(this) ) // If we removed the last use, the old def is now dead
- old_def.kill(); // Kill old def
- }
+ // Revisit neighbor because removed use
+ if( old_def != null && CODE.add(old_def).delUse(this) )
+ old_def.kill(); // If we removed the last use, the old def is now dead
}
assert isDead(); // Really dead now
}
@@ -236,7 +239,7 @@ public abstract class Node extends BaseNode<Node> implements Cloneable {
n._inputs.set(idx,nnn);
n.moveDepsToWorklist(); // Rewiring can change a dependent query without changing type.
nnn.addUse(n);
- IterPeeps.addAll(n._outputs);
+ CODE.addAll(n._outputs);
}
kill();
}
@@ -249,7 +252,7 @@ public abstract class Node extends BaseNode<Node> implements Cloneable {
* Always returns some not-null Node (often this).
*/
public final Node peephole() {
- var obs = CodeGen.CODE == null || IterPeeps.midAssert() || CodeGen.CODE._phase == CodeGen.Phase.Parse && CodeGen.CODE.P == null ? null : CodeGen.CODE._obs;
+ var obs = CodeGen.CODE._midAssert ? null : CodeGen.CODE._obs;
if( obs != null ) obs.before(this);
Node n = peepholeOpt();
Node rez = n == null ? this : deadCodeElim(n._nid >= _nid ? n.peephole() : n);
@@ -276,19 +279,22 @@ public abstract class Node extends BaseNode<Node> implements Cloneable {
* </ul>
*/
public final Node peepholeOpt( ) {
- ITER_CNT++;
+ CODE.iterCnt();
// Compute initial or improved Type
Type old = setType(compute());
- // Replace constant computations from non-constants with a constant node.
+ // Replace constant computations from non-constants with a constant
+ // node. If peeps are disabled, still allow high Phis to collapse;
+ // they typically come from dead Regions, and we want the Region to
+ // collapse, which requires the Phis to die first.
if( _type.isHighOrConst() && !isConst() )
- return ConstantNode.make(_type).peepholeOpt();
+ return ConstantNode.make(_type).peephole();
// Global Value Numbering
if( _hash==0 ) {
- Node n = GVN.get(this); // Will set _hash as a side effect
+ Node n = CODE._gvn.get(this); // Will set _hash as a side effect
if( n==null )
- GVN.put(this,this); // Put in table now
+ CODE._gvn.put(this,this); // Put in table now
else {
// Because of random worklist ordering, the two equal nodes
// might have different types. Because of monotonicity, both
@@ -306,10 +312,9 @@ public abstract class Node extends BaseNode<Node> implements Cloneable {
return n; // Report progress
if( old!=_type ) return this; // Report progress;
- ITER_NOP_CNT++;
+ CODE.iterNop();
return null; // No progress
}
- public static int ITER_CNT, ITER_NOP_CNT;
// m is the new Node, self is the old.
// Return 'm', which may have zero uses but is alive nonetheless.
@@ -353,7 +358,7 @@ public abstract class Node extends BaseNode<Node> implements Cloneable {
assert old==null || type.isa(old); // Since _type not set, can just re-run this in assert in the debugger
if( old == type ) return old;
_type = type; // Set _type late for easier assert debugging
- IterPeeps.addAll(_outputs);
+ CODE.addAll(_outputs);
moveDepsToWorklist();
return old;
}
@@ -432,9 +437,10 @@ public abstract class Node extends BaseNode<Node> implements Cloneable {
private Node addDep(Node dep, boolean forwards) {
// Running peepholes during the big assert cannot have side effects
// like adding dependencies.
- if( IterPeeps.midAssert() ) return this;
- var obs = CodeGen.CODE == null || IterPeeps.midAssert() || CodeGen.CODE._phase == CodeGen.Phase.Parse && CodeGen.CODE.P == null ? null : CodeGen.CODE._obs;
+ if( CODE._midAssert ) return this;
+ var obs = CodeGen.CODE._midAssert ? null : CodeGen.CODE._obs;
if( obs != null ) obs.dep(this, dep);
+ if( dep == null ) return this;
if( _deps==null ) _deps = new Ary<>(Node.class);
if( _deps .find(dep) != -1 ) return this; // Already on list
if( !forwards && _inputs .find(dep) != -1 ) return this; // No need for deps on immediate neighbors
@@ -446,14 +452,10 @@ public abstract class Node extends BaseNode<Node> implements Cloneable {
// Move the dependents onto a worklist, and clear for future dependents.
public void moveDepsToWorklist( ) {
if( _deps==null ) return;
- IterPeeps.addAll(_deps);
+ CODE.addAll(_deps);
_deps.clear();
}
- // Global Value Numbering. Hash over opcode and inputs; hits in this table
- // are structurally equal.
- public static final HashMap<Node,Node> GVN = new HashMap<>();
-
// Two nodes are equal if they have the same inputs and the same "opcode"
// which means the same Java class, plus same internal parts.
@Override public final boolean equals( Object o ) {
@@ -481,7 +483,7 @@ public abstract class Node extends BaseNode<Node> implements Cloneable {
// If the _hash is set, then the Node is in the GVN table; remove it.
void unlock() {
if( _hash==0 ) return;
- Node old = GVN.remove(this); // Pull from table
+ Node old = CODE._gvn.remove(this); // Pull from table
assert old==this;
_hash=0; // Out of table now
}
@@ -562,7 +564,7 @@ public abstract class Node extends BaseNode<Node> implements Cloneable {
Node n;
try { n = (Node)clone(); }
catch( Exception e ) { throw new RuntimeException(e); }
- n._nid = UNIQUE_ID++; // allocate unique dense ID
+ n._nid = CODE.getUID(); // allocate unique dense ID
n._inputs = new Ary<>(Node.class);
n._outputs = new Ary<>(Node.class);
n._deps = null;
@@ -573,28 +575,21 @@ public abstract class Node extends BaseNode<Node> implements Cloneable {
// Report any post-optimize errors
public Parser.ParseException err() { return null; }
- /**
- * 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.
- */
- public static void reset() {
- UNIQUE_ID = 1;
- GVN.clear();
- ITER_CNT = ITER_NOP_CNT = 0;
- }
+ // Common integer constants
+ public static ConstantNode con(long x) { return (ConstantNode)(new ConstantNode(TypeInteger.constant(x)).peephole()); }
// Utility to walk the entire graph applying a function; return the first
// not-null result.
- private static final BitSet WVISIT = new BitSet();
final public <E> E walk( Function<Node,E> pred ) {
- assert WVISIT.isEmpty();
- try { return _walk(pred); }
- finally { WVISIT.clear(); }
+ assert CODE._visit.isEmpty();
+ E rez = _walk(pred);
+ CODE._visit.clear();
+ return rez;
}
private <E> E _walk( Function<Node,E> pred ) {
- if( WVISIT.get(_nid) ) return null; // Been there, done that
- WVISIT.set(_nid);
+ if( CODE._visit.get(_nid) ) return null; // Been there, done that
+ CODE._visit.set(_nid);
E x = pred.apply(this);
if( x != null ) return x;
for( Node def : _inputs ) if( def != null && (x = def._walk(pred)) != null ) return x;
diff --git a/src/main/java/com/seaofnodes/simple/node/ParmNode.java b/src/main/java/com/seaofnodes/simple/node/ParmNode.java
index 7f5924cc..c3a0cf21 100644
--- a/src/main/java/com/seaofnodes/simple/node/ParmNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/ParmNode.java
@@ -14,6 +14,7 @@ public class ParmNode extends PhiNode {
super(label,declaredType,inputs);
_idx = idx;
}
+ public ParmNode(ParmNode parm) { super(parm, parm._label, parm._declaredType); _idx = parm._idx; }
@Override public String label() { return MemOpNode.mlabel(_label); }
@@ -21,7 +22,7 @@ public class ParmNode extends PhiNode {
@Override
protected ExprPrinter<Node> _print1(ExprPrinter<Node> p) {
- if( "main".equals(fun().sig()._name) && _label.equals("arg") )
+ if( "main".equals(fun()._name) && _label.equals("arg") )
return p.p("arg");
p.p("Parm_").p(_label).open();
for( Node in : _inputs ) p.n(in).p(",");
diff --git a/src/main/java/com/seaofnodes/simple/node/PhiNode.java b/src/main/java/com/seaofnodes/simple/node/PhiNode.java
index 9b5e1c26..6fb315b4 100644
--- a/src/main/java/com/seaofnodes/simple/node/PhiNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/PhiNode.java
@@ -14,9 +14,11 @@ public class PhiNode extends Node {
final Type _declaredType;
public PhiNode(String label, Type declaredType, Node... inputs) { super(inputs); _label = label; assert declaredType!=null; _declaredType = declaredType; }
+ public PhiNode(PhiNode phi, String label, Type declaredType) { super(phi); _label = label; _declaredType = declaredType; }
+ public PhiNode(PhiNode phi) { super(phi); _label = phi._label; _declaredType = phi._declaredType; }
public PhiNode(RegionNode r, Node sample) {
- super(r);
+ super(new Node[]{r});
_label = "";
_declaredType = sample._type;
while( nIns() < r.nIns() )
diff --git a/src/main/java/com/seaofnodes/simple/node/ProjNode.java b/src/main/java/com/seaofnodes/simple/node/ProjNode.java
index 1e192676..6ae4a60d 100644
--- a/src/main/java/com/seaofnodes/simple/node/ProjNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/ProjNode.java
@@ -7,7 +7,7 @@ import com.seaofnodes.simple.type.TypeMem;
import com.seaofnodes.simple.type.TypeTuple;
-public class ProjNode extends Node implements MultiUse {
+public class ProjNode extends Node {
// Which slice of the incoming multipart value
public final int _idx;
@@ -16,13 +16,13 @@ public class ProjNode extends Node implements MultiUse {
public final String _label;
public ProjNode(Node ctrl, int idx, String label) {
- super(ctrl);
+ super(new Node[]{ctrl});
_idx = idx;
_label = label;
}
+ public ProjNode(ProjNode p) { super(p); _idx = p._idx; _label = p._label; }
@Override public String label() { return _label; }
- @Override public int idx() { return _idx; }
@Override
protected ExprPrinter<Node> _print1(ExprPrinter<Node> p) {
diff --git a/src/main/java/com/seaofnodes/simple/node/ReadOnlyNode.java b/src/main/java/com/seaofnodes/simple/node/ReadOnlyNode.java
index fa4e0242..d79f2815 100644
--- a/src/main/java/com/seaofnodes/simple/node/ReadOnlyNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/ReadOnlyNode.java
@@ -10,6 +10,7 @@ import com.seaofnodes.simple.type.TypeMemPtr;
*/
public class ReadOnlyNode extends Node {
public ReadOnlyNode( Node n ) { super(null,n); }
+ public ReadOnlyNode( ReadOnlyNode n ) { super(n); }
@Override public String label() { return "ReadOnly"; }
@Override protected String format() { return "(const)%1"; }
diff --git a/src/main/java/com/seaofnodes/simple/node/RegionNode.java b/src/main/java/com/seaofnodes/simple/node/RegionNode.java
index 945398e9..e1f095a9 100644
--- a/src/main/java/com/seaofnodes/simple/node/RegionNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/RegionNode.java
@@ -2,20 +2,17 @@ package com.seaofnodes.simple.node;
import com.seaofnodes.print.ExprPrinter;
-import com.seaofnodes.simple.IterPeeps;
-import com.seaofnodes.simple.Parser;
-import com.seaofnodes.simple.Utils;
+import com.seaofnodes.simple.*;
import com.seaofnodes.simple.type.Type;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashSet;
+import java.util.*;
public class RegionNode extends CFGNode {
// Source location for late discovered errors
public Parser.Lexer _loc;
- public RegionNode(Node... nodes) { this(null,nodes); }
- public RegionNode(Parser.Lexer loc, Node... nodes) { super(nodes); _loc = loc; }
+ public RegionNode(Parser.Lexer loc, Node... nodes) { super(nodes); _loc = loc; }
+ public RegionNode(RegionNode r, Parser.Lexer loc) { super(r); _loc = loc; }
+ public RegionNode(RegionNode r) { super(r); _loc = r._loc; }
@Override
public String label() { return "Region"; }
@@ -39,30 +36,10 @@ public class RegionNode extends CFGNode {
@Override
public Node idealize() {
if( inProgress() ) return null;
- // Delete dead paths into a Region
- int path = findDeadInput();
- if( path != 0 &&
- // Do not delete the entry path of a loop (ok to remove the back
- // edge and make the loop a single-entry Region which folds away
- // the Loop). Folding the entry path confused the loop structure,
- // moving the backedge to the entry point.
- !(this instanceof LoopNode loop && loop.entry()==in(path)) ) {
- // Cannot use the obvious output iterator here, because a Phi
- // deleting an input might recursively delete *itself*. This
- // shuffles the output array, and we might miss iterating an
- // unrelated Phi. So on rare occasions we repeat the loop to get
- // all the Phis.
- int nouts = 0;
- while( nouts != nOuts() ) {
- nouts = nOuts();
- for( int i=0; i<nOuts(); i++ )
- if( out(i) instanceof PhiNode phi && phi.nIns()==nIns() ) {
- phi.delDef(path);
- IterPeeps.addAll(phi._outputs);
- }
- }
- return isDead() ? Parser.XCTRL : delDef(path);
- }
+
+ Node progress = deadPath();
+ if( progress!=null ) return progress;
+
// If down to a single input, become that input
if( nIns()==2 && !hasPhi() )
return in(1); // Collapse if no Phis; 1-input Phis will collapse on their own
@@ -83,6 +60,31 @@ public class RegionNode extends CFGNode {
return null;
}
+ Node deadPath() {
+ // Delete dead paths into a Region
+ int path = findDeadInput();
+ if( path==0 ) return null;
+ // Do not delete the entry path of a loop (ok to remove the back edge
+ // and make the loop a single-entry Region which folds away the Loop).
+ // Folding the entry path confused the loop structure, moving the
+ // backedge to the entry point.
+ if( this instanceof LoopNode loop && loop.entry()==in(path) )
+ return null;
+ // Cannot use the obvious output iterator here, because a Phi deleting
+ // an input might recursively delete *itself*. This shuffles the
+ // output array, and we might miss iterating an unrelated Phi. So on
+ // rare occasions we repeat the loop to get all the Phis.
+ int nouts = 0;
+ while( nouts != nOuts() ) {
+ nouts = nOuts();
+ for( int i=0; i<nOuts(); i++ )
+ if( out(i) instanceof PhiNode phi && phi.nIns()==nIns() )
+ CodeGen.CODE.addAll(phi.delDef(path)._outputs);
+ }
+ return isDead() ? Parser.XCTRL : delDef(path);
+ }
+
+
private int findDeadInput() {
for( int i=1; i<nIns(); i++ )
if( in(i)._type==Type.XCONTROL )
diff --git a/src/main/java/com/seaofnodes/simple/node/ReturnNode.java b/src/main/java/com/seaofnodes/simple/node/ReturnNode.java
index d4b4094d..0716aa36 100644
--- a/src/main/java/com/seaofnodes/simple/node/ReturnNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/ReturnNode.java
@@ -10,12 +10,13 @@ import com.seaofnodes.simple.type.*;
/** Keeps all preceding memory effects and the return value alive: {ctrl, $mem, value, rpc}. */
public class ReturnNode extends CFGNode {
- public final FunNode _fun;
+ public FunNode _fun;
public ReturnNode(Node ctrl, Node mem, Node data, Node rpc, FunNode fun ) {
super(ctrl, mem, data, rpc);
_fun = fun;
}
+ public ReturnNode( ReturnNode ret, FunNode fun ) { super(ret); _fun = fun; }
public Node ctrl() { return in(0); }
public Node mem () { return in(1); }
diff --git a/src/main/java/com/seaofnodes/simple/node/ScopeNode.java b/src/main/java/com/seaofnodes/simple/node/ScopeNode.java
index de199d2d..881f65d6 100644
--- a/src/main/java/com/seaofnodes/simple/node/ScopeNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/ScopeNode.java
@@ -271,7 +271,7 @@ public class ScopeNode extends Node {
RegionNode r = ctrl(new RegionNode(loc,null,ctrl(), that.ctrl()).keep());
_merge(that,r);
that.kill(); // Kill merged scope
- IterPeeps.add(r);
+ CodeGen.CODE.add(r);
return r.unkeep();
}
@@ -336,7 +336,7 @@ public class ScopeNode extends Node {
if( in(i) instanceof PhiNode phi ) {
// Do an eager useless-phi removal
Node in = phi.peephole();
- IterPeeps.addAll(phi._outputs);
+ CodeGen.CODE.addAll(phi._outputs);
phi.moveDepsToWorklist();
if( in != phi ) {
if( !phi.iskeep() ) // Keeping phi around for parser elsewhere
@@ -370,7 +370,7 @@ public class ScopeNode extends Node {
}
// Invert the If conditional
if( invert )
- pred = pred instanceof NotNode not ? not.in(1) : IterPeeps.add(new NotNode(pred).peephole());
+ pred = pred instanceof NotNode not ? not.in(1) : CodeGen.CODE.add(new NotNode(pred).peephole());
// This is a zero/null test.
// Compute the positive test type.
Type tnz = pred._type.nonZero();
diff --git a/src/main/java/com/seaofnodes/simple/node/StartNode.java b/src/main/java/com/seaofnodes/simple/node/StartNode.java
index 8bdb60cf..d1151090 100644
--- a/src/main/java/com/seaofnodes/simple/node/StartNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/StartNode.java
@@ -2,6 +2,8 @@ package com.seaofnodes.simple.node;
import com.seaofnodes.print.ExprPrinter;
+import com.seaofnodes.simple.CodeGen;
+import com.seaofnodes.simple.Parser;
import com.seaofnodes.simple.type.*;
import java.util.HashSet;
import static com.seaofnodes.simple.Utils.TODO;
@@ -11,7 +13,8 @@ public class StartNode extends LoopNode implements MultiNode {
final Type _arg;
- public StartNode(Type arg) { super(null,null); _arg = arg; _type = compute(); }
+ public StartNode(Type arg) { super((Parser.Lexer)null,null); _arg = arg; _type = compute(); }
+ public StartNode(StartNode start) { super(start); _arg = start._arg; }
@Override public String label() { return "Start"; }
@@ -29,7 +32,7 @@ public class StartNode extends LoopNode implements MultiNode {
// Find "main", its the start.
CFGNode C = null;
for( Node use : _outputs )
- if( use instanceof FunNode fun && fun.sig().isa(TypeFunPtr.MAIN) )
+ if( use instanceof FunNode fun && fun.sig().isa(CodeGen.CODE._main) )
{ assert C==null; C = fun; }
return C;
}
diff --git a/src/main/java/com/seaofnodes/simple/node/StopNode.java b/src/main/java/com/seaofnodes/simple/node/StopNode.java
index a15125a1..01199e9b 100644
--- a/src/main/java/com/seaofnodes/simple/node/StopNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/StopNode.java
@@ -14,6 +14,7 @@ public class StopNode extends CFGNode {
_src = src;
_type = compute();
}
+ public StopNode(StopNode stop) { super(stop); _src = stop._src; }
@Override
public String label() {
diff --git a/src/main/java/com/seaofnodes/simple/node/StoreNode.java b/src/main/java/com/seaofnodes/simple/node/StoreNode.java
index de17bbad..86c759bd 100644
--- a/src/main/java/com/seaofnodes/simple/node/StoreNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/StoreNode.java
@@ -2,10 +2,7 @@ package com.seaofnodes.simple.node;
import com.seaofnodes.print.ExprPrinter;
-import com.seaofnodes.simple.IterPeeps;
-import com.seaofnodes.simple.Parser;
-import com.seaofnodes.simple.Utils;
-import com.seaofnodes.simple.IterPeeps;
+import com.seaofnodes.simple.*;
import com.seaofnodes.simple.type.*;
/**
@@ -55,7 +52,7 @@ public class StoreNode extends MemOpNode {
@Override
public Node idealize() {
if( mem() instanceof MemMergeNode merge ) {
- setDef(1,IterPeeps.add(merge.alias(_alias)));
+ setDef(1,CodeGen.CODE.add(merge.alias(_alias)));
return this;
}
@@ -74,33 +71,20 @@ public class StoreNode extends MemOpNode {
return this;
}
- // Simple store-after-new on same address. Should pick up
- // an init-store being stomped by a first user store.
- if( mem() instanceof ProjNode st && st .in(0) instanceof NewNode nnn &&
- ptr() instanceof ProjNode ptr && ptr.in(0) == nnn &&
- ptr()._type instanceof TypeMemPtr tmp && // No bother if weird dead pointers
- // Cannot fold a store of a single element over the array body initializer value
- !(tmp._obj.isAry() && tmp._obj._fields[1]._alias==_alias) &&
- // Very sad strong cutout: val has to be legal to hoist to a New
- // input, which means it cannot depend on the New. Many, many
- // things are legal here but difficult to check without doing a
- // full dominator check. Example failure:
- // "struct C { C? c; }; C self = new C { c=self; }"
- val()._type.isHighOrConst() &&
- // Must have exactly one use of "this" or you get weird
- // non-serializable memory effects in the worse case.
- checkOnlyUse(st) &&
- // Folding away a broken store
- err()==null ) {
- nnn.setDef(nnn.findAlias(_alias),val());
- // Must *retype* the NewNode, this is not monotonic in isolation
- // but is monotonic counting from this Store to the New.
- nnn ._type = nnn .compute();
- mem()._type = mem().compute();
- IterPeeps.addAll(nnn._outputs);
- return mem();
+ // Value is automatically truncated by narrow store
+ if( val() instanceof AndNode and && and.in(2)._type.isConstant() ) {
+ int log = _declaredType.log_size();
+ if( log<3 ) { // And-mask vs narrow store
+ long mask = ((TypeInteger)and.in(2)._type).value();
+ long bits = (1L<<(8<<log))-1;
+ // Mask does not mask any of the stored bits
+ if( (bits&mask)==bits )
+ // So and-mask is already covered by the store
+ { setDef(4,and.in(1)); return this; }
+ }
}
+
return null;
}
@@ -162,9 +146,10 @@ public class StoreNode extends MemOpNode {
if( ptr()._type == Type.TOP )
return null; // Dead store
TypeMemPtr tmp = (TypeMemPtr)ptr()._type;
- if( tmp._ro || (tmp._obj.field(_name)._final && !"[]".equals(_name)) )
+ if( (tmp._ro || tmp._obj.field(_name)._final) && !_init )
return Parser.error("Cannot modify final field '"+_name+"'",_loc);
Type t = val()._type;
- return _init || t.isa(_declaredType) ? null : Parser.error("Cannot store "+t+" into field "+_declaredType+" "+_name,_loc);
+ //return _init || t.isa(_declaredType) ? null : Parser.error("Cannot store "+t+" into field "+_declaredType+" "+_name,_loc);
+ return null;
}
}
diff --git a/src/main/java/com/seaofnodes/simple/node/XCtrlNode.java b/src/main/java/com/seaofnodes/simple/node/XCtrlNode.java
index 3eae8548..891291d0 100644
--- a/src/main/java/com/seaofnodes/simple/node/XCtrlNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/XCtrlNode.java
@@ -2,11 +2,12 @@ package com.seaofnodes.simple.node;
import com.seaofnodes.print.ExprPrinter;
+import com.seaofnodes.simple.CodeGen;
import com.seaofnodes.simple.Parser;
import com.seaofnodes.simple.type.Type;
public class XCtrlNode extends CFGNode {
- public XCtrlNode() { super(Parser.START); }
+ public XCtrlNode() { super(new Node[]{CodeGen.CODE._start}); }
@Override public String label() { return "Xctrl"; }
@Override protected ExprPrinter<Node> _print1(ExprPrinter<Node> p) { return p.p("Xctrl"); }
@Override public boolean isConst() { return true; }
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AddFRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AddFRISC.java
new file mode 100644
index 00000000..bc959977
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AddFRISC.java
@@ -0,0 +1,30 @@
+package com.seaofnodes.simple.node.cpus.riscv;
+
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.type.TypeInteger;
+import java.io.ByteArrayOutputStream;
+
+public class AddFRISC extends MachConcreteNode implements MachNode{
+ AddFRISC(Node addf) {super(addf);}
+
+ // Register mask allowed on input i.
+ @Override public RegMask regmap(int i) { assert i==1 || i==2; return riscv.FMASK; }
+ // Register mask allowed as a result. 0 for no register.
+ @Override public RegMask outregmap() { return riscv.FMASK; }
+ // Output is same register as input#1
+ @Override public int twoAddress() { return 1; }
+
+ // Encoding is appended into the byte array; size is returned
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+
+ // Default on double precision for now(64 bits)
+ // General form: "fadd.d rd = src1 + src2
+ @Override public void asm(CodeGen code, SB sb) {
+ sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" + ").p(code.reg(in(2)));
+ }
+
+ @Override public String op() { return "addf"; }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AddIRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AddIRISC.java
new file mode 100644
index 00000000..af4bf08d
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AddIRISC.java
@@ -0,0 +1,39 @@
+package com.seaofnodes.simple.node.cpus.riscv;
+
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.type.TypeInteger;
+import java.io.ByteArrayOutputStream;
+
+public class AddIRISC extends MachConcreteNode implements MachNode {
+ final TypeInteger _ti;
+ AddIRISC( Node add, TypeInteger ti ) {
+ super(add);
+ _inputs.pop();
+ _ti = ti;
+ }
+
+ // Register mask allowed on input i.
+ @Override public RegMask regmap(int i) {
+ // assert i== i;
+ return riscv.RMASK; }
+ // Register mask allowed as a result. 0 for no register.
+ @Override public RegMask outregmap() { return riscv.RMASK; }
+ // Output is same register as input#1
+ @Override public int twoAddress() { return 0; }
+
+ // Encoding is appended into the byte array; size is returned
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+
+ // General form: "addi rd = rs1 + imm"
+ @Override public void asm(CodeGen code, SB sb) {
+ sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" + #");
+ _ti.print(sb);
+ }
+
+ @Override public String op() {
+ return _ti.value() == 1 ? "inc" : (_ti.value() == -1 ? "dec" : "addi");
+ }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AddRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AddRISC.java
new file mode 100644
index 00000000..482b7f11
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AddRISC.java
@@ -0,0 +1,36 @@
+package com.seaofnodes.simple.node.cpus.riscv;
+
+
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.type.TypeInteger;
+import java.io.ByteArrayOutputStream;
+
+
+public class AddRISC extends MachConcreteNode implements MachNode {
+
+ AddRISC( Node add) {super(add); }
+
+ // Register mask allowed on input i.
+ @Override public RegMask regmap(int i) {
+ assert i== 1 || i == 2 || i == 3;
+ return riscv.RMASK;
+ }
+ // Register mask allowed as a result. 0 for no register.
+ @Override public RegMask outregmap() { return riscv.RMASK; }
+ // Output is same register as input#1
+ @Override public int twoAddress() { return 0; }
+
+ // Encoding is appended into the byte array; size is returned
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+
+ // General form: "rd = rs1 + rs2"
+ @Override public void asm(CodeGen code, SB sb) {
+ sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" + ").p(code.reg(in(2)));
+ }
+
+ @Override public String op() {
+ return "add";}
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AndIRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AndIRISC.java
new file mode 100644
index 00000000..29c2a686
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AndIRISC.java
@@ -0,0 +1,40 @@
+package com.seaofnodes.simple.node.cpus.riscv;
+
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.type.Type;
+import com.seaofnodes.simple.type.TypeInteger;
+import java.io.ByteArrayOutputStream;
+import java.util.BitSet;
+import java.lang.StringBuilder;
+
+public class AndIRISC extends MachConcreteNode implements MachNode{
+ final TypeInteger _ti;
+ AndIRISC(AndNode and, TypeInteger ti) {
+ super(and);
+ _inputs.pop();
+ _ti = ti;
+ }
+ // Register mask allowed on input i.
+ // This is the normal calling convention
+ @Override public RegMask regmap(int i) { assert i==1 || i == 2; return riscv.RMASK; }
+ // Register mask allowed as a result. 0 for no register.
+ @Override public RegMask outregmap() { return riscv.RMASK; }
+
+ // Output is same register as input#1
+ @Override public int twoAddress() { return 0; }
+
+ // Encoding is appended into the byte array; size is returned
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+
+ // General form
+ // General form: "andi rd = rs1 & imm"
+ @Override public void asm(CodeGen code, SB sb) {
+ sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" & #");
+ _ti.print(sb);
+ }
+
+ @Override public String op() { return "andi"; }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AndRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AndRISC.java
new file mode 100644
index 00000000..e697de36
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AndRISC.java
@@ -0,0 +1,41 @@
+package com.seaofnodes.simple.node.cpus.riscv;
+
+
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.node.cpus.x86_64_v2.x86_64_v2;
+import com.seaofnodes.simple.type.Type;
+import com.seaofnodes.simple.type.TypeInteger;
+import java.io.ByteArrayOutputStream;
+import java.util.BitSet;
+import java.lang.StringBuilder;
+
+public class AndRISC extends MachConcreteNode implements MachNode{
+ AndRISC(Node and) {
+ super(and);
+ }
+
+ // Register mask allowed on input i.
+ // This is the normal calling convention
+ @Override public RegMask regmap(int i) {
+ assert i==1 || i==2;
+ return riscv.RMASK;
+ }
+
+ // Register mask allowed as a result. 0 for no register.
+ @Override public RegMask outregmap() { return riscv.RMASK; }
+
+ // Output is same register as input#1
+ @Override public int twoAddress() { return 0; }
+
+ // Encoding is appended into the byte array; size is returned
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+
+ // General form
+ // General form: #rd = rs1 & rs2
+ @Override public void asm(CodeGen code, SB sb) {
+ sb.p(code.reg(this)).p(" = ").p(code.reg(in(1)));
+ }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CBranchRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CBranchRISC.java
new file mode 100644
index 00000000..d7e23c6d
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CBranchRISC.java
@@ -0,0 +1,49 @@
+package com.seaofnodes.simple.node.cpus.riscv;
+
+
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.type.Type;
+import com.seaofnodes.simple.type.TypeInteger;
+import java.io.ByteArrayOutputStream;
+// Conditional branch such as: BEQ
+public class CBranchRISC extends IfNode implements MachNode{
+ final String _bop;
+ // label is obtained implicitly
+ CBranchRISC( IfNode iff, String bop ) {
+ super(iff);
+ _bop = bop;
+ }
+
+ @Override public String label() { return op(); }
+
+ @Override public void postSelect() {
+ Node set = in(1);
+ Node cmp = set.in(1);
+ // Bypass an expected Set and just reference the cmp directly
+ if( set instanceof SetRISC)
+ _inputs.set(1,cmp);
+ else
+ throw Utils.TODO();
+ }
+
+ @Override public RegMask regmap(int i) { assert i==1; return riscv.RMASK; }
+ @Override public RegMask outregmap() { return null; }
+
+ // Encoding is appended into the byte array; size is returned
+
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+
+ @Override public void asm(CodeGen code, SB sb) {
+ String src = code.reg(in(1));
+ if( src!="FLAGS" ) sb.p(src);
+ }
+
+ @Override public String op() { return "b"+_bop; }
+
+ @Override public String comment() {
+ return "L"+cproj(1)._nid+", L"+cproj(0)._nid;
+ }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CallRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CallRISC.java
new file mode 100644
index 00000000..7f062104
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CallRISC.java
@@ -0,0 +1,37 @@
+package com.seaofnodes.simple.node.cpus.riscv;
+
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.type.TypeFunPtr;
+import java.io.ByteArrayOutputStream;
+
+public class CallRISC extends CallNode implements MachNode{
+ final TypeFunPtr _tfp;
+ final String _name;
+ CallRISC( CallNode call, TypeFunPtr tfp ) {
+ super(call);
+ _inputs.pop(); // Pop constant target
+ assert tfp.isConstant();
+ _tfp = tfp;
+ _name = CodeGen.CODE.link(tfp)._name;
+ }
+
+ @Override public String label() { return op(); }
+ @Override public RegMask regmap(int i) {
+ return riscv.callInMaskInt(i); // Normal argument
+ }
+ @Override public RegMask outregmap() { return riscv.RET_MASK; }
+
+ @Override public String name() { return _name; }
+
+ // Encoding is appended into the byte array; size is returned
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+
+ @Override public void asm(CodeGen code, SB sb) {
+ sb.p(_name);
+ }
+
+ @Override public String op() { return "call"; }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CallRRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CallRRISC.java
new file mode 100644
index 00000000..02850582
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CallRRISC.java
@@ -0,0 +1,31 @@
+package com.seaofnodes.simple.node.cpus.riscv;
+
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.node.*;
+
+import java.io.ByteArrayOutputStream;
+
+public class CallRRISC extends CallNode implements MachNode{
+ CallRRISC( CallNode call ) { super(call); }
+
+ @Override public String label() { return op(); }
+ @Override public RegMask regmap(int i) {
+ // Todo: float or int?
+ return i==_inputs._len
+ ? riscv.RMASK // Function call target
+ : riscv.callInMaskInt(i); // Normal argument
+ }
+ @Override public RegMask outregmap() { return riscv.RET_MASK; }
+
+ // Encoding is appended into the byte array; size is returned
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+
+ @Override public void asm(CodeGen code, SB sb) {
+ sb.p(code.reg(fptr()));
+ }
+
+ @Override public String op() { return "callr"; }
+
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CmpIRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CmpIRISC.java
new file mode 100644
index 00000000..39fa3a29
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CmpIRISC.java
@@ -0,0 +1,44 @@
+package com.seaofnodes.simple.node.cpus.riscv;
+
+
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.type.Type;
+import com.seaofnodes.simple.type.TypeInteger;
+import java.io.ByteArrayOutputStream;
+
+// Compare immediate. Sets flags.(RFLAGS)
+public class CmpIRISC extends MachConcreteNode implements MachNode{
+ final int _imm;
+ final String _bop;
+ CmpIRISC( BoolNode bool, TypeInteger ti ) {
+ super(bool);
+ _inputs.pop(); // Toss ideal ConstantNode away, embedded into machine op
+ _bop = bool.op(); // One of <,<=,==
+ _imm = (int)ti.value();
+ assert _imm == ti.value();
+ }
+ CmpIRISC( Node cmp, double ignore ) {
+ super(cmp);
+ _bop = "==";
+ _imm = 0;
+ }
+
+ @Override public RegMask regmap(int i) { assert i==1; return riscv.RMASK; }
+ @Override public RegMask outregmap() { return riscv.FLAGS_MASK; }
+
+ // Encoding is appended into the byte array; size is returned
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+
+ // General form: "cmp rs1, 1"
+ @Override public void asm(CodeGen code, SB sb) {
+ String dst = code.reg(this);
+ if( dst!="FLAGS" ) sb.p(dst).p(" = ");
+ sb.p(code.reg(in(1)));
+ if( _imm != 0 ) sb.p(", #").p(_imm);
+ }
+
+ @Override public String op() { return _imm==0 ? "test" : "cmp"; }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CmpRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CmpRISC.java
new file mode 100644
index 00000000..3bd4008e
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CmpRISC.java
@@ -0,0 +1,28 @@
+package com.seaofnodes.simple.node.cpus.riscv;
+
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.type.Type;
+import com.seaofnodes.simple.type.TypeFunPtr;
+import java.io.ByteArrayOutputStream;
+
+// Compare. Sets flags.(RFLAGS)
+public class CmpRISC extends MachConcreteNode implements MachNode{
+ CmpRISC( Node cmp ) { super(cmp); }
+
+ // Encoding is appended into the byte array; size is returned
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+
+
+ // General form: "cmp rs1, rs2"
+ @Override public void asm(CodeGen code, SB sb) {
+ String dst = code.reg(this);
+ if( dst!="FLAGS" ) sb.p(dst).p(" = ");
+ sb.p(code.reg(in(1))).p(", ").p(code.reg(in(2)));
+ }
+
+ @Override public RegMask regmap(int i) { assert i==1 || i==2; return riscv.RMASK; }
+ @Override public RegMask outregmap() { return riscv.FLAGS_MASK; }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/DivFRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/DivFRISC.java
new file mode 100644
index 00000000..deb2628d
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/DivFRISC.java
@@ -0,0 +1,30 @@
+package com.seaofnodes.simple.node.cpus.riscv;
+
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.node.*;
+import java.io.ByteArrayOutputStream;
+
+public class DivFRISC extends MachConcreteNode implements MachNode{
+ DivFRISC(Node divf) {super(divf);}
+
+ // Register mask allowed on input i.
+ @Override public RegMask regmap(int i) { assert i==1 || i==2; return riscv.FMASK; }
+ // Register mask allowed as a result. 0 for no register.
+ @Override public RegMask outregmap() { return riscv.FMASK; }
+ // Output is same register as input#1
+ @Override public int twoAddress() { return 1; }
+
+ // Encoding is appended into the byte array; size is returned
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+
+ // Default on double precision for now(64 bits)
+ // General form: "fdiv.d rd = src1 / src2
+ @Override public void asm(CodeGen code, SB sb) {
+ sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" + ").p(code.reg(in(2)));
+ }
+
+ @Override public String op() { return "divf"; }
+
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/DivRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/DivRISC.java
new file mode 100644
index 00000000..98a8406f
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/DivRISC.java
@@ -0,0 +1,33 @@
+package com.seaofnodes.simple.node.cpus.riscv;
+
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.node.cpus.x86_64_v2.x86_64_v2;
+import com.seaofnodes.simple.type.TypeInteger;
+import java.io.ByteArrayOutputStream;
+
+public class DivRISC extends MachConcreteNode implements MachNode{
+ DivRISC(Node div) {super(div);}
+
+ // Register mask allowed on input i.
+ @Override public RegMask regmap(int i) {
+ assert i==1 || i==2;
+ return riscv.FMASK; }
+ // Register mask allowed as a result. 0 for no register.
+ @Override public RegMask outregmap() { return x86_64_v2.RMASK; }
+ // Output is same register as input#1
+ @Override public int twoAddress() { return 1; }
+
+ // Encoding is appended into the byte array; size is returned
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+
+ // Signed division
+ // General form: "rd = rs1 / rs"
+ @Override public void asm(CodeGen code, SB sb) {
+ sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" / ").p(code.reg(in(2)));
+ }
+
+ @Override public String op() { return "div"; }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/F8ToIRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/F8ToIRISC.java
new file mode 100644
index 00000000..261a1dcd
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/F8ToIRISC.java
@@ -0,0 +1,27 @@
+package com.seaofnodes.simple.node.cpus.riscv;
+
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.node.cpus.x86_64_v2.x86_64_v2;
+import com.seaofnodes.simple.type.TypeInteger;
+import java.io.ByteArrayOutputStream;
+
+public class F8ToIRISC extends MachConcreteNode implements MachNode {
+ F8ToIRISC(Node f8toi) {super(f8toi);}
+
+ // Register mask allowed on input i.
+ @Override public RegMask regmap(int i) { assert i==1; return riscv.FMASK; }
+ // Register mask allowed as a result. 0 for no register.
+ @Override public RegMask outregmap() { return riscv.RMASK; }
+
+ // Encoding is appended into the byte array; size is returned
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+
+ @Override public void asm(CodeGen code, SB sb) {
+ sb.p(code.reg(this)).p(" = ").p("(int)").p(code.reg(in(1)));
+ }
+
+ @Override public String op() { return "f8toi"; }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/FltRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/FltRISC.java
new file mode 100644
index 00000000..e1637eec
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/FltRISC.java
@@ -0,0 +1,39 @@
+package com.seaofnodes.simple.node.cpus.riscv;
+
+
+import com.seaofnodes.simple.CodeGen;
+import com.seaofnodes.simple.RegMask;
+import com.seaofnodes.simple.SB;
+import com.seaofnodes.simple.Utils;
+import com.seaofnodes.simple.node.ConstantNode;
+import com.seaofnodes.simple.node.MachNode;
+import com.seaofnodes.simple.node.cpus.x86_64_v2.x86_64_v2;
+
+import java.io.ByteArrayOutputStream;
+
+public class FltRISC extends ConstantNode implements MachNode{
+ FltRISC(ConstantNode con) {
+ super(con);
+ }
+ // Register mask allowed on input i. 0 for no register.
+ @Override public RegMask regmap(int i) { return null; }
+ // General int registers
+ @Override public RegMask outregmap() { return riscv.FMASK; }
+
+ // Encoding is appended into the byte array; size is returned
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+
+ // Human-readable form appended to the SB. Things like the encoding,
+ // indentation, leading address or block labels not printed here.
+ // Just something like "ld4\tR17=[R18+12] // Load array base".
+ // General form: "op\tdst=src+src"
+ @Override public void asm(CodeGen code, SB sb) {
+ _con.print(sb.p(code.reg(this)).p(" #"));
+ }
+
+ @Override public String op() {
+ return "flw"; // Some fancier encoding
+ }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/FunRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/FunRISC.java
new file mode 100644
index 00000000..66e32e29
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/FunRISC.java
@@ -0,0 +1,22 @@
+package com.seaofnodes.simple.node.cpus.riscv;
+
+import com.seaofnodes.simple.RegMask;
+import com.seaofnodes.simple.Utils;
+import com.seaofnodes.simple.node.FunNode;
+import com.seaofnodes.simple.node.MachNode;
+import com.seaofnodes.simple.node.cpus.x86_64_v2.FunX86;
+
+import java.io.ByteArrayOutputStream;
+
+public class FunRISC extends FunNode implements MachNode{
+ FunRISC(FunNode fun) {super(fun);}
+ // Register mask allowed on input i. 0 for no register.
+ @Override public RegMask regmap(int i) { return null; }
+ // Register mask allowed as a result. 0 for no register.
+ @Override public RegMask outregmap() { return null; }
+
+ // Encoding is appended into the byte array; size is returned
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/I2F8RISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/I2F8RISC.java
new file mode 100644
index 00000000..df005e86
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/I2F8RISC.java
@@ -0,0 +1,29 @@
+package com.seaofnodes.simple.node.cpus.riscv;
+
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.node.cpus.x86_64_v2.x86_64_v2;
+import com.seaofnodes.simple.type.TypeInteger;
+import java.io.ByteArrayOutputStream;
+
+// fcvt.d.w
+public class I2F8RISC extends MachConcreteNode implements MachNode {
+ I2F8RISC(Node i2f8) {super(i2f8);}
+
+ // Register mask allowed on input i.
+ @Override public RegMask regmap(int i) { assert i==1; return riscv.RMASK; }
+ // Register mask allowed as a result. 0 for no register.
+ @Override public RegMask outregmap() { return riscv.FMASK; }
+
+ // Encoding is appended into the byte array; size is returned
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+
+ // General form: "i2f8 (flt)int_value"
+ @Override public void asm(CodeGen code, SB sb) {
+ sb.p(code.reg(this)).p(" = ").p("(flt)").p(code.reg(in(1)));
+ }
+
+ @Override public String op() { return "i2f8"; }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/IntRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/IntRISC.java
new file mode 100644
index 00000000..bf170b22
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/IntRISC.java
@@ -0,0 +1,43 @@
+package com.seaofnodes.simple.node.cpus.riscv;
+
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.node.ConstantNode;
+import com.seaofnodes.simple.node.MachNode;
+import com.seaofnodes.simple.type.Type;
+import com.seaofnodes.simple.type.TypeInteger;
+
+import java.io.ByteArrayOutputStream;
+// Integer constants
+public class IntRISC extends ConstantNode implements MachNode {
+ IntRISC(ConstantNode con) {
+ super(con);
+ }
+
+ // Register mask allowed on input i. 0 for no register.
+ @Override public RegMask regmap(int i) { return null; }
+ // General int registers
+ @Override public RegMask outregmap() { return riscv.RMASK; }
+
+ // Encoding is appended into the byte array; size is returned
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+
+ // Human-readable form appended to the SB. Things like the encoding,
+ // indentation, leading address or block labels not printed here.
+ // Just something like "ld4\tR17=[R18+12] // Load array base".
+ // General form: "op\tdst=src+src"
+ @Override public void asm(CodeGen code, SB sb) {
+ String reg = code.reg(this);
+ if( _con == Type.NIL || _con == TypeInteger.ZERO )
+ sb.p(reg).p(",").p(reg);
+ else
+ _con.print(sb.p(reg).p(" #"));
+ }
+
+ @Override public String op() {
+ if( _con == Type.NIL || _con == TypeInteger.ZERO )
+ return "xor";
+ return "ldi"; // Some fancier encoding
+ }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/LoadRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/LoadRISC.java
new file mode 100644
index 00000000..846e313c
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/LoadRISC.java
@@ -0,0 +1,34 @@
+package com.seaofnodes.simple.node.cpus.riscv;
+
+
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.type.TypeInteger;
+import java.io.ByteArrayOutputStream;
+
+public class LoadRISC extends MemOpRISC{
+ LoadRISC(LoadNode ld) {
+ super(ld, ld);
+ }
+
+ @Override public RegMask regmap(int i) {
+ // wider mask, encode it differently, immediate handled with hard split(in RA)
+ return riscv.RMASK.or(riscv.FMASK);
+ }
+ // Register mask allowed as a result. 0 for no register.
+ @Override public RegMask outregmap() { return riscv.RMASK; }
+
+
+ // Encoding is appended into the byte array; size is returned
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+
+ @Override public void asm(CodeGen code, SB sb) {
+ }
+
+ @Override public String op() {
+ return "lw" +_sz;
+ }
+}
+
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/MemOpRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/MemOpRISC.java
new file mode 100644
index 00000000..3bf1cf09
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/MemOpRISC.java
@@ -0,0 +1,39 @@
+package com.seaofnodes.simple.node.cpus.riscv;
+
+import com.seaofnodes.print.ExprPrinter;
+
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.node.cpus.x86_64_v2.x86_64_v2;
+import com.seaofnodes.simple.type.*;
+import java.io.ByteArrayOutputStream;
+import java.lang.StringBuilder;
+
+
+public class MemOpRISC extends MemOpNode implements MachNode{
+ final char _sz = (char)('0'+(1<<_declaredType.log_size()));
+ MemOpRISC(Node op, MemOpNode mop) {
+ super(op, mop);
+ }
+ @Override protected ExprPrinter<Node> _printMach(ExprPrinter<Node> p) { return p.p(".").p(_name); }
+
+ @Override public boolean isMem() { return _type instanceof TypeMem; }
+
+ @Override public String label() { return op(); }
+ @Override public Type compute() { throw Utils.TODO(); }
+ @Override public Node idealize() { throw Utils.TODO(); }
+
+ // Register mask allowed on input i.
+ @Override public RegMask regmap(int i) {
+ throw Utils.TODO();
+ }
+ // Register mask allowed as a result. 0 for no register.
+ @Override public RegMask outregmap() { throw Utils.TODO(); }
+
+ @Override public int encoding(ByteArrayOutputStream bytes) { throw Utils.TODO(); }
+
+
+ SB asm_address(CodeGen code, SB sb) {
+ return sb;
+ }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/MulFRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/MulFRISC.java
new file mode 100644
index 00000000..43197b50
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/MulFRISC.java
@@ -0,0 +1,30 @@
+package com.seaofnodes.simple.node.cpus.riscv;
+
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.node.*;
+import java.io.ByteArrayOutputStream;
+
+public class MulFRISC extends MachConcreteNode implements MachNode{
+ MulFRISC(Node mulf) {super(mulf);}
+
+ // Register mask allowed on input i.
+ @Override public RegMask regmap(int i) { assert i==1 || i==2; return riscv.FMASK; }
+ // Register mask allowed as a result. 0 for no register.
+ @Override public RegMask outregmap() { return riscv.FMASK; }
+ // Output is same register as input#1
+ @Override public int twoAddress() { return 1; }
+
+ // Encoding is appended into the byte array; size is returned
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+
+ // Default on double precision for now(64 bits)
+ // General form: "fmul.d rd = src1 * src2
+ @Override public void asm(CodeGen code, SB sb) {
+ sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" + ").p(code.reg(in(2)));
+ }
+
+ @Override public String op() { return "mulf"; }
+
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/MulIRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/MulIRISC.java
new file mode 100644
index 00000000..8735068e
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/MulIRISC.java
@@ -0,0 +1,35 @@
+package com.seaofnodes.simple.node.cpus.riscv;
+
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.type.TypeInteger;
+import java.io.ByteArrayOutputStream;
+
+public class MulIRISC extends MachConcreteNode implements MachNode {
+ final TypeInteger _ti;
+ MulIRISC(Node mul, TypeInteger ti) {super(mul); _inputs.pop(); _ti = ti;}
+
+ // Register mask allowed on input i.
+ // This is the normal calling convention
+ @Override public RegMask regmap(int i) {
+ return riscv.RMASK; }
+ // Register mask allowed as a result. 0 for no register.
+ @Override public RegMask outregmap() { return riscv.RMASK; }
+
+
+ // Output is same register as input#1
+ @Override public int twoAddress() { return 0; }
+
+ // Encoding is appended into the byte array; size is returned
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+ // General form
+ // General form: "muli dst * #imm"
+ @Override public void asm(CodeGen code, SB sb) {
+ sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" * #");
+ _ti.print(sb);
+ }
+
+ @Override public String op() { return "muli"; }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/MulRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/MulRISC.java
new file mode 100644
index 00000000..8713486e
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/MulRISC.java
@@ -0,0 +1,33 @@
+package com.seaofnodes.simple.node.cpus.riscv;
+
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.node.cpus.x86_64_v2.x86_64_v2;
+import com.seaofnodes.simple.type.TypeInteger;
+import java.io.ByteArrayOutputStream;
+
+
+// mulh signed multiply instruction(no-imm form)
+public class MulRISC extends MachConcreteNode implements MachNode{
+ MulRISC(Node mul) {super(mul);}
+
+ // Register mask allowed on input i.
+ @Override public RegMask regmap(int i) { assert i==1 || i==2; return riscv.RMASK; }
+ // Register mask allowed as a result. 0 for no register.
+ @Override public RegMask outregmap() { return riscv.RMASK; }
+ // Output is same register as input#1
+ @Override public int twoAddress() { return 1; }
+
+ // Encoding is appended into the byte array; size is returned
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+
+ // General form: "rd = rs1 * rs2"
+ @Override public void asm(CodeGen code, SB sb) {
+ sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" * ").p(code.reg(in(2)));
+ }
+
+ @Override public String op() { return "mul"; }
+
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/NewRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/NewRISC.java
new file mode 100644
index 00000000..2b8ec4f2
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/NewRISC.java
@@ -0,0 +1,44 @@
+package com.seaofnodes.simple.node.cpus.riscv;
+
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.node.cpus.x86_64_v2.x86_64_v2;
+
+import java.io.ByteArrayOutputStream;
+
+
+public class NewRISC extends NewNode implements MachNode{
+
+ // A pre-zeroed chunk of memory.
+ NewRISC( NewNode nnn ) { super(nnn); }
+
+ // Register mask allowed on input i, the size
+ @Override public RegMask regmap(int i) {
+ // Size
+ if( i==2 ) return riscv.RET_MASK;
+ // Memory has no register
+ return null;
+ }
+
+ @Override public RegMask outregmap() { throw Utils.TODO(); }
+
+ // Register mask allowed as a result. Pointer result in standard calling
+ // convention.
+ @Override public RegMask outregmap(int i) {
+ if( i == 0 ) return riscv.RET_MASK;
+ // Memory has no register
+ return null;
+ }
+
+ // Encoding is appended into the byte array; size is returned
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+
+ // General form: "alloc #bytes"
+ @Override public void asm(CodeGen code, SB sb) {
+ sb.p(code.reg(size()));
+ }
+
+ @Override public String op() { return "alloc"; }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/OrIRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/OrIRISC.java
new file mode 100644
index 00000000..f3f79ed1
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/OrIRISC.java
@@ -0,0 +1,43 @@
+package com.seaofnodes.simple.node.cpus.riscv;
+
+import com.seaofnodes.simple.CodeGen;
+import com.seaofnodes.simple.RegMask;
+import com.seaofnodes.simple.SB;
+import com.seaofnodes.simple.Utils;
+import com.seaofnodes.simple.node.MachConcreteNode;
+import com.seaofnodes.simple.node.MachNode;
+import com.seaofnodes.simple.node.Node;
+import com.seaofnodes.simple.node.cpus.x86_64_v2.x86_64_v2;
+import com.seaofnodes.simple.type.Type;
+import com.seaofnodes.simple.type.TypeInteger;
+
+import java.io.ByteArrayOutputStream;
+
+public class OrIRISC extends MachConcreteNode implements MachNode{
+ final TypeInteger _ti;
+ OrIRISC(Node or, TypeInteger ti) {super(or); _inputs.pop(); _ti = ti;}
+ // Register mask allowed on input i.
+ // This is the normal calling convention
+ @Override public RegMask regmap(int i) {
+ assert i==1 || i==2;
+ return riscv.RMASK;
+ }
+ // Register mask allowed as a result. 0 for no register.
+ @Override public RegMask outregmap() { return riscv.RMASK; }
+ // Output is same register as input#1
+ @Override public int twoAddress() { return 0;}
+
+ // Encoding is appended into the byte array; size is returned
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+
+ // General form
+ // General form: "rd = rs1 | imm"
+ @Override public void asm(CodeGen code, SB sb) {
+ sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" | #");
+ _ti.print(sb);
+ }
+
+ @Override public String op() { return "ori"; }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/OrRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/OrRISC.java
new file mode 100644
index 00000000..8f1e9305
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/OrRISC.java
@@ -0,0 +1,43 @@
+package com.seaofnodes.simple.node.cpus.riscv;
+
+import com.seaofnodes.simple.node.MachConcreteNode;
+import com.seaofnodes.simple.node.MachNode;
+
+import com.seaofnodes.simple.CodeGen;
+import com.seaofnodes.simple.RegMask;
+import com.seaofnodes.simple.SB;
+import com.seaofnodes.simple.Utils;
+import com.seaofnodes.simple.node.Node;
+import com.seaofnodes.simple.node.cpus.x86_64_v2.x86_64_v2;
+import java.io.ByteArrayOutputStream;
+
+public class OrRISC extends MachConcreteNode implements MachNode {
+ OrRISC(Node or) {
+ super(or);
+ }
+
+ // Register mask allowed on input i.
+ // This is the normal calling convention
+ @Override public RegMask regmap(int i) {
+ assert i==1 || i==2;
+ return riscv.RMASK;
+ }
+
+ // Register mask allowed as a result. 0 for no register.
+ @Override public RegMask outregmap() { return riscv.RMASK; }
+
+ // Output is same register as input#1
+ @Override public int twoAddress() { return 0; }
+
+ // Encoding is appended into the byte array; size is returned
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+
+ // General form
+ // General form: # rd = rs1 | rs2
+ @Override public void asm(CodeGen code, SB sb) {
+ sb.p(code.reg(this)).p(" = ").p(code.reg(in(1)));
+ }
+
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/ParmRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/ParmRISC.java
new file mode 100644
index 00000000..2e20f96f
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/ParmRISC.java
@@ -0,0 +1,25 @@
+package com.seaofnodes.simple.node.cpus.riscv;
+
+import com.seaofnodes.simple.RegMask;
+import com.seaofnodes.simple.SB;
+import com.seaofnodes.simple.Utils;
+import com.seaofnodes.simple.node.ParmNode;
+import com.seaofnodes.simple.node.MachNode;
+import java.io.ByteArrayOutputStream;
+
+public class ParmRISC extends ParmNode implements MachNode{
+ final RegMask _rmask;
+ ParmRISC(ParmNode parm) {
+ super(parm);
+ // Assume int
+ _rmask = riscv.callInMaskInt(_idx);
+ }
+
+ // Register mask allowed on input i. 0 for no register.
+ @Override public RegMask regmap(int i) { return null; }
+ // Register mask allowed as a result. Calling convention register
+ @Override public RegMask outregmap() { return _rmask; }
+
+ // Encoding is appended into the byte array. Returns size
+ @Override public int encoding(ByteArrayOutputStream bytes) { return 0; }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/ProjRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/ProjRISC.java
new file mode 100644
index 00000000..a1312c4c
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/ProjRISC.java
@@ -0,0 +1,23 @@
+package com.seaofnodes.simple.node.cpus.riscv;
+
+
+import com.seaofnodes.simple.RegMask;
+import com.seaofnodes.simple.SB;
+import com.seaofnodes.simple.Utils;
+import com.seaofnodes.simple.node.ProjNode;
+import com.seaofnodes.simple.node.MachNode;
+import java.io.ByteArrayOutputStream;
+
+public class ProjRISC extends ProjNode implements MachNode{
+ ProjRISC(ProjNode p) {super(p);}
+
+ // Register mask allowed on input i. 0 for no register.
+ @Override public RegMask regmap(int i) { return null; }
+ // Register mask allowed as a result. 0 for no register.
+ @Override public RegMask outregmap() { return null; }
+
+ // Encoding is appended into the byte array; size is returned
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/RetRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/RetRISC.java
new file mode 100644
index 00000000..c92e6989
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/RetRISC.java
@@ -0,0 +1,48 @@
+package com.seaofnodes.simple.node.cpus.riscv;
+
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.node.FunNode;
+import com.seaofnodes.simple.node.ReturnNode;
+import com.seaofnodes.simple.node.MachNode;
+
+import java.io.ByteArrayOutputStream;
+
+public class RetRISC extends ReturnNode implements MachNode{
+ RetRISC(ReturnNode ret, FunNode fun) { super(ret, fun); fun.setRet(this); }
+ // Todo: postSelect
+ // Register mask allowed on input i.
+ // This is the normal calling convention
+ @Override public RegMask regmap(int i) {
+ return switch( i ) {
+ case 0 -> null;
+ case 1 -> null;
+ case 2 -> riscv.RET_MASK;
+ case 3 -> null; // RPC is always on stack
+ default -> throw Utils.TODO();
+ };
+ }
+ // Register mask allowed as a result. 0 for no register.
+ @Override public RegMask outregmap() { return null; }
+
+ // Encoding is appended into the byte array; size is returned
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+
+ // Human-readable form appended to the SB. Things like the encoding,
+ // indentation, leading address or block labels not printed here.
+ // Just something like "ld4\tR17=[R18+12] // Load array base".
+ // General form: "op\tdst=src+src"
+ @Override public void asm(CodeGen code, SB sb) {
+ sb.p(code.reg(in(2)));
+ }
+
+ // Correct Nodes outside the normal edges
+ @Override public void postSelect() {
+ FunNode fun = (FunNode)rpc().in(0);
+ _fun = fun;
+ fun.setRet(this);
+ }
+
+ @Override public String op() { return "ret"; }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SetRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SetRISC.java
new file mode 100644
index 00000000..1d8c9de8
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SetRISC.java
@@ -0,0 +1,35 @@
+package com.seaofnodes.simple.node.cpus.riscv;
+
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.type.Type;
+import com.seaofnodes.simple.type.TypeInteger;
+import java.io.ByteArrayOutputStream;
+
+// corresponds to slt,sltu,slti,sltiu, seqz
+// RISCV doesn't have rflags.
+public class SetRISC extends MachConcreteNode implements MachNode{
+ final String _bop; // One of <,<=,==
+ SetRISC( Node cmp, String bop ) {
+ super(cmp);
+ _inputs.setLen(1); // Pop the cmp inputs
+ // Replace with the matched cmp
+ _inputs.push(cmp);
+ _bop = bop;
+ }
+ @Override public RegMask regmap(int i) { assert i==1; return riscv.FLAGS_MASK; }
+ @Override public RegMask outregmap() { return riscv.RMASK; }
+
+ // Encoding is appended into the byte array; size is returned
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+
+ @Override public void asm(CodeGen code, SB sb) {
+ sb.p(code.reg(this));
+ String src = code.reg(in(1));
+ if( src!="FLAGS" ) sb.p(" = ").p(src);
+ }
+
+ @Override public String op() { return "set"+_bop; }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SllIRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SllIRISC.java
new file mode 100644
index 00000000..8927c086
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SllIRISC.java
@@ -0,0 +1,40 @@
+package com.seaofnodes.simple.node.cpus.riscv;
+
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.node.cpus.x86_64_v2.x86_64_v2;
+import com.seaofnodes.simple.type.Type;
+import com.seaofnodes.simple.type.TypeInteger;
+import java.io.ByteArrayOutputStream;
+import java.util.BitSet;
+
+public class SllIRISC extends MachConcreteNode implements MachNode{
+ final TypeInteger _ti;
+ SllIRISC(Node sll, TypeInteger ti) {super(sll); _inputs.pop(); _ti = ti;}
+
+ // Register mask allowed on input i.
+ // This is the normal calling convention
+ @Override public RegMask regmap(int i) {
+ //assert i==1;
+ return riscv.RMASK; }
+
+ // Register mask allowed as a result. 0 for no register.
+ @Override public RegMask outregmap() { return riscv.RMASK; }
+
+ // Output is same register as input#1
+ @Override public int twoAddress() { return 0; }
+
+ // Encoding is appended into the byte array; size is returned
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+
+ // General form
+ // General form: "slli rd, rs1, imm"
+ @Override public void asm(CodeGen code, SB sb) {
+ sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" << #");
+ _ti.print(sb);
+ }
+
+ @Override public String op() { return "slli"; }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SllRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SllRISC.java
new file mode 100644
index 00000000..69538433
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SllRISC.java
@@ -0,0 +1,36 @@
+package com.seaofnodes.simple.node.cpus.riscv;
+
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.type.Type;
+import com.seaofnodes.simple.type.TypeInteger;
+import java.io.ByteArrayOutputStream;
+import java.util.BitSet;
+
+public class SllRISC extends MachConcreteNode implements MachNode{
+ SllRISC(Node sll) {super(sll);}
+
+ // Register mask allowed on input i.
+ // This is the normal calling convention
+ @Override public RegMask regmap(int i) {
+ //assert i==1;
+ return riscv.RMASK; }
+ // Register mask allowed as a result. 0 for no register.
+ @Override public RegMask outregmap() { return riscv.RMASK; }
+
+ // Output is same register as input#1
+ @Override public int twoAddress() { return 1; }
+
+ // Encoding is appended into the byte array; size is returned
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+
+ // General form
+ // General form: "sll rd, rs1, rs2"
+ @Override public void asm(CodeGen code, SB sb) {
+ sb.p(code.reg(this)).p(" = ").p(code.reg(in(1)));
+ }
+
+ @Override public String op() { return "sll"; }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SraIRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SraIRISC.java
new file mode 100644
index 00000000..654dd2ae
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SraIRISC.java
@@ -0,0 +1,43 @@
+package com.seaofnodes.simple.node.cpus.riscv;
+
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.node.cpus.x86_64_v2.x86_64_v2;
+import com.seaofnodes.simple.type.Type;
+import com.seaofnodes.simple.type.TypeInteger;
+import java.io.ByteArrayOutputStream;
+import java.util.BitSet;
+import java.lang.StringBuilder;
+
+
+// Right Shift Arithmetic(immediate)
+public class SraIRISC extends MachConcreteNode implements MachNode{
+ final TypeInteger _ti;
+ SraIRISC(Node sra, TypeInteger ti) {super(sra); _inputs.pop(); _ti = ti;}
+
+ // Register mask allowed on input i.
+ // This is the normal calling convention
+ @Override public RegMask regmap(int i) {
+ // assert i==1;
+ return riscv.RMASK; }
+
+ // Register mask allowed as a result. 0 for no register.
+ @Override public RegMask outregmap() { return riscv.RMASK; }
+
+ // Output is same register as input#1
+ @Override public int twoAddress() { return 0; }
+
+ // Encoding is appended into the byte array; size is returned
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+
+ // General form
+ // General form: "srai rd, rs1, imm"
+ @Override public void asm(CodeGen code, SB sb) {
+ sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" >> #");
+ _ti.print(sb);
+ }
+
+ @Override public String op() { return "srai"; }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SraRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SraRISC.java
new file mode 100644
index 00000000..2d50ceaa
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SraRISC.java
@@ -0,0 +1,42 @@
+package com.seaofnodes.simple.node.cpus.riscv;
+
+import com.seaofnodes.simple.CodeGen;
+import com.seaofnodes.simple.RegMask;
+import com.seaofnodes.simple.SB;
+import com.seaofnodes.simple.Utils;
+import com.seaofnodes.simple.node.MachConcreteNode;
+import com.seaofnodes.simple.node.MachNode;
+import com.seaofnodes.simple.node.Node;
+import com.seaofnodes.simple.type.TypeInteger;
+
+import java.io.ByteArrayOutputStream;
+
+// Right Shift Arithmetic
+public class SraRISC extends MachConcreteNode implements MachNode {
+
+ SraRISC(Node sra) {super(sra);}
+
+ // Register mask allowed on input i.
+ // This is the normal calling convention
+ @Override public RegMask regmap(int i) {
+ // assert i==1;
+ return riscv.RMASK; }
+ // Register mask allowed as a result. 0 for no register.
+ @Override public RegMask outregmap() { return riscv.RMASK; }
+
+ // Output is same register as input#1
+ @Override public int twoAddress() { return 1; }
+
+ // Encoding is appended into the byte array; size is returned
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+
+ // General form
+ // General form: "sra rd, rs1, rs2"
+ @Override public void asm(CodeGen code, SB sb) {
+ sb.p(code.reg(this)).p(" = ").p(code.reg(in(1)));
+ }
+
+ @Override public String op() { return "sra"; }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SrlIRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SrlIRISC.java
new file mode 100644
index 00000000..f7ad6c25
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SrlIRISC.java
@@ -0,0 +1,43 @@
+package com.seaofnodes.simple.node.cpus.riscv;
+
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.node.cpus.x86_64_v2.x86_64_v2;
+import com.seaofnodes.simple.type.Type;
+import com.seaofnodes.simple.type.TypeInteger;
+import java.io.ByteArrayOutputStream;
+import java.util.BitSet;
+import java.lang.StringBuilder;
+
+
+// Right Shift Logical Immediate
+public class SrlIRISC extends MachConcreteNode implements MachNode {
+ final TypeInteger _ti;
+ SrlIRISC(Node srl, TypeInteger ti) {super(srl); _inputs.pop(); _ti = ti;}
+
+
+ // Register mask allowed on input i.
+ // This is the normal calling convention
+ @Override public RegMask regmap(int i) {
+ // assert i==1;
+ return riscv.RMASK; }
+ // Register mask allowed as a result. 0 for no register.
+ @Override public RegMask outregmap() { return riscv.RMASK; }
+
+ // Output is same register as input#1
+ @Override public int twoAddress() { return 1; }
+
+ // Encoding is appended into the byte array; size is returned
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+
+ // General form
+ // General form: "srli dst << #imm"
+ @Override public void asm(CodeGen code, SB sb) {
+ sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" >> #");
+ _ti.print(sb);
+ }
+
+ @Override public String op() { return "srli"; }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SrlRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SrlRISC.java
new file mode 100644
index 00000000..25d62688
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SrlRISC.java
@@ -0,0 +1,38 @@
+package com.seaofnodes.simple.node.cpus.riscv;
+
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.type.Type;
+import com.seaofnodes.simple.type.TypeInteger;
+import java.io.ByteArrayOutputStream;
+import java.util.BitSet;
+import java.lang.StringBuilder;
+
+// Right Shift Logical
+public class SrlRISC extends MachConcreteNode implements MachNode {
+ SrlRISC(Node srli) {super(srli);}
+
+ // Register mask allowed on input i.
+ // This is the normal calling convention
+ @Override public RegMask regmap(int i) {
+ // assert i==1;
+ return riscv.RMASK; }
+ // Register mask allowed as a result. 0 for no register.
+ @Override public RegMask outregmap() { return riscv.RMASK; }
+
+ // Output is same register as input#1
+ @Override public int twoAddress() { return 1; }
+
+ // Encoding is appended into the byte array; size is returned
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+
+ // General form
+ // General form: "srl rd, rs1, rs2"
+ @Override public void asm(CodeGen code, SB sb) {
+ sb.p(code.reg(this)).p(" = ").p(code.reg(in(1)));
+ }
+
+ @Override public String op() { return "srl"; }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/StoreRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/StoreRISC.java
new file mode 100644
index 00000000..1bd40d0c
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/StoreRISC.java
@@ -0,0 +1,40 @@
+package com.seaofnodes.simple.node.cpus.riscv;
+
+import com.seaofnodes.simple.CodeGen;
+import com.seaofnodes.simple.RegMask;
+import com.seaofnodes.simple.SB;
+import com.seaofnodes.simple.Utils;
+import com.seaofnodes.simple.node.MachConcreteNode;
+import com.seaofnodes.simple.node.MachNode;
+import com.seaofnodes.simple.node.Node;
+import com.seaofnodes.simple.node.StoreNode;
+
+import java.io.ByteArrayOutputStream;
+import java.util.BitSet;
+
+
+// sw rs2,offset(rs1)
+public class StoreRISC extends MemOpRISC {
+ StoreRISC( StoreNode st) {
+ super(st, st);
+ }
+
+ @Override public RegMask regmap(int i) {
+ return i>=2 && i<=4 ? riscv.RMASK : null;
+ }
+
+
+ // Register mask allowed as a result. 0 for no register.
+ @Override public RegMask outregmap() { return null; }
+
+ // Encoding is appended into the byte array; size is returned
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+
+ @Override public void asm(CodeGen code, SB sb) {
+
+ }
+
+ @Override public String op() { return "st"+_sz; }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SubFRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SubFRISC.java
new file mode 100644
index 00000000..e6a0b8e4
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SubFRISC.java
@@ -0,0 +1,30 @@
+package com.seaofnodes.simple.node.cpus.riscv;
+
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.type.TypeInteger;
+import java.io.ByteArrayOutputStream;
+
+public class SubFRISC extends MachConcreteNode implements MachNode{
+ SubFRISC(Node subf) {super(subf);}
+
+ // Register mask allowed on input i.
+ @Override public RegMask regmap(int i) { assert i==1 || i==2; return riscv.FMASK; }
+ // Register mask allowed as a result. 0 for no register.
+ @Override public RegMask outregmap() { return riscv.FMASK; }
+ // Output is same register as input#1
+ @Override public int twoAddress() { return 0; }
+
+ // Encoding is appended into the byte array; size is returned
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+
+ // Default on double precision for now(64 bits)
+ // General form: "fsub.d rd = src1 - src2
+ @Override public void asm(CodeGen code, SB sb) {
+ sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" + ").p(code.reg(in(2)));
+ }
+
+ @Override public String op() { return "subf"; }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SubRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SubRISC.java
new file mode 100644
index 00000000..6fb0e0ef
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SubRISC.java
@@ -0,0 +1,29 @@
+package com.seaofnodes.simple.node.cpus.riscv;
+
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.type.TypeInteger;
+import java.io.ByteArrayOutputStream;
+
+public class SubRISC extends MachConcreteNode implements MachNode {
+ SubRISC( Node sub ) { super(sub); }
+
+ // Register mask allowed on input i.
+ @Override public RegMask regmap(int i) { assert i==1 || i==2; return riscv.RMASK; }
+ // Register mask allowed as a result. 0 for no register.
+ @Override public RegMask outregmap() { return riscv.RMASK; }
+ // Output is same register as input#1
+ @Override public int twoAddress() { return 0; }
+
+ // Encoding is appended into the byte array; size is returned
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+
+ // General form: "sub # rd = rs1 - rs2"
+ @Override public void asm(CodeGen code, SB sb) {
+ sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" - ").p(code.reg(in(2)));
+ }
+
+ @Override public String op() { return "sub"; }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/TFPRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/TFPRISC.java
new file mode 100644
index 00000000..b508541a
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/TFPRISC.java
@@ -0,0 +1,43 @@
+package com.seaofnodes.simple.node.cpus.riscv;
+
+
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.node.ConstantNode;
+import com.seaofnodes.simple.node.MachNode;
+import com.seaofnodes.simple.node.cpus.x86_64_v2.TFPX86;
+import com.seaofnodes.simple.node.cpus.x86_64_v2.x86_64_v2;
+import com.seaofnodes.simple.type.Type;
+import com.seaofnodes.simple.type.TypeFunPtr;
+
+import java.io.ByteArrayOutputStream;
+
+public class TFPRISC extends ConstantNode implements MachNode{
+ TFPRISC(ConstantNode con) {super(con);}
+ // Register mask allowed on input i. 0 for no register.
+ @Override public RegMask regmap(int i) { return null; }
+ // General int registers
+ @Override public RegMask outregmap() { return x86_64_v2.RMASK; }
+
+ // Encoding is appended into the byte array; size is returned
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+
+ // Human-readable form appended to the SB. Things like the encoding,
+ // indentation, leading address or block labels not printed here.
+ // Just something like "ld4\tR17=[R18+12] // Load array base".
+ // General form: "op\tdst=src+src"
+ @Override public void asm(CodeGen code, SB sb) {
+ String reg = code.reg(this);
+ if( _con == Type.NIL )
+ sb.p(reg).p(",").p(reg);
+ else
+ _con.print(sb.p(reg).p(" #"));
+ }
+
+ @Override public String op() {
+ if( _con == Type.NIL )
+ return "xor";
+ return "ldx"; // Some fancier encoding
+ }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/XorIRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/XorIRISC.java
new file mode 100644
index 00000000..3d53ee64
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/XorIRISC.java
@@ -0,0 +1,45 @@
+package com.seaofnodes.simple.node.cpus.riscv;
+
+import com.seaofnodes.simple.CodeGen;
+import com.seaofnodes.simple.RegMask;
+import com.seaofnodes.simple.SB;
+import com.seaofnodes.simple.Utils;
+import com.seaofnodes.simple.node.MachConcreteNode;
+import com.seaofnodes.simple.node.MachNode;
+import com.seaofnodes.simple.node.Node;
+import com.seaofnodes.simple.node.cpus.x86_64_v2.x86_64_v2;
+import com.seaofnodes.simple.type.TypeInteger;
+
+import java.io.ByteArrayOutputStream;
+
+public class XorIRISC extends MachConcreteNode implements MachNode{
+ final TypeInteger _ti;
+ XorIRISC(Node xor, TypeInteger ti) {super(xor); _inputs.pop(); _ti = ti;}
+
+ // Register mask allowed on input i.
+ // This is the normal calling convention
+ @Override public RegMask regmap(int i) {
+ assert i==1 || i==2;
+
+ return riscv.RMASK; }
+
+ // Register mask allowed as a result. 0 for no register.
+ @Override public RegMask outregmap() { return riscv.RMASK; }
+
+ // Output is same register as input#1
+ @Override public int twoAddress() { return 1; }
+
+ // Encoding is appended into the byte array; size is returned
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+
+ // General form
+ // General form: "rd = rs1 ^ imm"
+ @Override public void asm(CodeGen code, SB sb) {
+ sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" ^ #");
+ _ti.print(sb);
+ }
+
+ @Override public String op() { return "xori"; }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/XorRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/XorRISC.java
new file mode 100644
index 00000000..2f34529e
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/XorRISC.java
@@ -0,0 +1,43 @@
+package com.seaofnodes.simple.node.cpus.riscv;
+
+import com.seaofnodes.simple.CodeGen;
+import com.seaofnodes.simple.RegMask;
+import com.seaofnodes.simple.SB;
+import com.seaofnodes.simple.Utils;
+import com.seaofnodes.simple.node.MachConcreteNode;
+import com.seaofnodes.simple.node.MachNode;
+import com.seaofnodes.simple.node.Node;
+import com.seaofnodes.simple.type.TypeInteger;
+
+import java.io.ByteArrayOutputStream;
+
+public class XorRISC extends MachConcreteNode implements MachNode {
+
+ XorRISC(Node or) {
+ super(or);
+ }
+
+ // Register mask allowed on input i.
+ // This is the normal calling convention
+ @Override public RegMask regmap(int i) {
+ assert i==1 || i==2;
+ return riscv.RMASK;
+ }
+
+ // Register mask allowed as a result. 0 for no register.
+ @Override public RegMask outregmap() { return riscv.RMASK; }
+
+ // Output is same register as input#1
+ @Override public int twoAddress() { return 0; }
+
+ // Encoding is appended into the byte array; size is returned
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+
+ // General form
+ // General form: # # rd = rs1 ^ rs2
+ @Override public void asm(CodeGen code, SB sb) {
+ sb.p(code.reg(this)).p(" = ").p(code.reg(in(1)));
+ }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/riscv.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/riscv.java
new file mode 100644
index 00000000..ad6e1c0f
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/riscv.java
@@ -0,0 +1,332 @@
+package com.seaofnodes.simple.node.cpus.riscv;
+
+import com.seaofnodes.simple.Machine;
+import com.seaofnodes.simple.RegMask;
+import com.seaofnodes.simple.Utils;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.node.cpus.x86_64_v2.*;
+import com.seaofnodes.simple.type.*;
+
+public class riscv extends Machine{
+ @Override public String name() {return "riscv";}
+
+ // Using ABI names instead of register names
+ public static int ZERO = 0, RA = 1, SP = 2, GP = 3, TP = 4, T0 = 5, T1 = 6, T2 = 7;
+ public static int S0 = 8, S1 = 9, A0 = 10, A1 = 11, A2 = 12, A3 = 13, A4 = 14, A5 = 15;
+ public static int A6 = 16, A7 = 17, S2 = 18, S3 = 19, S4 = 20, S5 = 21, S6 = 22, S7 = 23;
+ public static int S8 = 24, S9 = 25, S10 = 26, S11 = 27, T3 = 28, T4 = 29, T5 = 30, T6 = 31;
+
+ // FP registers
+ public static int F0 = 32, F1 = 33, F2 = 34, F3 = 35, F4 = 36, F5 = 37, F6 = 38, F7 = 39;
+ public static int FS0 = 40, FS1 = 41, FA0 = 42, FA1 = 43, FA2 = 44, FA3 = 45, FA4 = 46, FA5 = 47;
+ public static int FA6 = 48, FA7 = 49, FS2 = 50, FS3 = 51, FS4 = 52, FS5 = 53, FS6 = 54, FS7 = 55;
+ public static int FS8 = 56, FS9 = 57, FS10 = 58, FS11 = 59, FT8 = 60, FT9 = 61, FT10 = 62, FT11 = 63;
+
+ public static int FLAGS = 64;
+
+ // General purpose register mask: pointers and ints, not floats
+ public static RegMask RMASK = new RegMask(0b11111111111111111111111111111111);
+ // Float mask from(ft0–ft11)
+ public static RegMask FMASK = new RegMask(0b11111111111111111111111111111111);
+
+ // Return single int/ptr register
+ public static RegMask RET_MASK = new RegMask(A0);
+
+ // Arguments masks
+ public static RegMask A0_MASK = new RegMask(1L<<A0);
+ public static RegMask A1_MASK = new RegMask(1L<<A1);
+ public static RegMask A2_MASK = new RegMask(1L<<A2);
+ public static RegMask A3_MASK = new RegMask(1L<<A3);
+ public static RegMask A4_MASK = new RegMask(1L<<A4);
+ public static RegMask A5_MASK = new RegMask(1L<<A5);
+ public static RegMask A6_MASK = new RegMask(1L<<A6);
+ public static RegMask A7_MASK = new RegMask(1L<<A7);
+
+ public static RegMask FLAGS_MASK = new RegMask(1L << FLAGS);
+
+ // Float arguments masks
+ public static RegMask FA0_MASK = new RegMask(1L<<FA0);
+ public static RegMask FA1_MASK = new RegMask(1L<<FA1);
+ public static RegMask FA2_MASK = new RegMask(1L<<FA2);
+ public static RegMask FA3_MASK = new RegMask(1L<<FA3);
+ public static RegMask FA4_MASK = new RegMask(1L<<FA4);
+ public static RegMask FA5_MASK = new RegMask(1L<<FA5);
+ public static RegMask FA6_MASK = new RegMask(1L<<FA6);
+ public static RegMask FA7_MASK = new RegMask(1L<<FA7);
+
+ // Int arguments calling conv
+ static RegMask[] CALLINMASK_RISCV_INT = new RegMask[] {
+ null,
+ null,
+ A0_MASK,
+ A1_MASK,
+ A2_MASK,
+ A3_MASK,
+ A4_MASK,
+ A5_MASK,
+ A6_MASK,
+ A7_MASK
+ };
+
+ static int[] CALLINARG_RISCV_INT = new int[] {
+ 0, // Control, no register
+ 0, // Memory, no register
+ A0,
+ A1,
+ A2,
+ A3,
+ A4,
+ A5,
+ A6,
+ A7
+ };
+
+ static int callInArgInt(int idx) {
+ return CALLINARG_RISCV_INT[idx];
+ }
+
+ static RegMask callInMaskInt(int idx) {
+ return CALLINMASK_RISCV_INT[idx];
+ }
+
+ // Float arguments
+ static RegMask[] CALLINMASK_RISCV_FLOAT = new RegMask[] {
+ null,
+ null,
+ FA0_MASK,
+ FA1_MASK,
+ FA2_MASK,
+ FA3_MASK,
+ FA4_MASK,
+ FA5_MASK,
+ FA6_MASK,
+ FA7_MASK
+ };
+
+
+ static int[] CALLINARG_RISCV_FLOAT = new int[] {
+ 0, // Control, no register
+ 0, // Memory, no register
+ FA0,
+ FA1,
+ FA2,
+ FA3,
+ FA4,
+ FA5,
+ FA6,
+ FA7
+ };
+
+
+ static int callInArgFloat(int idx) {
+ return CALLINARG_RISCV_FLOAT[idx];
+ }
+
+ static RegMask callInMaskFloat(int idx) {
+ return CALLINMASK_RISCV_FLOAT[idx];
+ }
+ // caller saved(riscv)
+ //public static final long RISCV_CALLER_SAVED= TBD
+ // callee saved(riscv)
+ public static final long RISCV_CALLEE_SAVED =
+ (1L << FS0) | (1L << FS1) | (1L << FS2) | (1L << FS3) | (1L << FS4)
+ | (1L << FS5) | (1L << FS6) | (1L << FS7) | (1L << FS8) | (1L << FS9) | (1L << FS10);
+
+
+ // Calling conv metadata
+ public int GPR_COUNT_CONV_RISCV = 7; // A0, A1, A2, A3, A4, A5, A6, A7
+ public int FLOAT_COUNT_CONV_RISCV = 7; // FA0, FA1, FA2, FA3, FA4, FA5, FA6, FA7
+
+ public static final String[] REGS = new String[] {
+ "zero", "ra" , "sp" , "gp" , "tp" , "t0" , "t1" , "t2" ,
+ "s0" , "s1" , "a0" , "a1" , "a2" , "a3" , "a4" , "a5" ,
+ "a6" , "a7" , "s2" , "s3" , "s4" , "s5" , "s6" , "s7" ,
+ "s8" , "s9" , "s10" , "s11" , "t3" , "t4" , "t5" , "t6" ,
+ "f0" , "f1" , "f2" , "f3" , "f4" , "f5" , "f6" , "f7" ,
+ "fs0" , "fs1" , "fa0" , "fa1" , "fa2" , "fa3" , "fa4" , "fa5" ,
+ "fa6" , "fa7" , "fs2" , "fs3" , "fs4" , "fs5" , "fs6" , "fs7" ,
+ "fs8" , "fs9" , "fs10", "fs11", "ft8" , "ft9" , "ft10", "ft11",
+ "flags"
+ };
+
+ // General purpose register mask:
+ @Override public String reg( int reg ) { return REGS[reg]; }
+ // Return a MachNode unconditional branch
+ @Override public CFGNode jump() {
+ throw Utils.TODO();
+ }
+
+ // Create a split op; any register to any register, including stack slots
+ @Override public Node split() {
+ throw Utils.TODO();
+ }
+
+ // Break an infinite loop
+ @Override public IfNode never( CFGNode ctrl ) {
+ throw Utils.TODO();
+ }
+
+ @Override public Node instSelect( Node n ) {
+ return switch (n) {
+ case AddFNode addf -> addf(addf);
+ case AddNode add -> add(add);
+ case AndNode and -> and(and);
+ case BoolNode bool -> cmp(bool);
+ case CallEndNode cend -> new CallEndNode((CallNode) cend.in(0));
+ case CallNode call -> call(call);
+ case CProjNode c -> new CProjNode(c);
+ case ConstantNode con -> con(con);
+ case DivFNode divf -> new DivFRISC(divf);
+ case DivNode div -> new DivRISC(div);
+ case FunNode fun -> new FunRISC(fun);
+ case IfNode iff -> jmp(iff);
+ case LoadNode ld -> ld(ld);
+ case MemMergeNode mem -> new MemMergeNode(mem);
+ case MulFNode mulf -> new MulFRISC(mulf);
+ case MulNode mul -> mul(mul);
+ case NewNode nnn -> new NewRISC(nnn);
+ case OrNode or -> or(or);
+ case ParmNode parm -> new ParmRISC(parm);
+ case BulkMemPhiNode phi -> new BulkMemPhiNode(phi);
+ case MemPhiNode phi -> new MemPhiNode(phi);
+ case PhiNode phi -> new PhiNode(phi);
+ case ProjNode prj -> prj(prj);
+ case ReadOnlyNode read -> new ReadOnlyNode(read);
+ case ReturnNode ret -> new RetRISC(ret, ret.fun());
+ case SarNode sar -> sra(sar);
+ case ShlNode shl -> sll(shl);
+ case ShrNode shr -> srl(shr);
+ case StartNode start -> new StartNode(start);
+ case StopNode stop -> new StopNode(stop);
+ case StoreNode st -> st(st);
+ case SubFNode subf -> new SubFRISC(subf);
+ case SubNode sub -> sub(sub);
+ case ToFloatNode tfn -> i2f8(tfn);
+ case ToIntegerNode cvt -> new F8ToIRISC(cvt);
+ case XorNode xor -> xor(xor);
+
+ case LoopNode loop -> new LoopNode(loop);
+ case RegionNode region -> new RegionNode(region);
+ default -> throw Utils.TODO();
+ };
+ }
+
+ private Node addf(AddFNode addf) {
+ return new AddFRISC(addf);
+ }
+
+ private Node add(AddNode add) {
+ Node rhs = add.in(2);
+ if( rhs instanceof ConstantNode off && off._con instanceof TypeInteger toff ) {
+ return new AddIRISC(add, toff);
+ }
+ return new AddRISC(add);
+ }
+
+ private Node and(AndNode and) {
+ if( and.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti )
+ return new AndIRISC(and, ti);
+ return new AndRISC(and);
+ }
+
+ private Node cmp(BoolNode bool) {
+ Node cmp = _cmp(bool);
+ return new SetRISC(cmp, bool.op());
+ }
+
+ private Node call(CallNode call) {
+ if( call.fptr() instanceof ConstantNode con && con._con instanceof TypeFunPtr tfp )
+ return new CallRISC(call, tfp);
+ return new CallRRISC(call);
+ }
+
+ private Node _cmp(BoolNode bool) {
+ Node rhs = bool.in(2);
+
+ return rhs instanceof ConstantNode con && con._con instanceof TypeInteger ti
+ ? new CmpIRISC(bool, ti)
+ : new CmpRISC(bool);
+ }
+
+ private Node con( ConstantNode con ) {
+ return switch( con._con ) {
+ case TypeInteger ti -> new IntRISC(con);
+ case TypeFloat tf -> new IntRISC(con);
+ case TypeFunPtr tfp -> new TFPRISC(con);
+ case TypeMemPtr tmp -> new ConstantNode(con);
+ case TypeNil tn -> throw Utils.TODO();
+ // TOP, BOTTOM, XCtrl, Ctrl, etc. Never any executable code.
+ case Type t -> new ConstantNode(con);
+ };
+ }
+
+ private Node jmp( IfNode iff ) {
+ // If/Bool combos will match to a Cmp/Set which sets flags.
+ // Most general arith ops will also set flags, which the Jmp needs directly.
+ // Loads do not set the flags, and will need an explicit TEST
+ if( !(iff.in(1) instanceof BoolNode) )
+ iff.setDef(1,new BoolNode.EQ(iff.in(1),new ConstantNode(TypeInteger.ZERO)));
+ return new CBranchRISC(iff, ((BoolNode)iff.in(1)).op());
+ }
+
+ private Node or(OrNode or) {
+ if(or.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti)
+ return new OrIRISC(or, ti);
+
+ return new OrRISC(or);
+ }
+
+ private Node xor(XorNode xor) {
+ if(xor.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti)
+ return new XorIRISC(xor, ti);
+ return new XorRISC(xor);
+ }
+
+ private Node sra(SarNode sar) {
+ if( sar.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti)
+ return new SraIRISC(sar, ti);
+ return new SraRISC(sar);
+ }
+
+ private Node srl(ShrNode shr) {
+ if( shr.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti)
+ return new SrlIRISC(shr, ti);
+ return new SrlRISC(shr);
+ }
+
+ private Node sll(ShlNode sll) {
+ if( sll.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti)
+ return new SllIRISC(sll, ti);
+ return new SllRISC(sll);
+ }
+
+ private Node sub(SubNode sub) {
+ return sub.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti
+ ? new AddIRISC(sub, TypeInteger.constant(-ti.value()))
+ : new SubRISC(sub);
+ }
+
+ private Node i2f8(ToFloatNode tfn) {
+ assert tfn.in(1)._type instanceof TypeInteger ti;
+ return new I2F8RISC(tfn);
+ }
+
+ private Node prj(ProjNode prj) {
+ return new ProjRISC(prj);
+ }
+
+ private Node ld(LoadNode ld) {
+ return new LoadRISC(ld);
+ }
+
+ private Node st(StoreNode st) {
+ return new StoreRISC(st);
+ }
+
+ private Node mul(MulNode mul) {
+ return mul.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti
+ ? new MulIRISC(mul, ti)
+ : new MulRISC(mul);
+ }
+}
+
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddFMemX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddFMemX86.java
new file mode 100644
index 00000000..4bb24fa3
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddFMemX86.java
@@ -0,0 +1,33 @@
+package com.seaofnodes.simple.node.cpus.x86_64_v2;
+
+import com.seaofnodes.simple.CodeGen;
+import com.seaofnodes.simple.Utils;
+import com.seaofnodes.simple.RegMask;
+import com.seaofnodes.simple.SB;
+import com.seaofnodes.simple.node.*;
+
+public class AddFMemX86 extends MemOpX86 {
+ AddFMemX86( AddFNode add, LoadNode ld , Node base, Node idx, int off, int scale, Node val ) {
+ super(add,ld, base, idx, off, scale, 0, val );
+ }
+
+ @Override public RegMask regmap(int i) {
+ if( i==1 ) return null; // Memory
+ if( i==2 ) return x86_64_v2.RMASK; // base
+ if( i==3 ) return x86_64_v2.RMASK; // index
+ if( i==4 ) return x86_64_v2.XMASK; // value
+ throw Utils.TODO();
+ }
+
+ // Register mask allowed as a result. 0 for no register.
+ @Override public RegMask outregmap() { return x86_64_v2.XMASK; }
+
+
+ // General form: "add dst = src + [base + idx<<2 + 12]"
+ @Override public void asm(CodeGen code, SB sb) {
+ sb.p(code.reg(this)).p(" = ");
+ sb.p(val()==null ? "#"+_imm : code.reg(val())).p(" + ");
+ asm_address(code,sb);
+ }
+ @Override public String op() { return "addf"+_sz; }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddFX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddFX86.java
new file mode 100644
index 00000000..a3f5c7b3
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddFX86.java
@@ -0,0 +1,29 @@
+package com.seaofnodes.simple.node.cpus.x86_64_v2;
+
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.type.TypeInteger;
+import java.io.ByteArrayOutputStream;
+
+public class AddFX86 extends MachConcreteNode implements MachNode {
+ AddFX86( Node addf ) { super(addf); }
+
+ // Register mask allowed on input i.
+ @Override public RegMask regmap(int i) { assert i==1 || i==2; return x86_64_v2.XMASK; }
+ // Register mask allowed as a result. 0 for no register.
+ @Override public RegMask outregmap() { return x86_64_v2.XMASK; }
+ // Output is same register as input#1
+ @Override public int twoAddress() { return 1; }
+
+ // Encoding is appended into the byte array; size is returned
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+
+ // General form: "addf dst += src"
+ @Override public void asm(CodeGen code, SB sb) {
+ sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" + ").p(code.reg(in(2)));
+ }
+
+ @Override public String op() { return "addf"; }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddIX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddIX86.java
new file mode 100644
index 00000000..6a332ff5
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddIX86.java
@@ -0,0 +1,39 @@
+package com.seaofnodes.simple.node.cpus.x86_64_v2;
+
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.type.TypeInteger;
+import java.io.ByteArrayOutputStream;
+
+public class AddIX86 extends MachConcreteNode implements MachNode {
+ final TypeInteger _ti;
+ AddIX86( Node add, TypeInteger ti ) {
+ super(add);
+ _inputs.pop();
+ _ti = ti;
+ }
+
+ // Register mask allowed on input i.
+ @Override public RegMask regmap(int i) {
+ // assert i== i;
+ return x86_64_v2.WMASK; }
+ // Register mask allowed as a result. 0 for no register.
+ @Override public RegMask outregmap() { return x86_64_v2.WMASK; }
+ // Output is same register as input#1
+ @Override public int twoAddress() { return 1; }
+
+ // Encoding is appended into the byte array; size is returned
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+
+ // General form: "addi dst += #imm"
+ @Override public void asm(CodeGen code, SB sb) {
+ sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" + #");
+ _ti.print(sb);
+ }
+
+ @Override public String op() {
+ return _ti.value() == 1 ? "inc" : (_ti.value() == -1 ? "dec" : "addi");
+ }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddMemX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddMemX86.java
new file mode 100644
index 00000000..7d45d757
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddMemX86.java
@@ -0,0 +1,25 @@
+package com.seaofnodes.simple.node.cpus.x86_64_v2;
+
+import com.seaofnodes.simple.CodeGen;
+import com.seaofnodes.simple.RegMask;
+import com.seaofnodes.simple.SB;
+import com.seaofnodes.simple.node.*;
+
+public class AddMemX86 extends MemOpX86 {
+ AddMemX86( AddNode add, LoadNode ld , Node base, Node idx, int off, int scale, int imm, Node val ) {
+ super(add,ld, base, idx, off, scale, imm, val );
+ }
+
+ @Override public String op() { return "add"+_sz; }
+
+ // Register mask allowed as a result. 0 for no register.
+ @Override public RegMask outregmap() { return x86_64_v2.WMASK; }
+
+
+ // General form: "add dst = src + [base + idx<<2 + 12]"
+ @Override public void asm(CodeGen code, SB sb) {
+ sb.p(code.reg(this)).p(" = ");
+ sb.p(val()==null ? "#"+_imm : code.reg(val())).p(" + ");
+ asm_address(code,sb);
+ }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddX86.java
new file mode 100644
index 00000000..61aa5a9e
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddX86.java
@@ -0,0 +1,29 @@
+package com.seaofnodes.simple.node.cpus.x86_64_v2;
+
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.type.TypeInteger;
+import java.io.ByteArrayOutputStream;
+
+public class AddX86 extends MachConcreteNode implements MachNode {
+ AddX86( Node add ) { super(add); }
+
+ // Register mask allowed on input i.
+ @Override public RegMask regmap(int i) { assert i==1 || i==2; return x86_64_v2.RMASK; }
+ // Register mask allowed as a result. 0 for no register.
+ @Override public RegMask outregmap() { return x86_64_v2.WMASK; }
+ // Output is same register as input#1
+ @Override public int twoAddress() { return 1; }
+
+ // Encoding is appended into the byte array; size is returned
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+
+ // General form: "add dst += src"
+ @Override public void asm(CodeGen code, SB sb) {
+ sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" + ").p(code.reg(in(2)));
+ }
+
+ @Override public String op() { return "add"; }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AndIX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AndIX86.java
new file mode 100644
index 00000000..f2ee1c66
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AndIX86.java
@@ -0,0 +1,42 @@
+package com.seaofnodes.simple.node.cpus.x86_64_v2;
+
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.type.Type;
+import com.seaofnodes.simple.type.TypeInteger;
+import java.io.ByteArrayOutputStream;
+import java.util.BitSet;
+import java.lang.StringBuilder;
+
+public class AndIX86 extends MachConcreteNode implements MachNode {
+ final TypeInteger _ti;
+ AndIX86(Node and, TypeInteger ti) {
+ super(and);
+ _inputs.pop();
+ _ti = ti;
+ }
+
+ // Register mask allowed on input i.
+ // This is the normal calling convention
+ @Override public RegMask regmap(int i) { assert i==1; return x86_64_v2.WMASK; }
+ // Register mask allowed as a result. 0 for no register.
+ @Override public RegMask outregmap() { return x86_64_v2.WMASK; }
+
+
+ // Output is same register as input#1
+ @Override public int twoAddress() { return 1; }
+
+ // Encoding is appended into the byte array; size is returned
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+
+ // General form
+ // General form: "andi dst & #imm"
+ @Override public void asm(CodeGen code, SB sb) {
+ sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" & #");
+ _ti.print(sb);
+ }
+
+ @Override public String op() { return "andi"; }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AndX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AndX86.java
new file mode 100644
index 00000000..45cf2b22
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AndX86.java
@@ -0,0 +1,33 @@
+package com.seaofnodes.simple.node.cpus.x86_64_v2;
+
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.node.*;
+import java.io.ByteArrayOutputStream;
+
+public class AndX86 extends MachConcreteNode implements MachNode{
+ AndX86(Node and) {
+ super(and);
+ }
+ // Register mask allowed on input i.
+ // This is the normal calling convention
+ @Override public RegMask regmap(int i) { assert i==1; return x86_64_v2.WMASK; }
+
+ @Override public RegMask outregmap() { return x86_64_v2.WMASK; }
+
+ // Output is same register as input#1
+ @Override public int twoAddress() { return 1; }
+
+ // Encoding is appended into the byte array; size is returned
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+
+ // General form
+ // General form: "and dst & #imm"
+ @Override public void asm(CodeGen code, SB sb) {
+ sb.p(code.reg(this)).p(" = ").p(code.reg(in(1)));
+ }
+
+ @Override public String op() { return "and"; }
+}
+
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CallEndX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CallEndX86.java
new file mode 100644
index 00000000..55ab90c9
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CallEndX86.java
@@ -0,0 +1,37 @@
+package com.seaofnodes.simple.node.cpus.x86_64_v2;
+
+import com.seaofnodes.simple.SB;
+import com.seaofnodes.simple.Utils;
+import com.seaofnodes.simple.CodeGen;
+import com.seaofnodes.simple.RegMask;
+import com.seaofnodes.simple.node.CallEndNode;
+import com.seaofnodes.simple.node.MachNode;
+import com.seaofnodes.simple.type.TypeFloat;
+import com.seaofnodes.simple.type.TypeFunPtr;
+import java.io.ByteArrayOutputStream;
+
+public class CallEndX86 extends CallEndNode implements MachNode {
+ final TypeFunPtr _tfp;
+ CallEndX86( CallEndNode cend ) {
+ super(cend);
+ _tfp = (TypeFunPtr)cend.call().fptr()._type;
+ }
+
+ @Override public String label() { return op(); }
+ @Override public RegMask regmap(int i) { return null; }
+ @Override public RegMask outregmap() { throw Utils.TODO(); }
+ @Override public RegMask outregmap(int idx) {
+ if( idx != 2 ) return null;
+ return _tfp._ret instanceof TypeFloat ? x86_64_v2.RET_FMASK : x86_64_v2.RET_MASK;
+ }
+
+ // Encoding is appended into the byte array; size is returned
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+
+ @Override public void asm(CodeGen code, SB sb) { }
+
+ @Override public String op() { return "cend"; }
+
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CallRX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CallRX86.java
new file mode 100644
index 00000000..99260a27
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CallRX86.java
@@ -0,0 +1,31 @@
+package com.seaofnodes.simple.node.cpus.x86_64_v2;
+
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.type.Type;
+import com.seaofnodes.simple.type.TypeFunPtr;
+import java.io.ByteArrayOutputStream;
+
+public class CallRX86 extends CallNode implements MachNode {
+ CallRX86( CallNode call ) { super(call); }
+
+ @Override public String label() { return op(); }
+ @Override public RegMask regmap(int i) {
+ return i==_inputs._len
+ ? x86_64_v2.WMASK // Function call target
+ : x86_64_v2.callInMask(tfp(),i); // Normal argument
+ }
+ @Override public RegMask outregmap() { return x86_64_v2.RET_MASK; }
+
+ // Encoding is appended into the byte array; size is returned
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+
+ @Override public void asm(CodeGen code, SB sb) {
+ sb.p(code.reg(fptr()));
+ }
+
+ @Override public String op() { return "callr"; }
+
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CallX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CallX86.java
new file mode 100644
index 00000000..b3e3bbe8
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CallX86.java
@@ -0,0 +1,41 @@
+package com.seaofnodes.simple.node.cpus.x86_64_v2;
+
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.type.Type;
+import com.seaofnodes.simple.type.TypeFunPtr;
+import java.io.ByteArrayOutputStream;
+import java.util.BitSet;
+
+public class CallX86 extends CallNode implements MachNode {
+ final TypeFunPtr _tfp;
+ final String _name;
+ CallX86( CallNode call, TypeFunPtr tfp ) {
+ super(call);
+ _inputs.pop(); // Pop constant target
+ assert tfp.isConstant();
+ _tfp = tfp;
+ _name = CodeGen.CODE.link(tfp)._name;
+ }
+
+ @Override public String label() { return op(); }
+ @Override public RegMask regmap(int i) {
+ return x86_64_v2.callInMask(tfp(),i); // Normal argument
+ }
+ @Override public RegMask outregmap() { return x86_64_v2.RET_MASK; }
+
+ @Override public String name() { return _name; }
+ @Override public TypeFunPtr tfp() { return _tfp; }
+
+ // Encoding is appended into the byte array; size is returned
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+
+ @Override public void asm(CodeGen code, SB sb) {
+ sb.p(_name);
+ }
+
+ @Override public String op() { return "call"; }
+
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CmpIX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CmpIX86.java
new file mode 100644
index 00000000..ea526f33
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CmpIX86.java
@@ -0,0 +1,43 @@
+package com.seaofnodes.simple.node.cpus.x86_64_v2;
+
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.type.Type;
+import com.seaofnodes.simple.type.TypeInteger;
+import java.io.ByteArrayOutputStream;
+
+// Compare immediate. Sets flags.(RFLAGS)
+public class CmpIX86 extends MachConcreteNode implements MachNode {
+ final int _imm;
+ final String _bop;
+ CmpIX86( BoolNode bool, TypeInteger ti ) {
+ super(bool);
+ _inputs.pop(); // Toss ideal ConstantNode away, embedded into machine op
+ _bop = bool.op(); // One of <,<=,==
+ _imm = (int)ti.value();
+ assert _imm == ti.value();
+ }
+ CmpIX86( Node cmp, double ignore ) {
+ super(cmp);
+ _bop = "==";
+ _imm = 0;
+ }
+
+ @Override public RegMask regmap(int i) { assert i==1; return x86_64_v2.RMASK; }
+ @Override public RegMask outregmap() { return x86_64_v2.FLAGS_MASK; }
+
+ // Encoding is appended into the byte array; size is returned
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+
+ @Override public void asm(CodeGen code, SB sb) {
+ String dst = code.reg(this);
+ if( dst!="FLAGS" ) sb.p(dst).p(" = ");
+ sb.p(code.reg(in(1)));
+ if( _imm != 0 ) sb.p(", #").p(_imm);
+ }
+
+ @Override public String op() { return _imm==0 ? "test" : "cmp"; }
+
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CmpMemX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CmpMemX86.java
new file mode 100644
index 00000000..873ca9df
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CmpMemX86.java
@@ -0,0 +1,33 @@
+package com.seaofnodes.simple.node.cpus.x86_64_v2;
+
+import com.seaofnodes.simple.CodeGen;
+import com.seaofnodes.simple.RegMask;
+import com.seaofnodes.simple.SB;
+import com.seaofnodes.simple.node.*;
+
+public class CmpMemX86 extends MemOpX86 {
+ final boolean _invert; // Op switched LHS, RHS
+ CmpMemX86( BoolNode bool, LoadNode ld , Node base, Node idx, int off, int scale, int imm, Node val, boolean invert ) {
+ super(bool,ld, base, idx, off, scale, imm, val );
+ _invert = invert;
+ }
+
+ // Register mask allowed as a result. 0 for no register.
+ @Override public RegMask outregmap() { return x86_64_v2.FLAGS_MASK; }
+
+ // General form: "cmp dst = src, [base + idx<<2 + 12]"
+ @Override public void asm(CodeGen code, SB sb) {
+ String dst = code.reg(this);
+ if( dst!="FLAGS" ) sb.p(dst).p(" = ");
+ if( _invert ) asm_address(code,sb).p(",");
+ if( val()==null ) {
+ if( _imm!=0 ) sb.p("#").p(_imm);
+ } else {
+ sb.p(code.reg(val()));
+ }
+ if( !_invert ) asm_address(code,sb.p(", "));
+ }
+
+ @Override public String op() { return ((val()==null && _imm==0) ? "test" : "cmp") + _sz; }
+
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CmpX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CmpX86.java
new file mode 100644
index 00000000..d8ebb12d
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CmpX86.java
@@ -0,0 +1,28 @@
+package com.seaofnodes.simple.node.cpus.x86_64_v2;
+
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.type.TypeInteger;
+import java.io.ByteArrayOutputStream;
+
+// Compare. Sets flags.(RFLAGS)
+public class CmpX86 extends MachConcreteNode implements MachNode {
+ CmpX86( Node cmp ) { super(cmp); }
+
+ @Override public RegMask regmap(int i) { assert i==1 || i==2; return x86_64_v2.RMASK; }
+ @Override public RegMask outregmap() { return x86_64_v2.FLAGS_MASK; }
+
+ // Encoding is appended into the byte array; size is returned
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+
+ // General form: "cmp reg1, reg2"
+ @Override public void asm(CodeGen code, SB sb) {
+ String dst = code.reg(this);
+ if( dst!="FLAGS" ) sb.p(dst).p(" = ");
+ sb.p(code.reg(in(1))).p(", ").p(code.reg(in(2)));
+ }
+
+ @Override public String op() { return "cmp"; }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/DivFX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/DivFX86.java
new file mode 100644
index 00000000..d73bfd5b
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/DivFX86.java
@@ -0,0 +1,33 @@
+package com.seaofnodes.simple.node.cpus.x86_64_v2;
+
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.type.TypeInteger;
+import java.io.ByteArrayOutputStream;
+
+public class DivFX86 extends MachConcreteNode implements MachNode {
+ DivFX86( Node divf) { super(divf); }
+
+ // Register mask allowed on input i.
+ @Override public RegMask regmap(int i) { assert i==1 || i==2; return x86_64_v2.XMASK; }
+ // Register mask allowed as a result. 0 for no register.
+ @Override public RegMask outregmap() { return x86_64_v2.XMASK; }
+ // Output is same register as input#1
+ @Override public int twoAddress() { return 1; }
+
+ // Encoding is appended into the byte array; size is returned
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+
+
+ // General form: "divf dst /= src"
+ @Override public void asm(CodeGen code, SB sb) {
+ sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" / ").p(code.reg(in(2)));
+ }
+
+ @Override public String op() {
+ // 8 bytes = 64 bits
+ return "divf";
+ }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/DivX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/DivX86.java
new file mode 100644
index 00000000..1dc20017
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/DivX86.java
@@ -0,0 +1,31 @@
+package com.seaofnodes.simple.node.cpus.x86_64_v2;
+
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.type.TypeInteger;
+import java.io.ByteArrayOutputStream;
+
+public class DivX86 extends MachConcreteNode implements MachNode {
+ DivX86( Node div ) { super(div); }
+
+ // Register mask allowed on input i.
+ @Override public RegMask regmap(int i) {
+ assert i==1 || i==2;
+ return x86_64_v2.RMASK; }
+ // Register mask allowed as a result. 0 for no register.
+ @Override public RegMask outregmap() { return x86_64_v2.WMASK; }
+ // Output is same register as input#1
+ @Override public int twoAddress() { return 1; }
+
+ // Encoding is appended into the byte array; size is returned
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+
+ // General form: "div dst /= src"
+ @Override public void asm(CodeGen code, SB sb) {
+ sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" / ").p(code.reg(in(2)));
+ }
+
+ @Override public String op() { return "div"; }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/F8ToIX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/F8ToIX86.java
new file mode 100644
index 00000000..33a77d4c
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/F8ToIX86.java
@@ -0,0 +1,27 @@
+package com.seaofnodes.simple.node.cpus.x86_64_v2;
+
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.type.TypeInteger;
+import java.io.ByteArrayOutputStream;
+
+public class F8ToIX86 extends MachConcreteNode implements MachNode {
+ F8ToIX86(Node f8toi ) { super(f8toi); }
+
+ // Register mask allowed on input i.
+ @Override public RegMask regmap(int i) { assert i==1; return x86_64_v2.XMASK; }
+ // Register mask allowed as a result. 0 for no register.
+ @Override public RegMask outregmap() { return x86_64_v2.WMASK; }
+
+ // Encoding is appended into the byte array; size is returned
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+
+
+ @Override public void asm(CodeGen code, SB sb) {
+ sb.p(code.reg(this)).p(" = ").p("(int)").p(code.reg(in(1)));
+ }
+
+ @Override public String op() { return "f8toi"; }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/FltX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/FltX86.java
new file mode 100644
index 00000000..5e1190da
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/FltX86.java
@@ -0,0 +1,36 @@
+package com.seaofnodes.simple.node.cpus.x86_64_v2;
+
+import com.seaofnodes.simple.CodeGen;
+import com.seaofnodes.simple.RegMask;
+import com.seaofnodes.simple.SB;
+import com.seaofnodes.simple.Utils;
+import com.seaofnodes.simple.node.ConstantNode;
+import com.seaofnodes.simple.node.MachNode;
+
+import java.io.ByteArrayOutputStream;
+
+public class FltX86 extends ConstantNode implements MachNode {
+ FltX86(ConstantNode con ) { super(con); }
+
+ // Register mask allowed on input i. 0 for no register.
+ @Override public RegMask regmap(int i) { return null; }
+ // General int registers
+ @Override public RegMask outregmap() { return x86_64_v2.XMASK; }
+
+ // Encoding is appended into the byte array; size is returned
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+
+ // Human-readable form appended to the SB. Things like the encoding,
+ // indentation, leading address or block labels not printed here.
+ // Just something like "ld4\tR17=[R18+12] // Load array base".
+ // General form: "op\tdst=src+src"
+ @Override public void asm(CodeGen code, SB sb) {
+ _con.print(sb.p(code.reg(this)).p(" #"));
+ }
+
+ @Override public String op() {
+ return "fld"; // Some fancier encoding
+ }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/FunX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/FunX86.java
new file mode 100644
index 00000000..8b0f016a
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/FunX86.java
@@ -0,0 +1,21 @@
+package com.seaofnodes.simple.node.cpus.x86_64_v2;
+
+import com.seaofnodes.simple.RegMask;
+import com.seaofnodes.simple.Utils;
+import com.seaofnodes.simple.node.FunNode;
+import com.seaofnodes.simple.node.MachNode;
+import java.io.ByteArrayOutputStream;
+
+public class FunX86 extends FunNode implements MachNode {
+ FunX86( FunNode fun ) { super(fun); }
+
+ // Register mask allowed on input i. 0 for no register.
+ @Override public RegMask regmap(int i) { return null; }
+ // Register mask allowed as a result. 0 for no register.
+ @Override public RegMask outregmap() { return null; }
+
+ // Encoding is appended into the byte array; size is returned
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/I2f8X86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/I2f8X86.java
new file mode 100644
index 00000000..983f817d
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/I2f8X86.java
@@ -0,0 +1,28 @@
+package com.seaofnodes.simple.node.cpus.x86_64_v2;
+
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.type.TypeInteger;
+import java.io.ByteArrayOutputStream;
+
+public class I2f8X86 extends MachConcreteNode implements MachNode {
+ I2f8X86(Node i2f8 ) { super(i2f8); }
+
+ // Register mask allowed on input i.
+ @Override public RegMask regmap(int i) { assert i==1; return x86_64_v2.WMASK; }
+ // Register mask allowed as a result. 0 for no register.
+ @Override public RegMask outregmap() { return x86_64_v2.XMASK; }
+
+ // Encoding is appended into the byte array; size is returned
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+
+ // General form: "i2f8 (flt)int_value"
+
+ @Override public void asm(CodeGen code, SB sb) {
+ sb.p(code.reg(this)).p(" = ").p("(flt)").p(code.reg(in(1)));
+ }
+
+ @Override public String op() { return "i2f8"; }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/IntX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/IntX86.java
new file mode 100644
index 00000000..566a7293
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/IntX86.java
@@ -0,0 +1,45 @@
+package com.seaofnodes.simple.node.cpus.x86_64_v2;
+
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.node.ConstantNode;
+import com.seaofnodes.simple.node.MachNode;
+import com.seaofnodes.simple.type.Type;
+import com.seaofnodes.simple.type.TypeInteger;
+
+import java.io.ByteArrayOutputStream;
+
+// Integer constants
+public class IntX86 extends ConstantNode implements MachNode {
+
+ IntX86( ConstantNode con ) {
+ super(con);
+ }
+
+ // Register mask allowed on input i. 0 for no register.
+ @Override public RegMask regmap(int i) { return null; }
+ // General int registers
+ @Override public RegMask outregmap() { return x86_64_v2.WMASK; }
+
+ // Encoding is appended into the byte array; size is returned
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+
+ // Human-readable form appended to the SB. Things like the encoding,
+ // indentation, leading address or block labels not printed here.
+ // Just something like "ld4\tR17=[R18+12] // Load array base".
+ // General form: "op\tdst=src+src"
+ @Override public void asm(CodeGen code, SB sb) {
+ String reg = code.reg(this);
+ if( _con == Type.NIL || _con == TypeInteger.ZERO )
+ sb.p(reg).p(",").p(reg);
+ else
+ _con.print(sb.p(reg).p(" #"));
+ }
+
+ @Override public String op() {
+ if( _con == Type.NIL || _con == TypeInteger.ZERO )
+ return "xor";
+ return "ldi"; // Some fancier encoding
+ }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/JmpX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/JmpX86.java
new file mode 100644
index 00000000..2262e847
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/JmpX86.java
@@ -0,0 +1,46 @@
+package com.seaofnodes.simple.node.cpus.x86_64_v2;
+
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.type.Type;
+import com.seaofnodes.simple.type.TypeInteger;
+import java.io.ByteArrayOutputStream;
+
+// Jump on flags, uses flags
+public class JmpX86 extends IfNode implements MachNode {
+ final String _bop;
+ JmpX86( IfNode iff, String bop ) {
+ super(iff);
+ _bop = bop;
+ }
+
+ @Override public String label() { return op(); }
+
+ @Override public void postSelect() {
+ Node set = in(1);
+ Node cmp = set.in(1);
+ // Bypass an expected Set and just reference the cmp directly
+ if( set instanceof SetX86 && (cmp instanceof CmpX86 || cmp instanceof CmpIX86 || cmp instanceof CmpMemX86) )
+ _inputs.set(1,cmp);
+ else
+ throw Utils.TODO();
+ }
+ @Override public RegMask regmap(int i) { assert i==1; return x86_64_v2.FLAGS_MASK; }
+ @Override public RegMask outregmap() { return null; }
+
+ // Encoding is appended into the byte array; size is returned
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+
+ @Override public void asm(CodeGen code, SB sb) {
+ String src = code.reg(in(1));
+ if( src!="FLAGS" ) sb.p(src);
+ }
+
+ @Override public String op() { return "j"+_bop; }
+
+ @Override public String comment() {
+ return "L"+cproj(1)._nid+", L"+cproj(0)._nid;
+ }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/LeaX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/LeaX86.java
new file mode 100644
index 00000000..a49b8d7f
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/LeaX86.java
@@ -0,0 +1,42 @@
+package com.seaofnodes.simple.node.cpus.x86_64_v2;
+
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.type.TypeInteger;
+import java.io.ByteArrayOutputStream;
+
+public class LeaX86 extends MachConcreteNode implements MachNode {
+ final int _scale;
+ final long _offset;
+ LeaX86( Node add, Node base, Node idx, int scale, long offset ) {
+ super(add);
+ assert scale==1 || scale==2 || scale==4 || scale==8;
+ _inputs.pop();
+ _inputs.pop();
+ _inputs.push(base);
+ _inputs.push(idx);
+ _scale = scale;
+ _offset = offset;
+ }
+
+ // Register mask allowed on input i.
+ @Override public RegMask regmap(int i) { assert i==1 || i==2; return x86_64_v2.RMASK; }
+ // Register mask allowed as a result. 0 for no register.
+ @Override public RegMask outregmap() { return x86_64_v2.WMASK; }
+
+ // Encoding is appended into the byte array; size is returned
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+
+ // General form: "lea dst = base + 4*idx + 12"
+ @Override public void asm(CodeGen code, SB sb) {
+ sb.p(code.reg(this)).p(" = ");
+ if( in(1) != null )
+ sb.p(code.reg(in(1))).p(" + ");
+ sb.p(code.reg(in(2))).p("*").p(_scale);
+ if( _offset!=0 ) sb.p(" + #").p(_offset);
+ }
+
+ @Override public String op() { return "lea"; }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/LoadX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/LoadX86.java
new file mode 100644
index 00000000..02a9a4fe
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/LoadX86.java
@@ -0,0 +1,25 @@
+package com.seaofnodes.simple.node.cpus.x86_64_v2;
+
+import com.seaofnodes.simple.CodeGen;
+import com.seaofnodes.simple.RegMask;
+import com.seaofnodes.simple.SB;
+import com.seaofnodes.simple.node.LoadNode;
+import com.seaofnodes.simple.node.Node;
+
+public class LoadX86 extends MemOpX86 {
+ LoadX86( LoadNode ld, Node base, Node idx, int off, int scale ) {
+ super(ld,ld, base, idx, off, scale, 0);
+ }
+
+ @Override public String op() { return "ld"+_sz; }
+
+ // Register mask allowed as a result. 0 for no register.
+ @Override public RegMask outregmap() { return x86_64_v2.WMASK; }
+
+
+ // General form: "ldN dst,[base + idx<<2 + 12]"
+ @Override public void asm(CodeGen code, SB sb) {
+ sb.p(code.reg(this)).p(",");
+ asm_address(code,sb);
+ }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MemAddX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MemAddX86.java
new file mode 100644
index 00000000..a6cdbd05
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MemAddX86.java
@@ -0,0 +1,28 @@
+package com.seaofnodes.simple.node.cpus.x86_64_v2;
+
+import com.seaofnodes.simple.CodeGen;
+import com.seaofnodes.simple.RegMask;
+import com.seaofnodes.simple.SB;
+import com.seaofnodes.simple.node.*;
+
+public class MemAddX86 extends MemOpX86 {
+ MemAddX86( StoreNode st, Node base, Node idx, int off, int scale, int imm, Node val ) {
+ super(st, st, base, idx, off, scale, imm, val );
+ }
+
+ // Register mask allowed as a result. 0 for no register.
+ @Override public RegMask outregmap() { return null; }
+
+
+ // General form: "add [base + idx<<2 + 12] += src"
+ @Override public void asm(CodeGen code, SB sb) {
+ asm_address(code,sb);
+ if( val()==null ) {
+ if( _imm != 1 && _imm != -1 ) sb.p(" += #").p(_imm);
+ } else sb.p(" += ").p(code.reg(val()));
+ }
+
+ @Override public String op() {
+ return (_imm == 1 ? "inc" : (_imm == -1 ? "dec" : "add")) + _sz;
+ }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MemOpX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MemOpX86.java
new file mode 100644
index 00000000..5a79fb09
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MemOpX86.java
@@ -0,0 +1,90 @@
+package com.seaofnodes.simple.node.cpus.x86_64_v2;
+
+import com.seaofnodes.print.ExprPrinter;
+
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.type.*;
+import java.io.ByteArrayOutputStream;
+import java.lang.StringBuilder;
+
+// Generic X86 memory operand base.
+// inputs:
+// ctrl - will appear for possibly RCE (i.e. array ops)
+// mem - memory dependence edge
+// base - Basic object pointer
+// idx/null - Scaled index offset, or null if none
+// val/null - Value to store, as part of a op-to-mem or op-from-mem. Null for loads, or if an immediate is being used
+// Constants:
+// offset - offset added to base. Can be zero.
+// scale - scale on index; only 0,1,2,4,8 allowed, 0 is only when index is null
+// imm - immediate value to store or op-to-mem, and only when val is null
+public abstract class MemOpX86 extends MemOpNode implements MachNode {
+ final int _off; // Limit 32 bits
+ final int _scale; // Limit 0,1,2,3
+ final int _imm; // Limit 32 bits
+ final char _sz = (char)('0'+(1<<_declaredType.log_size()));
+ MemOpX86( Node op, MemOpNode mop, Node base, Node idx, int off, int scale, int imm ) {
+ super(op,mop);
+ assert base._type instanceof TypeMemPtr && !(base instanceof AddNode);
+ assert (idx==null && scale==0) || (idx!=null && 0<= scale && scale<=3);
+
+ // Copy memory parts from eg the LoadNode over the opcode, e.g. an Add
+ if( op != mop ) {
+ _inputs.set(0,mop.in(0)); // Control from mem op
+ _inputs.set(1,mop.in(1)); // Memory from mem op
+ _inputs.set(2,base); // Base handed in
+ }
+
+ assert ptr() == base;
+ _inputs.setX(3,idx);
+ _off = off;
+ _scale = scale;
+ _imm = imm;
+ }
+
+ // Store-based flavors have a value edge
+ MemOpX86( Node op, MemOpNode mop, Node base, Node idx, int off, int scale, int imm, Node val ) {
+ this(op,mop,base,idx,off,scale,imm);
+ _inputs.setX(4,val);
+ }
+
+ Node idx() { return in(3); }
+ Node val() { return in(4); }
+
+ @Override protected ExprPrinter<Node> _printMach(ExprPrinter<Node> p) { return p.p(".").p(_name); }
+
+ @Override public boolean isMem() { return _type instanceof TypeMem; }
+
+ @Override public String label() { return op(); }
+ @Override public Type compute() { throw Utils.TODO(); }
+ @Override public Node idealize() { throw Utils.TODO(); }
+
+ // Register mask allowed on input i.
+ @Override public RegMask regmap(int i) {
+ if( i==1 ) return null; // Memory
+ if( i==2 ) return x86_64_v2.RMASK; // base
+ if( i==3 ) return x86_64_v2.RMASK; // index
+ if( i==4 ) return x86_64_v2.RMASK; // value
+ return null; // Trailing anti-dependence edges
+ }
+ // Register mask allowed as a result. 0 for no register.
+ @Override public RegMask outregmap() { throw Utils.TODO(); }
+
+ @Override public int encoding(ByteArrayOutputStream bytes) { throw Utils.TODO(); }
+
+
+ // "[base + idx<<2 + 12]"
+ SB asm_address(CodeGen code, SB sb) {
+ sb.p("[").p(code.reg(ptr()));
+ if( idx() != null ) {
+ sb.p("+").p(code.reg(idx()));
+ if( _scale != 0 )
+ sb.p("<<").p(_scale);
+ }
+ if( _off != 0 )
+ sb.p("+").p(_off);
+ return sb.p("]");
+ }
+
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MulFX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MulFX86.java
new file mode 100644
index 00000000..aef813e1
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MulFX86.java
@@ -0,0 +1,30 @@
+package com.seaofnodes.simple.node.cpus.x86_64_v2;
+
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.type.TypeFloat;
+import com.seaofnodes.simple.type.TypeInteger;
+import java.io.ByteArrayOutputStream;
+
+public class MulFX86 extends MachConcreteNode implements MachNode {
+ MulFX86( Node mulf) { super(mulf); }
+
+ // Register mask allowed on input i.
+ @Override public RegMask regmap(int i) { assert i==1 || i==2; return x86_64_v2.XMASK; }
+ // Register mask allowed as a result. 0 for no register.
+ @Override public RegMask outregmap() { return x86_64_v2.XMASK; }
+ // Output is same register as input#1
+ @Override public int twoAddress() { return 1; }
+
+ // Encoding is appended into the byte array; size is returned
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+
+ // General form: "mulf dst *= src"
+ @Override public void asm(CodeGen code, SB sb) {
+ sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" * ").p(code.reg(in(2)));
+ }
+
+ @Override public String op() { return "mulf"; }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MulIX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MulIX86.java
new file mode 100644
index 00000000..e9eb17fc
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MulIX86.java
@@ -0,0 +1,36 @@
+package com.seaofnodes.simple.node.cpus.x86_64_v2;
+
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.type.TypeInteger;
+import java.io.ByteArrayOutputStream;
+
+public class MulIX86 extends MachConcreteNode implements MachNode {
+ final TypeInteger _ti;
+ MulIX86(Node mul, TypeInteger ti) {super(mul); _inputs.pop(); _ti = ti;}
+
+ // Register mask allowed on input i.
+ // This is the normal calling convention
+ @Override public RegMask regmap(int i) {
+ assert i==1;
+ return x86_64_v2.WMASK; }
+ // Register mask allowed as a result. 0 for no register.
+ @Override public RegMask outregmap() { return x86_64_v2.WMASK; }
+
+
+ // Output is same register as input#1
+ @Override public int twoAddress() { return 1; }
+
+ // Encoding is appended into the byte array; size is returned
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+ // General form
+ // General form: "muli dst * #imm"
+ @Override public void asm(CodeGen code, SB sb) {
+ sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" * #");
+ _ti.print(sb);
+ }
+
+ @Override public String op() { return "muli"; }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MulX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MulX86.java
new file mode 100644
index 00000000..15fd9496
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MulX86.java
@@ -0,0 +1,30 @@
+package com.seaofnodes.simple.node.cpus.x86_64_v2;
+
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.type.TypeInteger;
+import java.io.ByteArrayOutputStream;
+
+public class MulX86 extends MachConcreteNode implements MachNode {
+
+ MulX86( Node mul ) { super(mul); }
+
+ // Register mask allowed on input i.
+ @Override public RegMask regmap(int i) { assert i==1 || i==2; return x86_64_v2.RMASK; }
+ // Register mask allowed as a result. 0 for no register.
+ @Override public RegMask outregmap() { return x86_64_v2.WMASK; }
+ // Output is same register as input#1
+ @Override public int twoAddress() { return 1; }
+
+ // Encoding is appended into the byte array; size is returned
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+
+ // General form: "mul dst *= src"
+ @Override public void asm(CodeGen code, SB sb) {
+ sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" * ").p(code.reg(in(2)));
+ }
+
+ @Override public String op() { return "mul"; }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/NewX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/NewX86.java
new file mode 100644
index 00000000..c70e1c54
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/NewX86.java
@@ -0,0 +1,40 @@
+package com.seaofnodes.simple.node.cpus.x86_64_v2;
+
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.node.*;
+import java.io.ByteArrayOutputStream;
+
+public class NewX86 extends NewNode implements MachNode {
+
+ // A pre-zeroed chunk of memory.
+ NewX86( NewNode nnn ) { super(nnn); }
+
+ // Register mask allowed on input i, the size
+ @Override public RegMask regmap(int i) {
+ // Size
+ if( i==2 ) return x86_64_v2.RDI_MASK;
+ // Memory has no register
+ return null;
+ }
+ @Override public RegMask outregmap() { throw Utils.TODO(); }
+
+ // Register mask allowed as a result. Pointer result in standard calling
+ // convention.
+ @Override public RegMask outregmap(int i) {
+ if( i == 0 ) return x86_64_v2.RET_MASK;
+ // Memory has no register
+ return null;
+ }
+
+ // Encoding is appended into the byte array; size is returned
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+
+ // General form: "alloc #bytes"
+ @Override public void asm(CodeGen code, SB sb) {
+ sb.p(code.reg(size()));
+ }
+
+ @Override public String op() { return "alloc"; }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/OrIX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/OrIX86.java
new file mode 100644
index 00000000..d3940838
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/OrIX86.java
@@ -0,0 +1,44 @@
+package com.seaofnodes.simple.node.cpus.x86_64_v2;
+
+import com.seaofnodes.simple.CodeGen;
+import com.seaofnodes.simple.RegMask;
+import com.seaofnodes.simple.SB;
+import com.seaofnodes.simple.Utils;
+import com.seaofnodes.simple.node.MachConcreteNode;
+import com.seaofnodes.simple.node.MachNode;
+import com.seaofnodes.simple.node.Node;
+import com.seaofnodes.simple.type.TypeInteger;
+
+import java.io.ByteArrayOutputStream;
+
+public class OrIX86 extends MachConcreteNode implements MachNode {
+
+ final TypeInteger _ti;
+ OrIX86(Node or, TypeInteger ti) {super(or); _inputs.pop(); _ti = ti;}
+
+ // Register mask allowed on input i.
+ // This is the normal calling convention
+ @Override public RegMask regmap(int i) {
+ assert i==1;
+ return x86_64_v2.WMASK; }
+ // Register mask allowed as a result. 0 for no register.
+ @Override public RegMask outregmap() { return x86_64_v2.WMASK; }
+
+
+ // Output is same register as input#1
+ @Override public int twoAddress() { return 1; }
+
+ // Encoding is appended into the byte array; size is returned
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+
+ // General form
+ // General form: "ori ori | #imm"
+ @Override public void asm(CodeGen code, SB sb) {
+ sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" | #");
+ _ti.print(sb);
+ }
+
+ @Override public String op() { return "ori"; }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/OrX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/OrX86.java
new file mode 100644
index 00000000..7c25bdae
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/OrX86.java
@@ -0,0 +1,33 @@
+package com.seaofnodes.simple.node.cpus.x86_64_v2;
+
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.node.*;
+import java.io.ByteArrayOutputStream;
+
+public class OrX86 extends MachConcreteNode implements MachNode{
+ OrX86(Node or) {
+ super(or);
+ }
+
+ // Register mask allowed on input i.
+ // This is the normal calling convention
+ @Override public RegMask regmap(int i) { assert i==1; return x86_64_v2.WMASK; }
+
+ @Override public RegMask outregmap() { return x86_64_v2.WMASK; }
+
+ // Output is same register as input#1
+ @Override public int twoAddress() { return 1; }
+
+ // Encoding is appended into the byte array; size is returned
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+
+ // General form
+ // General form: "and dst & #imm"
+ @Override public void asm(CodeGen code, SB sb) {
+ sb.p(code.reg(this)).p(" = ").p(code.reg(in(1)));
+ }
+
+ @Override public String op() { return "or"; }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ParmX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ParmX86.java
new file mode 100644
index 00000000..0fcc922d
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ParmX86.java
@@ -0,0 +1,23 @@
+package com.seaofnodes.simple.node.cpus.x86_64_v2;
+
+import com.seaofnodes.simple.RegMask;
+import com.seaofnodes.simple.node.ParmNode;
+import com.seaofnodes.simple.node.MachNode;
+import java.io.ByteArrayOutputStream;
+
+public class ParmX86 extends ParmNode implements MachNode {
+ final RegMask _rmask;
+ ParmX86( ParmNode parm ) {
+ super(parm);
+ // default to int
+ _rmask = x86_64_v2.callInMask(fun().sig(),_idx);
+ }
+
+ // Register mask allowed on input i. 0 for no register.
+ @Override public RegMask regmap(int i) { return null; }
+ // Register mask allowed as a result. Calling convention register
+ @Override public RegMask outregmap() { return _rmask; }
+
+ // Encoding is appended into the byte array. Returns size
+ @Override public int encoding(ByteArrayOutputStream bytes) { return 0; }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ProjX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ProjX86.java
new file mode 100644
index 00000000..77fd7d34
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ProjX86.java
@@ -0,0 +1,25 @@
+package com.seaofnodes.simple.node.cpus.x86_64_v2;
+
+import com.seaofnodes.simple.RegMask;
+import com.seaofnodes.simple.SB;
+import com.seaofnodes.simple.Utils;
+import com.seaofnodes.simple.node.ProjNode;
+import com.seaofnodes.simple.node.MachNode;
+import java.io.ByteArrayOutputStream;
+
+public class ProjX86 extends ProjNode implements MachNode {
+
+ ProjX86( ProjNode p ) { super(p); }
+
+ // Register mask allowed on input i. 0 for no register.
+ @Override public RegMask regmap(int i) { return null; }
+ // Register mask allowed as a result. 0 for no register.
+ @Override public RegMask outregmap() {
+ return ((MachNode)in(0)).outregmap(_idx);
+ }
+
+ // Encoding is appended into the byte array; size is returned
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/RetX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/RetX86.java
new file mode 100644
index 00000000..21db209c
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/RetX86.java
@@ -0,0 +1,48 @@
+package com.seaofnodes.simple.node.cpus.x86_64_v2;
+
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.node.FunNode;
+import com.seaofnodes.simple.node.ReturnNode;
+import com.seaofnodes.simple.node.MachNode;
+import java.io.ByteArrayOutputStream;
+
+// Return
+public class RetX86 extends ReturnNode implements MachNode {
+ RetX86( ReturnNode ret, FunNode fun ) { super(ret, fun); fun.setRet(this); }
+
+ // Correct Nodes outside the normal edges
+ @Override public void postSelect() {
+ FunNode fun = (FunNode)rpc().in(0);
+ _fun = fun;
+ fun.setRet(this);
+ }
+
+ // Register mask allowed on input i.
+ // This is the normal calling convention
+ @Override public RegMask regmap(int i) {
+ return switch( i ) {
+ case 0 -> null;
+ case 1 -> null;
+ case 2 -> x86_64_v2.RET_MASK;
+ case 3 -> null; // RPC is always on stack
+ default -> throw Utils.TODO();
+ };
+ }
+ // Register mask allowed as a result. 0 for no register.
+ @Override public RegMask outregmap() { return null; }
+
+ // Encoding is appended into the byte array; size is returned
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+
+ // Human-readable form appended to the SB. Things like the encoding,
+ // indentation, leading address or block labels not printed here.
+ // Just something like "ld4\tR17=[R18+12] // Load array base".
+ // General form: "op\tdst=src+src"
+ @Override public void asm(CodeGen code, SB sb) {
+ sb.p(code.reg(in(2)));
+ }
+
+ @Override public String op() { return "ret"; }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SarIX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SarIX86.java
new file mode 100644
index 00000000..f5337ee8
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SarIX86.java
@@ -0,0 +1,37 @@
+package com.seaofnodes.simple.node.cpus.x86_64_v2;
+
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.type.TypeInteger;
+import java.io.ByteArrayOutputStream;
+
+// Arithmetic Right Shift
+public class SarIX86 extends MachConcreteNode implements MachNode {
+ final TypeInteger _ti;
+ SarIX86(Node sar, TypeInteger ti) {super(sar); _inputs.pop(); _ti = ti;}
+
+ // Register mask allowed on input i.
+ // This is the normal calling convention
+ @Override public RegMask regmap(int i) {
+ // assert i==1;
+ return x86_64_v2.RMASK; }
+ // Register mask allowed as a result. 0 for no register.
+ @Override public RegMask outregmap() { return x86_64_v2.WMASK; }
+
+ // Output is same register as input#1
+ @Override public int twoAddress() { return 1; }
+
+ // Encoding is appended into the byte array; size is returned
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+
+ // General form
+ // General form: "sari dst << #imm"
+ @Override public void asm(CodeGen code, SB sb) {
+ sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" >> #");
+ _ti.print(sb);
+ }
+
+ @Override public String op() { return "sari"; }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SarX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SarX86.java
new file mode 100644
index 00000000..cb0b18bf
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SarX86.java
@@ -0,0 +1,42 @@
+package com.seaofnodes.simple.node.cpus.x86_64_v2;
+
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.type.Type;
+import com.seaofnodes.simple.type.TypeInteger;
+import java.io.ByteArrayOutputStream;
+import java.util.BitSet;
+import java.lang.StringBuilder;
+
+public class SarX86 extends MachConcreteNode implements MachNode{
+ SarX86(Node sar) {super(sar);}
+
+ // Register mask allowed on input i.
+ // This is the normal calling convention
+
+ // CL register must be used for input(2)
+ @Override public RegMask regmap(int i) {
+ if(i == 1) return x86_64_v2.WMASK;
+ if(i == 2) return x86_64_v2.RCX_MASK;
+ throw Utils.TODO();
+ }
+
+ @Override public RegMask outregmap() { return x86_64_v2.WMASK; }
+
+ // Output is same register as input#1
+ @Override public int twoAddress() { return 1; }
+
+ // Encoding is appended into the byte array; size is returned
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+
+ // General form
+ // General form: "sar dst >> src"
+ @Override public void asm(CodeGen code, SB sb) {
+ sb.p(code.reg(this)).p(" = ").p(code.reg(in(1)));
+ }
+
+ @Override public String op() { return "sar"; }
+
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SetX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SetX86.java
new file mode 100644
index 00000000..25367e03
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SetX86.java
@@ -0,0 +1,39 @@
+package com.seaofnodes.simple.node.cpus.x86_64_v2;
+
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.type.Type;
+import com.seaofnodes.simple.type.TypeInteger;
+import java.io.ByteArrayOutputStream;
+
+// Compare immediate. Sets flags.
+// Corresponds to the x86 instruction "sete && setne".
+// Use result of comparison without jump.
+public class SetX86 extends MachConcreteNode implements MachNode {
+ final String _bop; // One of <,<=,==
+ // Constructor expects input is an X86 and not an Ideal node.
+ SetX86( Node cmp, String bop ) {
+ super(cmp);
+ _inputs.setLen(1); // Pop the cmp inputs
+ // Replace with the matched cmp
+ _inputs.push(cmp);
+ _bop = bop;
+ }
+
+ @Override public RegMask regmap(int i) { assert i==1; return x86_64_v2.FLAGS_MASK; }
+ @Override public RegMask outregmap() { return x86_64_v2.WMASK; }
+
+ // Encoding is appended into the byte array; size is returned
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+
+ @Override public void asm(CodeGen code, SB sb) {
+ sb.p(code.reg(this));
+ String src = code.reg(in(1));
+ if( src!="FLAGS" ) sb.p(" = ").p(src);
+ }
+
+ @Override public String op() { return "set"+_bop; }
+
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShlIX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShlIX86.java
new file mode 100644
index 00000000..61e0d262
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShlIX86.java
@@ -0,0 +1,38 @@
+package com.seaofnodes.simple.node.cpus.x86_64_v2;
+
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.type.Type;
+import com.seaofnodes.simple.type.TypeInteger;
+import java.io.ByteArrayOutputStream;
+import java.util.BitSet;
+
+public class ShlIX86 extends MachConcreteNode implements MachNode{
+ final TypeInteger _ti;
+ ShlIX86(Node shl, TypeInteger ti) {super(shl); _inputs.pop(); _ti = ti;}
+
+ // Register mask allowed on input i.
+ // This is the normal calling convention
+ @Override public RegMask regmap(int i) {
+ //assert i==1;
+ return x86_64_v2.RMASK; }
+ // Register mask allowed as a result. 0 for no register.
+ @Override public RegMask outregmap() { return x86_64_v2.WMASK; }
+
+ // Output is same register as input#1
+ @Override public int twoAddress() { return 1; }
+
+ // Encoding is appended into the byte array; size is returned
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+
+ // General form
+ // General form: "shli dst << #imm"
+ @Override public void asm(CodeGen code, SB sb) {
+ sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" << #");
+ _ti.print(sb);
+ }
+
+ @Override public String op() { return "shli"; }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShlX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShlX86.java
new file mode 100644
index 00000000..b95f9f46
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShlX86.java
@@ -0,0 +1,39 @@
+package com.seaofnodes.simple.node.cpus.x86_64_v2;
+
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.node.*;
+import java.io.ByteArrayOutputStream;
+
+public class ShlX86 extends MachConcreteNode implements MachNode {
+ ShlX86(Node shl) {
+ super(shl);
+ };
+
+ // Register mask allowed on input i.
+ // This is the normal calling convention
+
+ // CL register must be used for input(2)
+ @Override public RegMask regmap(int i) {
+ if(i == 1) return x86_64_v2.WMASK;
+ if(i == 2) return x86_64_v2.RCX_MASK;
+ throw Utils.TODO();
+ }
+
+ @Override public RegMask outregmap() { return x86_64_v2.WMASK; }
+
+ // Output is same register as input#1
+ @Override public int twoAddress() { return 1; }
+
+ // Encoding is appended into the byte array; size is returned
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+
+ // General form
+ // General form: "shl dst << src"
+ @Override public void asm(CodeGen code, SB sb) {
+ sb.p(code.reg(this)).p(" = ").p(code.reg(in(1)));
+ }
+
+ @Override public String op() { return "shl"; }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShrIX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShrIX86.java
new file mode 100644
index 00000000..80115ff2
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShrIX86.java
@@ -0,0 +1,40 @@
+package com.seaofnodes.simple.node.cpus.x86_64_v2;
+
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.type.Type;
+import com.seaofnodes.simple.type.TypeInteger;
+import java.io.ByteArrayOutputStream;
+import java.util.BitSet;
+import java.lang.StringBuilder;
+
+// Logical Right Shift
+public class ShrIX86 extends MachConcreteNode implements MachNode{
+ final TypeInteger _ti;
+ ShrIX86(Node shr, TypeInteger ti) {super(shr); _inputs.pop(); _ti = ti;}
+
+ // Register mask allowed on input i.
+ // This is the normal calling convention
+ @Override public RegMask regmap(int i) {
+ //assert i==1;
+ return x86_64_v2.RMASK; }
+ // Register mask allowed as a result. 0 for no register.
+ @Override public RegMask outregmap() { return x86_64_v2.WMASK; }
+
+ // Output is same register as input#1
+ @Override public int twoAddress() { return 1; }
+
+ // Encoding is appended into the byte array; size is returned
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+
+ // General form
+ // General form: "shri dst >>> #imm"
+ @Override public void asm(CodeGen code, SB sb) {
+ sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" >>> #");
+ _ti.print(sb);
+ }
+
+ @Override public String op() { return "shri"; }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShrX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShrX86.java
new file mode 100644
index 00000000..43fc730b
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShrX86.java
@@ -0,0 +1,38 @@
+package com.seaofnodes.simple.node.cpus.x86_64_v2;
+
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.node.*;
+import java.io.ByteArrayOutputStream;
+
+public class ShrX86 extends MachConcreteNode implements MachNode {
+ ShrX86(Node shr) {
+ super(shr);
+ };
+ // Register mask allowed on input i.
+ // This is the normal calling convention
+
+ // CL register must be used for input(2)
+ @Override public RegMask regmap(int i) {
+ if(i == 1) return x86_64_v2.WMASK;
+ if(i == 2) return x86_64_v2.RCX_MASK;
+ throw Utils.TODO();
+ }
+
+ @Override public RegMask outregmap() { return x86_64_v2.WMASK; }
+
+ // Output is same register as input#1
+ @Override public int twoAddress() { return 1; }
+
+ // Encoding is appended into the byte array; size is returned
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+
+ // General form
+ // General form: "shr dst >>> src"
+ @Override public void asm(CodeGen code, SB sb) {
+ sb.p(code.reg(this)).p(" = ").p(code.reg(in(1)));
+ }
+
+ @Override public String op() { return "shr"; }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/StoreX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/StoreX86.java
new file mode 100644
index 00000000..76bd2afb
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/StoreX86.java
@@ -0,0 +1,31 @@
+package com.seaofnodes.simple.node.cpus.x86_64_v2;
+
+import com.seaofnodes.print.ExprPrinter;
+
+import com.seaofnodes.simple.CodeGen;
+import com.seaofnodes.simple.RegMask;
+import com.seaofnodes.simple.SB;
+import com.seaofnodes.simple.node.Node;
+import com.seaofnodes.simple.node.StoreNode;
+
+public class StoreX86 extends MemOpX86 {
+ StoreX86( StoreNode st, Node base, Node idx, int off, int scale, int imm, Node val ) {
+ super(st,st, base, idx, off, scale, imm, val);
+ }
+
+ @Override public String op() { return "st"+_sz; }
+
+ @Override protected ExprPrinter<Node> _printMach(ExprPrinter<Node> p) {
+ return p.p(".").p(_name).p("=").p(val()==null ? _imm : val()).p(";");
+ }
+
+ // Register mask allowed as a result. 0 for no register.
+ @Override public RegMask outregmap() { return null; }
+
+ // General form: "stN [base + idx<<2 + 12],val"
+ @Override public void asm(CodeGen code, SB sb) {
+ asm_address(code,sb).p(",");
+ if( val()==null ) sb.p("#").p(_imm);
+ else sb.p(code.reg(val()));
+ }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SubFX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SubFX86.java
new file mode 100644
index 00000000..ed19ad79
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SubFX86.java
@@ -0,0 +1,29 @@
+package com.seaofnodes.simple.node.cpus.x86_64_v2;
+
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.node.*;
+import java.io.ByteArrayOutputStream;
+
+public class SubFX86 extends MachConcreteNode implements MachNode {
+ SubFX86( Node subf) { super(subf); }
+
+
+ // Register mask allowed on input i.
+ @Override public RegMask regmap(int i) { assert i==1 || i==2; return x86_64_v2.XMASK; }
+ // Register mask allowed as a result. 0 for no register.
+ @Override public RegMask outregmap() { return x86_64_v2.XMASK; }
+ // Output is same register as input#1
+ @Override public int twoAddress() { return 1; }
+
+ // Encoding is appended into the byte array; size is returned
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+
+ // General form: "subf dst -= src"
+ @Override public void asm(CodeGen code, SB sb) {
+ sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" - ").p(code.reg(in(2)));
+ }
+
+ @Override public String op() { return "subf"; }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SubX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SubX86.java
new file mode 100644
index 00000000..cac59d3b
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SubX86.java
@@ -0,0 +1,29 @@
+package com.seaofnodes.simple.node.cpus.x86_64_v2;
+
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.type.TypeInteger;
+import java.io.ByteArrayOutputStream;
+
+public class SubX86 extends MachConcreteNode implements MachNode {
+ SubX86( Node sub ) { super(sub); }
+
+ // Register mask allowed on input i.
+ @Override public RegMask regmap(int i) { assert i==1 || i==2; return x86_64_v2.RMASK; }
+ // Register mask allowed as a result. 0 for no register.
+ @Override public RegMask outregmap() { return x86_64_v2.WMASK; }
+ // Output is same register as input#1
+ @Override public int twoAddress() { return 1; }
+
+ // Encoding is appended into the byte array; size is returned
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+
+ // General form: "sub dst -= src"
+ @Override public void asm(CodeGen code, SB sb) {
+ sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" - ").p(code.reg(in(2)));
+ }
+
+ @Override public String op() { return "sub"; }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/TFPX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/TFPX86.java
new file mode 100644
index 00000000..c7edfcc4
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/TFPX86.java
@@ -0,0 +1,41 @@
+package com.seaofnodes.simple.node.cpus.x86_64_v2;
+
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.node.ConstantNode;
+import com.seaofnodes.simple.node.MachNode;
+import com.seaofnodes.simple.type.Type;
+import com.seaofnodes.simple.type.TypeFunPtr;
+
+import java.io.ByteArrayOutputStream;
+
+// Function constants
+public class TFPX86 extends ConstantNode implements MachNode {
+ TFPX86( ConstantNode con ) { super(con); }
+ // Register mask allowed on input i. 0 for no register.
+ @Override public RegMask regmap(int i) { return null; }
+ // General int registers
+ @Override public RegMask outregmap() { return x86_64_v2.WMASK; }
+
+ // Encoding is appended into the byte array; size is returned
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+
+ // Human-readable form appended to the SB. Things like the encoding,
+ // indentation, leading address or block labels not printed here.
+ // Just something like "ld4\tR17=[R18+12] // Load array base".
+ // General form: "op\tdst=src+src"
+ @Override public void asm(CodeGen code, SB sb) {
+ String reg = code.reg(this);
+ if( _con == Type.NIL )
+ sb.p(reg).p(",").p(reg);
+ else
+ _con.print(sb.p(reg).p(" #"));
+ }
+
+ @Override public String op() {
+ if( _con == Type.NIL )
+ return "xor";
+ return "ldx"; // Some fancier encoding
+ }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/XorIX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/XorIX86.java
new file mode 100644
index 00000000..d71a49dc
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/XorIX86.java
@@ -0,0 +1,42 @@
+package com.seaofnodes.simple.node.cpus.x86_64_v2;
+
+import com.seaofnodes.simple.CodeGen;
+import com.seaofnodes.simple.RegMask;
+import com.seaofnodes.simple.SB;
+import com.seaofnodes.simple.Utils;
+import com.seaofnodes.simple.node.MachConcreteNode;
+import com.seaofnodes.simple.node.MachNode;
+import com.seaofnodes.simple.node.Node;
+import com.seaofnodes.simple.type.TypeInteger;
+
+import java.io.ByteArrayOutputStream;
+
+public class XorIX86 extends MachConcreteNode implements MachNode {
+
+ final TypeInteger _ti;
+ XorIX86(Node xor, TypeInteger ti) {super(xor); _inputs.pop(); _ti = ti;}
+
+ // Register mask allowed on input i.
+ // This is the normal calling convention
+ @Override public RegMask regmap(int i) { assert i==1; return x86_64_v2.WMASK; }
+ // Register mask allowed as a result. 0 for no register.
+ @Override public RegMask outregmap() { return x86_64_v2.WMASK; }
+
+
+ // Output is same register as input#1
+ @Override public int twoAddress() { return 1; }
+
+ // Encoding is appended into the byte array; size is returned
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+
+ // General form
+ // General form: "ori xori ^ #imm"
+ @Override public void asm(CodeGen code, SB sb) {
+ sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" ^ #");
+ _ti.print(sb);
+ }
+
+ @Override public String op() { return "xori"; }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/x86_64_v2.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/x86_64_v2.java
new file mode 100644
index 00000000..e7e0b874
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/x86_64_v2.java
@@ -0,0 +1,443 @@
+package com.seaofnodes.simple.node.cpus.x86_64_v2;
+
+import com.seaofnodes.simple.CodeGen;
+import com.seaofnodes.simple.Machine;
+import com.seaofnodes.simple.RegMask;
+import com.seaofnodes.simple.Utils;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.type.*;
+
+public class x86_64_v2 extends Machine {
+ // X86-64 V2. Includes e.g. SSE4.2 and POPCNT.
+ @Override public String name() { return "x86_64_v2"; }
+
+ public static int RAX = 0, RCX = 1, RDX = 2, RBX = 3, RSP = 4, RBP = 5, RSI = 6, RDI = 7;
+ public static int R08 = 8, R09 = 9, R10 = 10, R11 = 11, R12 = 12, R13 = 13, R14 = 14, R15 = 15;
+ public static int FLAGS = 32;
+
+ public static int XMM0 = 16, XMM1 = 17, XMM2 = 18, XMM3 = 19, XMM4 = 20, XMM5 = 21, XMM6 = 22, XMM7 = 23;
+ public static int XMM8 = 24, XMM9 = 25, XMM10 = 26, XMM11 = 27, XMM12 = 28, XMM13 = 29, XMM14 = 30, XMM15 = 31;
+
+ // General purpose register mask: pointers and ints, not floats
+ public static RegMask RMASK = new RegMask(0b1111111111111111);
+ // No RSP in the *write* general set.
+ public static RegMask WMASK = new RegMask(0b1111111111101111);
+ // Xmm register mask
+ public static RegMask XMASK = new RegMask( 0b1111111111111111L << XMM0);
+
+ public static RegMask FLAGS_MASK = new RegMask(1L<<FLAGS);
+
+ // Return single int/ptr register
+ public static RegMask RET_MASK = new RegMask(1<<RAX);
+ public static RegMask RET_FMASK = new RegMask(1<<XMM0);
+
+ public static RegMask RDI_MASK = new RegMask(1L<<RDI);
+ public static RegMask RCX_MASK = new RegMask(1L<<RCX);
+ public static RegMask RDX_MASK = new RegMask(1L<<RDX);
+ public static RegMask R08_MASK = new RegMask(1L<<R08);
+ public static RegMask R09_MASK = new RegMask(1L<<R09);
+ public static RegMask RSI_MASK = new RegMask(1L<<RSI);
+
+ public static RegMask XMM0_MASK = new RegMask(1L<<XMM0);
+ public static RegMask XMM1_MASK = new RegMask(1L<<XMM1);
+ public static RegMask XMM2_MASK = new RegMask(1L<<XMM2);
+ public static RegMask XMM3_MASK = new RegMask(1L<<XMM3);
+ public static RegMask XMM4_MASK = new RegMask(1L<<XMM4);
+ public static RegMask XMM5_MASK = new RegMask(1L<<XMM5);
+ public static RegMask XMM6_MASK = new RegMask(1L<<XMM6);
+ public static RegMask XMM7_MASK = new RegMask(1L<<XMM7);
+ public static RegMask[] XMMS = new RegMask[]{
+ XMM0_MASK, XMM1_MASK, XMM2_MASK, XMM3_MASK,
+ XMM4_MASK, XMM5_MASK, XMM6_MASK, XMM7_MASK,
+ };
+
+ // Calling conv metadata
+ public int GPR_COUNT_CONV_WIN64 = 4; // RCX, RDX, R9, R9
+ public int XMM_COUNT_CONV_WIN64 = 4; // XMM0L, XMM1L, XMM2L, XMM3L
+
+ public int GPR_COUNT_CONV_SYSTEM_V = 6; // RDI, RSI, RDX, RCX, R8, R9
+ public int XMM_COUNT_CONV_SYSTEM_V = 4; // XMM0, XMM1, XMM2, XMM3 ....
+ // Human-readable name for a register number, e.g. "RAX".
+ // Hard crash for bad register number, fix yer bugs!
+ public static final String[] REGS = new String[] {
+ "rax" , "rcx" , "rdx" , "rbx" , "rsp" , "rbp" , "rsi" , "rdi" ,
+ "r8" , "r9" , "r10" , "r11" , "r12" , "r13" , "r14" , "r15" ,
+ "xmm0", "xmm1", "xmm2" , "xmm3" , "xmm4" , "xmm5" , "xmm6" , "xmm7" ,
+ "xmm8", "xmm9", "xmm10", "xmm11", "xmm12", "xmm13", "xmm14", "xmm15",
+ "flags",
+ };
+ @Override public String reg( int reg ) {
+ return reg < REGS.length ? REGS[reg] : "[rsp+"+(reg-REGS.length)*4+"]";
+ }
+
+ // Calling convention; returns a machine-specific register
+ // for incoming argument idx.
+ // index 0 for control, 1 for memory, real args start at index 2
+ static int callInArg( TypeFunPtr tfp, int idx ) {
+ int[] cargs = switch( CodeGen.CODE._callingConv ) {
+ case CodeGen.CallingConv.SystemV -> CALLINARG_SYSTEMV_INT;
+ case CodeGen.CallingConv.Win64 -> CALLINARG_WIN64_INT;
+ };
+ if( idx < 2 ) return -1;
+ if( idx >= cargs.length )
+ throw Utils.TODO(); // Pass on stack slot
+ if( tfp.arg(idx-2) instanceof TypeFloat )
+ return XMM0 + (idx-2);
+ return cargs[idx];
+ }
+
+ // WIN64(param passing)
+ static int[] CALLINARG_WIN64_INT = new int[] {
+ -1, // Control, no register
+ -1, // Memory, no register
+ RCX,
+ RDX,
+ R08,
+ R09,
+ };
+ static RegMask[] CALLINARG_WIN64_MASK = new RegMask[] {
+ null, // Control, no register
+ null, // Memory, no register
+ RCX_MASK,
+ RDX_MASK,
+ R08_MASK,
+ R09_MASK,
+ };
+
+ // caller saved(win64)
+ public static final long WIN64_ABI_CALLER_SAVED =
+ (1L << RAX) | (1L << RCX) | (1L << RDX) | (1L << R08) | (1L << R09) | (1L << R10) | (1L << R11);
+
+ // callee saved(win64)
+ public static final long WIN64_ABI_CALLEE_SAVED = ~WIN64_ABI_CALLER_SAVED;
+
+ static int[] CALLINARG_SYSTEMV_INT = new int[] {
+ -1, // Control, no register
+ -1, // Memory, no register
+ RDI,
+ RSI,
+ RDX,
+ RCX,
+ R08,
+ R09,
+ };
+ static RegMask[] CALLINARG_SYSTEMV_MASK = new RegMask[] {
+ null, // Control, no register
+ null, // Memory, no register
+ RDI_MASK,
+ RSI_MASK,
+ RDX_MASK,
+ RCX_MASK,
+ R08_MASK,
+ R09_MASK,
+ };
+ // caller saved(systemv)
+ // caller saved(win64)
+ public static final long SYSTEMV_ABI_CALLER_SAVED =
+ (1L << RAX) | (1L << RDI) | (1L << RSI) | (1L << RCX) | (1L << RDX) | (1L << R08) | (1L << R09) << (1L << R10) << (1L << R11);
+ // callee saved(systemv)
+ public static final long SYSTEMV_ABI_CALLE_SAVED = ~SYSTEMV_ABI_CALLER_SAVED;
+
+
+ static RegMask callInMask( TypeFunPtr tfp, int idx ) {
+ RegMask[] cargs = switch( CodeGen.CODE._callingConv ) {
+ case CodeGen.CallingConv.SystemV -> CALLINARG_SYSTEMV_MASK;
+ case CodeGen.CallingConv.Win64 -> CALLINARG_WIN64_MASK;
+ };
+ if( idx < 2 ) return null;
+ if( idx >= cargs.length )
+ throw Utils.TODO(); // Pass on stack slot
+ if( tfp.arg(idx-2) instanceof TypeFloat )
+ return XMMS[idx-2];
+ return cargs[idx];
+ }
+
+ // Create a split op; any register to any register, including stack slots
+ @Override
+ public Node split() {
+ throw Utils.TODO();
+ }
+
+ // Return a MachNode unconditional branch
+ @Override public CFGNode jump() {
+ throw Utils.TODO();
+ }
+
+ // Break an infinite loop
+ @Override public IfNode never( CFGNode ctrl ) {
+ throw Utils.TODO();
+ }
+
+ // Instruction selection
+ @Override public Node instSelect( Node n ) {
+ return switch( n ) {
+ case AddFNode addf -> addf(addf);
+ case AddNode add -> add(add);
+ case AndNode and -> and(and);
+ case BoolNode bool -> cmp(bool);
+ case CallEndNode cend -> new CallEndX86(cend);
+ case CallNode call -> call(call);
+ case CProjNode c -> new CProjNode(c);
+ case ConstantNode con -> con(con);
+ case DivFNode divf -> new DivFX86(divf);
+ case DivNode div -> new DivX86(div);
+ case FunNode fun -> new FunX86(fun);
+ case IfNode iff -> jmp(iff);
+ case LoadNode ld -> ld(ld);
+ case MemMergeNode mem -> new MemMergeNode(mem);
+ case MulFNode mulf -> new MulFX86(mulf);
+ case MulNode mul -> mul(mul);
+ case NewNode nnn -> new NewX86(nnn);
+ case OrNode or -> or(or);
+ case ParmNode parm -> new ParmX86(parm);
+ case BulkMemPhiNode phi -> new BulkMemPhiNode(phi);
+ case MemPhiNode phi -> new MemPhiNode(phi);
+ case PhiNode phi -> new PhiNode(phi);
+ case ProjNode prj -> prj(prj);
+ case ReadOnlyNode read -> new ReadOnlyNode(read);
+ case ReturnNode ret -> new RetX86(ret,ret.fun());
+ case SarNode sar -> sar(sar);
+ case ShlNode shl -> shl(shl);
+ case ShrNode shr -> shr(shr);
+ case StartNode start -> new StartNode(start);
+ case StopNode stop -> new StopNode(stop);
+ case StoreNode st -> st(st);
+ case SubFNode subf -> new SubFX86(subf);
+ case SubNode sub -> sub(sub);
+ case ToFloatNode tfn -> i2f8(tfn);
+ case ToIntegerNode cvt -> new F8ToIX86(cvt);
+ case XorNode xor -> xor(xor);
+
+ case LoopNode loop -> new LoopNode(loop);
+ case RegionNode region-> new RegionNode(region);
+ default -> throw Utils.TODO();
+ };
+ }
+
+
+ // Attempt a full LEA-style break down.
+ private Node add( AddNode add ) {
+ Node lhs = add.in(1);
+ Node rhs = add.in(2);
+ if( lhs instanceof LoadNode ld && ld.nOuts()==1 )
+ return new AddMemX86(add,address(ld),ld.ptr(),idx,off,scale, imm(rhs),val);
+
+ if( rhs instanceof LoadNode ld && ld.nOuts()==1 )
+ return new AddMemX86(add,address(ld),ld.ptr(),idx,off,scale, imm(lhs),val);
+
+ // Attempt a full LEA-style break down.
+ // Returns one of AddX86, AddIX86, LeaX86, or LHS
+ if( rhs instanceof ConstantNode off && off._con instanceof TypeInteger toff ) {
+ if( lhs instanceof AddNode ladd )
+ // ((base + (idx << scale)) + off)
+ return _lea(add,ladd.in(1),ladd.in(2),toff.value());
+ if( lhs instanceof ShlNode shift )
+ // (idx << scale) + off; no base
+ return _lea(add,null,shift,toff.value());
+
+ // lhs + rhs1
+ if( toff.value()==0 ) return add;
+ return new AddIX86(add, toff);
+ }
+ return _lea(add,lhs,rhs,0);
+ }
+
+
+ private Node addf( AddFNode addf ) {
+ if( addf.in(1) instanceof LoadNode ld && ld.nOuts()==1 )
+ return new AddFMemX86(addf,address(ld),ld.ptr(),idx,off,scale, addf.in(2));
+
+ if( addf.in(2) instanceof LoadNode ld && ld.nOuts()==1 )
+ throw Utils.TODO(); // Swap load sides
+
+ return new AddFX86(addf);
+ }
+
+
+ private Node _lea( Node add, Node base, Node idx, long off ) {
+ int scale = 1;
+ if( base instanceof ShlNode && !(idx instanceof ShlNode) ) throw Utils.TODO(); // Bug in canonicalization, should on RHS
+ if( idx instanceof ShlNode shift && shift.in(2) instanceof ConstantNode shfcon &&
+ shfcon._con instanceof TypeInteger tscale && 0 <= tscale.value() && tscale.value() <= 3 ) {
+ idx = shift.in(1);
+ scale = 1 << ((int)tscale.value());
+ }
+ // (base + idx) + off
+ return off==0 && scale==1
+ ? new AddX86(add)
+ : new LeaX86(add,base,idx,scale,off);
+ }
+
+
+ private Node and(AndNode and) {
+ if( and.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti )
+ return new AndIX86(and, ti);
+ return new AndX86(and);
+ }
+
+ private Node call(CallNode call) {
+ if( call.fptr() instanceof ConstantNode con && con._con instanceof TypeFunPtr tfp )
+ return new CallX86(call, tfp);
+ return new CallRX86( call );
+ }
+
+ // Because X86 flags, a normal ideal Bool is 2 X86 ops: a "cmp" and at "setz".
+ // Ideal If reading from a setz will skip it and use the "cmp" instead.
+ private Node cmp( BoolNode bool ) {
+ Node cmp = _cmp(bool);
+ return new SetX86(cmp,bool.op());
+ }
+ private Node _cmp( BoolNode bool ) {
+ Node lhs = bool.in(1);
+ Node rhs = bool.in(2);
+ if( lhs instanceof LoadNode ld && ld.nOuts()==1 )
+ return new CmpMemX86(bool,address(ld),ld.ptr(),idx,off,scale, imm(rhs),val,false);
+
+ if( rhs instanceof LoadNode ld && ld.nOuts()==1 )
+ return new CmpMemX86(bool,address(ld),ld.ptr(),idx,off,scale, imm(lhs),val,true);
+
+ return rhs instanceof ConstantNode con && con._con instanceof TypeInteger ti
+ ? new CmpIX86(bool, ti)
+ : new CmpX86(bool);
+ }
+
+ private Node con( ConstantNode con ) {
+ return switch( con._con ) {
+ case TypeInteger ti -> new IntX86(con);
+ case TypeFloat tf -> new FltX86(con);
+ case TypeFunPtr tfp -> new TFPX86(con);
+ case TypeMemPtr tmp -> new ConstantNode(con);
+ case TypeNil tn -> throw Utils.TODO();
+ // TOP, BOTTOM, XCtrl, Ctrl, etc. Never any executable code.
+ case Type t -> new ConstantNode(con);
+ };
+ }
+
+ private Node i2f8(ToFloatNode tfn) {
+ assert tfn.in(1)._type instanceof TypeInteger ti;
+ return new I2f8X86(tfn);
+ }
+
+ private Node jmp( IfNode iff ) {
+ // If/Bool combos will match to a Cmp/Set which sets flags.
+ // Most general arith ops will also set flags, which the Jmp needs directly.
+ // Loads do not set the flags, and will need an explicit TEST
+ if( !(iff.in(1) instanceof BoolNode) )
+ iff.setDef(1,new BoolNode.EQ(iff.in(1),new ConstantNode(TypeInteger.ZERO)));
+ return new JmpX86(iff, ((BoolNode)iff.in(1)).op());
+ }
+
+ private Node ld( LoadNode ld ) {
+ return new LoadX86(address(ld),ld.ptr(),idx,off,scale);
+ }
+
+ private Node mul(MulNode mul) {
+ return mul.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti
+ ? new MulIX86(mul, ti)
+ : new MulX86(mul);
+ }
+
+ private Node or(OrNode or) {
+ if( or.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti)
+ return new OrIX86(or, ti);
+ return new OrX86(or);
+ }
+
+ private Node prj( ProjNode prj ) {
+ return new ProjX86(prj);
+ }
+
+ private Node sar( SarNode sar ) {
+ if( sar.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti)
+ return new SarIX86(sar, ti);
+ return new SarX86(sar);
+ }
+
+ private Node shl( ShlNode shl ) {
+ if( shl.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti )
+ return new ShlIX86(shl, ti);
+ return new ShlX86(shl);
+ }
+
+ private Node shr(ShrNode shr) {
+ if( shr.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti )
+ return new ShrIX86(shr, ti);
+ return new ShrX86(shr);
+ }
+
+ private Node st( StoreNode st ) {
+ // Look for "*ptr op= val"
+ Node op = st.val();
+ if( op instanceof AddNode ) {
+ if( op.in(1) instanceof LoadNode ld &&
+ ld.in(0)==st.in(0) &&
+ ld.mem()==st.mem() &&
+ ld.ptr()==st.ptr() &&
+ ld.off()==st.off() ) {
+ if( op instanceof AddNode ) {
+ return new MemAddX86(address(st),st.ptr(),idx,off,scale,imm(op.in(2)),val);
+ }
+ throw Utils.TODO();
+ }
+ }
+
+ return new StoreX86(address(st),st.ptr(),idx,off,scale,imm(st.val()),val);
+ }
+
+ private Node sub( SubNode sub ) {
+ return sub.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti
+ ? new AddIX86(sub, TypeInteger.constant(-ti.value()))
+ : new SubX86(sub);
+ }
+
+ private Node xor(XorNode xor) {
+ if(xor.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti)
+ return new XorIX86(xor, ti);
+ throw Utils.TODO();
+ }
+
+
+ // Gather X86 addressing mode bits prior to constructing. This is a
+ // builder pattern, but saving the bits in a *local* *global* here to keep
+ // mess contained.
+ private static int off, scale, imm;
+ private static Node idx, val;
+ private <N extends MemOpNode> N address( N mop ) {
+ off = scale = imm = 0; // Reset
+ idx = val = null;
+ Node base = mop.ptr();
+ // Skip/throw-away a ReadOnly, only used to typecheck
+ if( base instanceof ReadOnlyNode read ) base = read.in(1);
+ assert !(base instanceof AddNode) && base._type instanceof TypeMemPtr; // Base ptr always, not some derived
+ if( mop.off() instanceof AddNode add && add.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti ) {
+ off = (int)ti.value();
+ assert off == ti.value(); // In 32-bit range
+ idx = add.in(1);
+ if( idx instanceof ShlNode shift && shift.in(2) instanceof ConstantNode shfcon &&
+ shfcon._con instanceof TypeInteger tscale && 0 <= tscale.value() && tscale.value() <= 3 ) {
+ idx = shift.in(1);
+ scale = (int)tscale.value();
+ }
+ } else {
+ if( mop.off() instanceof ConstantNode con && con._con instanceof TypeInteger ti ) {
+ off = (int)ti.value();
+ assert off == ti.value(); // In 32-bit range
+ } else {
+ idx = mop.off();
+ }
+ }
+ return mop;
+ }
+ private int imm( Node xval ) {
+ assert val==null && imm==0;
+ if( xval instanceof ConstantNode con && con._con instanceof TypeInteger ti ) {
+ val = null;
+ imm = (int)ti.value();
+ assert imm == ti.value(); // In 32-bit range
+ } else {
+ val = xval;
+ }
+ return imm;
+ }
+
+}
diff --git a/src/main/java/com/seaofnodes/simple/print/SimpleGraphAdapter.java b/src/main/java/com/seaofnodes/simple/print/SimpleGraphAdapter.java
index 3e23968a..1992a0c0 100644
--- a/src/main/java/com/seaofnodes/simple/print/SimpleGraphAdapter.java
+++ b/src/main/java/com/seaofnodes/simple/print/SimpleGraphAdapter.java
@@ -6,7 +6,7 @@ import com.seaofnodes.simple.node.*;
import com.seaofnodes.simple.node.Node;
import com.seaofnodes.simple.type.TypeMem;
-/** Chapter 18's view of the IR; browser, transport and layout are shared. */
+/** Chapter 19's view of the IR; browser, transport and layout are shared. */
public class SimpleGraphAdapter extends GraphAdapter<Node> {
@Override protected String edgeName(Node n, int i) {
diff --git a/src/main/java/com/seaofnodes/simple/print/SimpleGraphObserver.java b/src/main/java/com/seaofnodes/simple/print/SimpleGraphObserver.java
index b2dd3107..4ec55f4d 100644
--- a/src/main/java/com/seaofnodes/simple/print/SimpleGraphObserver.java
+++ b/src/main/java/com/seaofnodes/simple/print/SimpleGraphObserver.java
@@ -7,7 +7,7 @@ import com.seaofnodes.simple.node.Node;
import java.util.ArrayList;
import java.io.IOException;
-/** Chapter 18's compilation context; capture and event bookkeeping are shared. */
+/** Chapter 19's compilation context; capture and event bookkeeping are shared. */
public class SimpleGraphObserver extends GraphCapture<Node> {
private CodeGen _code;
public SimpleGraphObserver() { super(new SimpleGraphAdapter()); }
@@ -20,16 +20,15 @@ public class SimpleGraphObserver extends GraphCapture<Node> {
_code.parse().opto().typeCheck();
}
@Override protected String phase() { return _code._phase.name(); }
- @Override protected int pos() { return _code.P != null ? _code.P.pos() : -1; }
+ @Override protected int pos() { return _code._phase == CodeGen.Phase.Parse ? _code.P.pos() : -1; }
@Override protected void detach() {
if( _code != null ) _code._obs = null;
_code = null;
}
- @Override protected Node scope() { return _code.P == null ? null : _code.P._scope; }
+ @Override protected Node scope() { return _code.P._scope; }
@Override protected void roots(ArrayList<Node> roots) {
roots.add(_code._stop);
- if( _code.P != null ) {
- roots.add(com.seaofnodes.simple.Parser.START);
+ if( _code._phase == CodeGen.Phase.Parse ) {
roots.addAll(_code.P._xScopes);
}
}
diff --git a/src/main/java/com/seaofnodes/simple/type/Type.java b/src/main/java/com/seaofnodes/simple/type/Type.java
index 34f54be8..1ce5dbd0 100644
--- a/src/main/java/com/seaofnodes/simple/type/Type.java
+++ b/src/main/java/com/seaofnodes/simple/type/Type.java
@@ -236,8 +236,4 @@ public class Type {
// This is used by error messages, and is a shorted print.
public String str() { return STRS[_type]; }
-
- public static void reset() {
- TypeFunPtr.reset();
- }
}
diff --git a/src/main/java/com/seaofnodes/simple/type/TypeFunPtr.java b/src/main/java/com/seaofnodes/simple/type/TypeFunPtr.java
index 61fc71cf..94352790 100644
--- a/src/main/java/com/seaofnodes/simple/type/TypeFunPtr.java
+++ b/src/main/java/com/seaofnodes/simple/type/TypeFunPtr.java
@@ -25,7 +25,6 @@ public class TypeFunPtr extends TypeNil {
// within the TypeTuple. Can be upgraded to a BitSet for larger classes of
// functions. Negative means "these 63 concrete bits plus infinite unknown more"
public final long _fidxs; // 63 unique functions per signature
- public String _name; // Optional debug function name; only for named single function
private TypeFunPtr(byte nil, TypeTuple sig, Type ret, long fidxs) {
super(TFUNPTR,nil);
@@ -35,30 +34,16 @@ public class TypeFunPtr extends TypeNil {
_fidxs = fidxs;
}
- static TypeFunPtr make( byte nil, TypeTuple sig, Type ret, long fidxs ) { return new TypeFunPtr(nil,sig,ret,fidxs).intern(); }
+ public static TypeFunPtr make( byte nil, TypeTuple sig, Type ret, long fidxs ) { return new TypeFunPtr(nil,sig,ret,fidxs).intern(); }
public static TypeFunPtr make( boolean nil, TypeTuple sig, Type ret ) { return make((byte)(nil ? 3 : 2),sig,ret,-1); }
- @Override TypeFunPtr makeFrom( byte nil ) { return nil ==_nil ? this : make( nil,_sig,_ret, _fidxs).setName(_name); }
- public TypeFunPtr makeFrom( Type ret ) { return ret ==_ret ? this : make( _nil,_sig, ret, _fidxs).setName(_name); }
- public TypeFunPtr makeFrom( int fidx ) { return make((byte)2, _sig,_ret,1L<<fidx ).setName(_name); }
-
- // Compute "function indices": FIDX
- private static final HashMap<TypeTuple,Integer> FIDXS = new HashMap<>();
- private static int nextFIDX(TypeTuple sig) {
- Integer i = FIDXS.get(sig);
- int fidx = i==null ? 0 : i;
- FIDXS.put(sig,fidx+1); // Track count per sig
- assert fidx<64; // TODO: need a larger FIDX space
- return fidx; // Return
- }
- public static TypeFunPtr makeFun( TypeTuple sig, Type ret ) {
- return make((byte)2,sig,ret,1L<<nextFIDX(sig));
- }
+ @Override TypeFunPtr makeFrom( byte nil ) { return nil ==_nil ? this : make( nil,_sig,_ret, _fidxs); }
+ public TypeFunPtr makeFrom( Type ret ) { return ret ==_ret ? this : make( _nil,_sig, ret, _fidxs); }
+ public TypeFunPtr makeFrom( int fidx ) { return make((byte)2, _sig,_ret,1L<<fidx ); }
public static TypeFunPtr BOT = make((byte)3,TypeTuple.BOT,Type.BOTTOM,-1);
public static TypeFunPtr TEST = make((byte)2,TypeTuple.TEST,TypeInteger.BOT,1);
public static TypeFunPtr TEST0 = make((byte)3,TypeTuple.TEST,TypeInteger.BOT,3);
- public static TypeFunPtr MAIN = makeFun(TypeTuple.MAIN,Type.BOTTOM).setName("main");
- public static void gather(ArrayList<Type> ts) { ts.add(TEST); ts.add(TEST0); ts.add(BOT); ts.add(MAIN); }
+ public static void gather(ArrayList<Type> ts) { ts.add(TEST); ts.add(TEST0); ts.add(BOT); }
@Override
Type xmeet(Type t) {
@@ -93,15 +78,6 @@ public class TypeFunPtr extends TypeNil {
return _sig == ptr._sig && _ret == ptr._ret && _fidxs == ptr._fidxs && super.eq(ptr);
}
- public TypeFunPtr setName(String name) {
- if( name==null ) return this;
- assert _fidxs > 0 && Long.bitCount(_fidxs) == 1;
- // Name can be set to different things, just for debug
- //assert _name==null || _name.equals(name);
- _name = name;
- return this;
- }
-
@Override public String str() { return print(new SB()).toString();
}
@Override public SB print(SB sb) { return _print(sb,false,true); }
@@ -112,32 +88,20 @@ public class TypeFunPtr extends TypeNil {
if( this==BOT ) return sb.p("FunBot");
if( _nil==0 && _sig==TypeTuple.TOP && _ret==Type.TOP && _fidxs==0 ) return sb.p("FunTop");
sb.p(x()).p("{ ");
- if( n && _name!=null ) sb.p(_name);
- else {
- if( _sig._types!=null )
- for( Type t : _sig._types )
- _print(sb,g,t).p(" ");
- _print(sb.p(g ? "&rarr; " : "-> "),g,_ret).p(" #");
- if( isHigh() ) sb.p("~");
- long fidxs = isHigh() ? ~_fidxs : _fidxs;
- String fidx = fidxs==0 ? ""
- : Long.bitCount(fidxs) == 1 ? ""+Long.numberOfTrailingZeros(fidxs)
- : fidxs == -1 ? "ALL"
- : "b"+Long.toBinaryString(fidxs); // Just some function bits
- sb.p(fidx);
- }
- return sb.p("}").p(q());
+ if( _sig._types!=null )
+ for( Type t : _sig._types )
+ _print(sb,g,t).p(" ");
+ _print(sb.p(g ? "&rarr; " : "-> "),g,_ret).p(" #");
+ if( isHigh() ) sb.p("~");
+ long fidxs = isHigh() ? ~_fidxs : _fidxs;
+ String fidx = fidxs==0 ? ""
+ : Long.bitCount(fidxs) == 1 ? ""+Long.numberOfTrailingZeros(fidxs)
+ : fidxs == -1 ? "ALL"
+ : "b"+Long.toBinaryString(fidxs); // Just some function bits
+ return sb.p(fidx).p("}").p(q());
}
// Usage: for( long fidxs=fidxs(); fidxs!=0; fidxs=nextFIDX(fidxs) { int fidxs = Long.numberOfTrailingZeros(fidxs); ... }
public static long nextFIDX(long fidxs) { return fidxs & (fidxs-1); }
- public static void reset() {
- FIDXS.clear();
- FIDXS.put(MAIN._sig,1);
- for( Type t : INTERN.keySet() )
- if( t instanceof TypeFunPtr tfp && tfp!=MAIN)
- tfp._name = null;
- }
-
}
diff --git a/src/main/java/com/seaofnodes/simple/type/TypeStruct.java b/src/main/java/com/seaofnodes/simple/type/TypeStruct.java
index fff6a7db..89c79a1f 100644
--- a/src/main/java/com/seaofnodes/simple/type/TypeStruct.java
+++ b/src/main/java/com/seaofnodes/simple/type/TypeStruct.java
@@ -50,6 +50,7 @@ public class TypeStruct extends Type {
// Array
public static TypeStruct makeAry(TypeInteger len, int lenAlias, Type body, int bodyAlias) {
assert body instanceof TypeInteger || body instanceof TypeFloat || (body instanceof TypeNil tn && tn.nullable());
+ assert len.isa(TypeInteger.U32);
return make("[" + body.str() + "]",
Field.make("#" ,len , lenAlias,true ),
Field.make("[]",body,bodyAlias,false));
@@ -61,7 +62,7 @@ public class TypeStruct extends Type {
public static final TypeStruct S1 = make("S1", Field.make("a", TypeInteger.BOT, -1, false), Field.make("s2",TypeMemPtr.make((byte)2,S2F),-2, false) );
private static final TypeStruct S2 = make("S2", Field.make("b", TypeFloat .F64, -3, false), Field.make("s1",TypeMemPtr.make((byte)2,S1F),-4, false) );
- private static final TypeStruct ARY = makeAry(TypeInteger.BOT,-1,TypeInteger.BOT,-2);
+ private static final TypeStruct ARY = makeAry(TypeInteger.U32,-1,TypeInteger.BOT,-2);
public static void gather(ArrayList<Type> ts) { ts.add(TEST); ts.add(BOT); ts.add(S1); ts.add(S2); ts.add(ARY); }
@@ -225,6 +226,11 @@ public class TypeStruct extends Type {
private int[] offsets() { // Field byte offsets
// Compute a layout for a collection of fields
assert _fields != null; // No forward refs
+
+ // Array layout is different: len,[pad],body...
+ if( isAry() )
+ return _offs = new int[]{ 0, _fields[1]._type.log_size() < 3 ? 4 : 8 };
+
// Compute a layout
int[] cnts = new int[4]; // Count of fields at log field size
for( Field f : _fields )
diff --git a/src/test/java/com/seaofnodes/simple/BrainFuckTest.java b/src/test/java/com/seaofnodes/simple/BrainFuckTest.java
index 37ecde1c..8f09cb3d 100644
--- a/src/test/java/com/seaofnodes/simple/BrainFuckTest.java
+++ b/src/test/java/com/seaofnodes/simple/BrainFuckTest.java
@@ -63,7 +63,7 @@ for( int pc = 0; pc < program#; pc++ ) {
}
return output;
""");
- code.parse().opto().typeCheck().GCM();
+ code.parse().opto().typeCheck()/*.instSelect("x86_64_v2", "SystemV")*/.GCM().localSched();
assertEquals("Hello World!\n", Eval2.eval(code, 0, 10000));
}
}
diff --git a/src/test/java/com/seaofnodes/simple/Chapter03Test.java b/src/test/java/com/seaofnodes/simple/Chapter03Test.java
index 409e23ed..8212d246 100644
--- a/src/test/java/com/seaofnodes/simple/Chapter03Test.java
+++ b/src/test/java/com/seaofnodes/simple/Chapter03Test.java
@@ -46,7 +46,7 @@ public class Chapter03Test {
@Test
public void testRedeclareVar() {
try {
- new Parser("int a=1; int b=2; int c=0; int b=3; c=a+b;").parse();
+ new CodeGen("int a=1; int b=2; int c=0; int b=3; c=a+b;").parse();
fail();
} catch( RuntimeException e ) {
assertEquals("Redefining name 'b'",e.getMessage());
diff --git a/src/test/java/com/seaofnodes/simple/Chapter04Test.java b/src/test/java/com/seaofnodes/simple/Chapter04Test.java
index d2260e1f..934e794f 100644
--- a/src/test/java/com/seaofnodes/simple/Chapter04Test.java
+++ b/src/test/java/com/seaofnodes/simple/Chapter04Test.java
@@ -34,14 +34,14 @@ public class Chapter04Test {
public void testAddAddMul() {
CodeGen code = new CodeGen("return arg+0+arg;");
code.parse();
- assertEquals("return (arg*2);", code.print());
+ assertEquals("return (arg<<1);", code.print());
}
@Test
public void testPeephole3() {
CodeGen code = new CodeGen("return 1+arg+2+arg+3; ");
code.parse();
- assertEquals("return ((arg*2)+6);", code.print());
+ assertEquals("return ((arg<<1)+6);", code.print());
}
@Test
@@ -90,8 +90,7 @@ public class Chapter04Test {
// Adding functions means `main` does not constant fold the incoming arg value
@Ignore @Test
public void testConstantArg() {
- CodeGen code = new CodeGen("return arg; ", TypeInteger.constant(2));
- code.parse();
+ CodeGen code = new CodeGen("return arg; ", TypeInteger.constant(2), 123L).parse();
assertEquals("return 2;", code.expr().in(1).toString());
}
@@ -139,12 +138,8 @@ public class Chapter04Test {
@Test
public void testBug3() {
- try {
- new Parser("inta=1; return a;").parse();
- fail();
- } catch( RuntimeException e ) {
- assertEquals("Undefined name 'inta'",e.getMessage());
- }
+ try { new CodeGen("inta=1; return a;").parse(); fail(); }
+ catch( RuntimeException e ) { assertEquals("Undefined name 'inta'",e.getMessage()); }
}
@Test
@@ -156,8 +151,8 @@ public class Chapter04Test {
@Test
public void testBug5() {
- Parser parser = new Parser("return arg--2;");
- StopNode ret = parser.parse();
- assertEquals("return (arg--2);", ret.print());
+ CodeGen code = new CodeGen("return arg--2;");
+ code.parse();
+ assertEquals("return (arg--2);", code.print());
}
}
diff --git a/src/test/java/com/seaofnodes/simple/Chapter05Test.java b/src/test/java/com/seaofnodes/simple/Chapter05Test.java
index ff60c695..7a9414a2 100644
--- a/src/test/java/com/seaofnodes/simple/Chapter05Test.java
+++ b/src/test/java/com/seaofnodes/simple/Chapter05Test.java
@@ -64,7 +64,7 @@ else
b=a+1;
return a+b;""");
code.parse().opto();
- assertEquals("return ((arg*2)+Phi(Region,2,3));", code.print());
+ assertEquals("return ((arg<<1)+Phi(Region,2,3));", code.print());
}
@Test
@@ -79,7 +79,7 @@ else
a=b+1;
return a+b;""");
code.parse().opto();
- assertEquals("return ((Phi(Region,(arg*2),arg)+arg)+Phi(Region,4,5));", code.print());
+ assertEquals("return ((Phi(Region,(arg<<1),arg)+arg)+Phi(Region,4,5));", code.print());
}
@Test
@@ -150,79 +150,51 @@ return a;
@Test
public void testTrue() {
- StopNode stop = new Parser("return true;").parse();
- assertEquals("return 1;",stop.ret().print());
+ CodeGen code = new CodeGen("return true;").parse();
+ assertEquals("return 1;",code.print());
}
@Test
public void testHalfDef() {
- try {
- new CodeGen("if( arg==1 ) int b=2; return b;").parse();
- fail();
- } catch( RuntimeException e ) {
- assertEquals("Cannot define a 'b' on one arm of an if",e.getMessage());
- }
+ try { new CodeGen("if( arg==1 ) int b=2; return b;").parse(); fail(); }
+ catch( RuntimeException e ) { assertEquals("Cannot define a 'b' on one arm of an if",e.getMessage()); }
}
@Test
public void testHalfDef2() {
- try {
- new Parser("if( arg==1 ) { int b=2; } else { int b=3; } return b;").parse();
- fail();
- } catch( RuntimeException e ) {
- assertEquals("Undefined name 'b'",e.getMessage());
- }
+ try { new CodeGen("if( arg==1 ) { int b=2; } else { int b=3; } return b;").parse(); fail(); }
+ catch( RuntimeException e ) { assertEquals("Undefined name 'b'",e.getMessage()); }
}
@Test
public void testRegress1() {
- try {
- new CodeGen("if(arg==2) int a=1; else int b=2; return a;").parse();
- fail();
- } catch( RuntimeException e ) {
- assertEquals("Cannot define a 'a' on one arm of an if",e.getMessage());
- }
+ try { new CodeGen("if(arg==2) int a=1; else int b=2; return a;").parse(); fail(); }
+ catch( RuntimeException e ) { assertEquals("Cannot define a 'a' on one arm of an if",e.getMessage()); }
}
@Test
public void testBadNum() {
- try {
- new Parser("return 1-;").parse();
- fail();
- } catch( RuntimeException e ) {
- assertEquals("Syntax error, expected an identifier or expression: ;",e.getMessage());
- }
+ try { new CodeGen("return 1-;").parse(); fail(); }
+ catch( RuntimeException e ) { assertEquals("Syntax error, expected an identifier or expression: ;",e.getMessage()); }
}
@Test
public void testKeyword1() {
- try {
- new Parser("int true=0; return true;").parse();
- fail();
- } catch( RuntimeException e ) {
- assertEquals("Expected an identifier, found 'true'",e.getMessage());
- }
+ try { new CodeGen("int true=0; return true;").parse(); fail(); }
+ catch( RuntimeException e ) { assertEquals("Expected an identifier, found 'true'",e.getMessage()); }
}
@Test
public void testKeyword2() {
- try {
- new Parser("int else=arg; if(else) else=2; else else=1; return else;").parse();
- fail();
- } catch( RuntimeException e ) {
- assertEquals("Expected an identifier, found 'else'",e.getMessage());
- }
+ try { new CodeGen("int else=arg; if(else) else=2; else else=1; return else;").parse(); fail(); }
+ catch( RuntimeException e ) { assertEquals("Expected an identifier, found 'else'",e.getMessage()); }
}
@Test
public void testKeyword3() {
- try {
- new CodeGen("int a=1; ififif(arg);a=2;return a;").parse();
- fail();
- } catch( RuntimeException e ) {
- assertEquals("Undefined name 'ififif'",e.getMessage());
- }
+ try { new CodeGen("int a=1; ififif(arg);a=2;return a;").parse(); fail(); }
+ catch( RuntimeException e ) { assertEquals("Undefined name 'ififif'",e.getMessage()); }
}
}
diff --git a/src/test/java/com/seaofnodes/simple/Chapter06Test.java b/src/test/java/com/seaofnodes/simple/Chapter06Test.java
index 2107cdb0..117531f0 100644
--- a/src/test/java/com/seaofnodes/simple/Chapter06Test.java
+++ b/src/test/java/com/seaofnodes/simple/Chapter06Test.java
@@ -103,8 +103,7 @@ else if( arg==3 )
else
a=5;
return a;
-""", TypeInteger.constant(2));
- code.parse().opto();
+""", TypeInteger.constant(2), 123L).parse().opto();
assertEquals("return 5;", code.print());
}
@@ -124,8 +123,7 @@ else if( arg==3 )
else
a=5;
return a;
-""", TypeInteger.constant(1));
- code.parse().opto();
+""", TypeInteger.constant(1), 123L).parse().opto();
assertEquals("return 3;", code.print());
}
@@ -165,7 +163,7 @@ else if( arg==3 )
else
a=5;
return a;
-""", TypeInteger.constant(1));
+""", TypeInteger.constant(1), 123L);
code.parse().opto();
assertEquals("return 3;", code.print());
}
@@ -186,7 +184,7 @@ else if( arg==3 )
else
a=5;
return a;
-""", TypeInteger.constant(3));
+""", TypeInteger.constant(3), 123L);
code.parse().opto();
assertEquals("return 4;", code.print());
}
@@ -210,7 +208,8 @@ return a+b;
@Test
public void testDemo1True() {
- CodeGen code = new CodeGen("""
+ CodeGen code = new CodeGen(
+"""
arg=1;
int a = 0;
int b = 1;
@@ -220,14 +219,15 @@ if( arg ) {
else b = 3;
}
return a+b;
-""", TypeInteger.constant(1));
+""", TypeInteger.constant(1), 123L);
code.parse().opto();
assertEquals("return 4;", code.print());
}
@Test
public void testDemo1False() {
- CodeGen code = new CodeGen("""
+ CodeGen code = new CodeGen(
+"""
arg=0;
int a = 0;
int b = 1;
@@ -237,7 +237,7 @@ if( arg ) {
else b = 3;
}
return a+b;
-""", TypeInteger.constant(0));
+""", TypeInteger.constant(0), 123L);
code.parse().opto();
assertEquals("return 1;", code.print());
}
@@ -263,7 +263,8 @@ return a+b+c;
@Test
public void testDemo2True() {
- CodeGen code = new CodeGen("""
+ CodeGen code = new CodeGen(
+"""
arg=1;
int a = 0;
int b = 1;
@@ -275,14 +276,15 @@ if( arg ) {
else b = 3;
}
return a+b+c;
-""", TypeInteger.constant(1));
+""", TypeInteger.constant(1), 123L);
code.parse().opto();
assertEquals("return 6;", code.print());
}
@Test
public void testDemo2arg2() {
- CodeGen code = new CodeGen("""
+ CodeGen code = new CodeGen(
+"""
arg=2;
int a = 0;
int b = 1;
@@ -294,7 +296,7 @@ if( arg ) {
else b = 3;
}
return a+b+c;
-""", TypeInteger.constant(2));
+""", TypeInteger.constant(2), 123L);
code.parse().opto();
assertEquals("return 5;", code.print());
}
diff --git a/src/test/java/com/seaofnodes/simple/Chapter08Test.java b/src/test/java/com/seaofnodes/simple/Chapter08Test.java
index 03a2c50a..350df18b 100644
--- a/src/test/java/com/seaofnodes/simple/Chapter08Test.java
+++ b/src/test/java/com/seaofnodes/simple/Chapter08Test.java
@@ -1,8 +1,6 @@
package com.seaofnodes.simple;
-import com.seaofnodes.simple.evaluator.Evaluator;
import com.seaofnodes.simple.node.*;
-import com.seaofnodes.simple.type.TypeInteger;
import org.junit.Assert;
import org.junit.Test;
@@ -147,7 +145,7 @@ return arg;
@Test
public void testBreakOutsideLoop() {
try {
- new Parser("""
+ new CodeGen("""
if(arg <= 10) {
break;
arg = arg + 1;
diff --git a/src/test/java/com/seaofnodes/simple/Chapter09Test.java b/src/test/java/com/seaofnodes/simple/Chapter09Test.java
index b210f7e9..10f4a78b 100644
--- a/src/test/java/com/seaofnodes/simple/Chapter09Test.java
+++ b/src/test/java/com/seaofnodes/simple/Chapter09Test.java
@@ -41,7 +41,7 @@ else {
return x;
""");
code.parse().opto();
- assertEquals("return Phi(Region,(arg*2),(Mul+1));", code.print());
+ assertEquals("return Phi(Region,(arg<<1),(Shl+1));", code.print());
Assert.assertEquals( "2", Eval2.eval(code, 1));
Assert.assertEquals("23", Eval2.eval(code, 11));
}
@@ -255,7 +255,7 @@ while(0==1) while(v0)
v0=1+v0;
""");
code.parse().opto();
- assertEquals("return ~int;", code.print());
+ assertEquals("return 0;", code.print());
}
@Test
diff --git a/src/test/java/com/seaofnodes/simple/Chapter11Test.java b/src/test/java/com/seaofnodes/simple/Chapter11Test.java
index 9fd392d2..14819869 100644
--- a/src/test/java/com/seaofnodes/simple/Chapter11Test.java
+++ b/src/test/java/com/seaofnodes/simple/Chapter11Test.java
@@ -48,17 +48,11 @@ public class Chapter11Test {
assertEquals("S{x=1,y=2,z=3}",Eval2.eval(code,4));
}
- @Test public void testLateSliceWorklist() throws ReflectiveOperationException {
+ @Test public void testLateSliceWorklist() {
// Seed 97 exposed a store selecting a bulk predecessor without queuing
// it. Keep both the worklist assertion and the final heap check.
for (int seed : new int[]{0,97,123,456}) {
- var code = new CodeGen(Chapter10Test.NESTED_MEMORY).parse();
- var field = IterPeeps.class.getDeclaredField("WORK");
- field.setAccessible(true);
- Object work = field.get(null);
- field = work.getClass().getDeclaredField("_R");
- field.setAccessible(true);
- ((java.util.Random)field.get(work)).setSeed(seed);
+ var code = new CodeGen(Chapter10Test.NESTED_MEMORY,com.seaofnodes.simple.type.TypeInteger.BOT,seed).parse();
code.opto();
assertEquals("S{x=9,y=25,z=17}",Eval2.eval(code,4));
}
diff --git a/src/test/java/com/seaofnodes/simple/Chapter12Test.java b/src/test/java/com/seaofnodes/simple/Chapter12Test.java
index a15132d4..90aaf631 100644
--- a/src/test/java/com/seaofnodes/simple/Chapter12Test.java
+++ b/src/test/java/com/seaofnodes/simple/Chapter12Test.java
@@ -183,7 +183,7 @@ return new S1.s=new S2;
struct I {int i;};
struct P { !I? !pi; };
!P !p1 = new P;
-!P p2 = new P;
+!P !p2 = new P;
p2.pi = new I;
p2.pi.i = 2;
if (arg) p1 = new P;
diff --git a/src/test/java/com/seaofnodes/simple/Chapter15Test.java b/src/test/java/com/seaofnodes/simple/Chapter15Test.java
index 97f695bf..c38dfb00 100644
--- a/src/test/java/com/seaofnodes/simple/Chapter15Test.java
+++ b/src/test/java/com/seaofnodes/simple/Chapter15Test.java
@@ -142,7 +142,7 @@ struct S { int x; flt y; };
!S !s = new S; s.x=99; s.y = 3.14;
// Double-d array of Ss. Fill in one row.
-!S?[]?[] iss = new !S?[]?[2];
+!S?[]?[] !iss = new !S?[]?[2];
iss[0] = new !S?[7];
iss[0][2] = s;
@@ -151,10 +151,12 @@ flt rez = 1.2;
if( iss[arg] )
if( iss[arg][2] )
rez = iss[arg][2].y;
-return rez;
-""");
- try { code.parse().opto().typeCheck(); fail(); }
- catch( Exception e ) { assertEquals("Might be null accessing '[]'",e.getMessage()); }
+return rez;""");
+ code.parse().opto().typeCheck();
+ assertEquals("return Phi(Region,Phi(Region,.y,1.2),1.2);", code.print());
+ assertEquals("3.14", Eval2.eval(code, 0));
+ try { Eval2.eval(code, 10); fail(); } // Fails AIOOBE in Eval2, no range checks
+ catch( Exception e ) { assertEquals("Index 10 out of bounds for length 2",e.getMessage()); }
}
@Test
diff --git a/src/test/java/com/seaofnodes/simple/Chapter17bTest.java b/src/test/java/com/seaofnodes/simple/Chapter17bTest.java
index e83b43af..19cc25d9 100644
--- a/src/test/java/com/seaofnodes/simple/Chapter17bTest.java
+++ b/src/test/java/com/seaofnodes/simple/Chapter17bTest.java
@@ -39,7 +39,7 @@ return arg++;
return arg+++arg++;
""");
code.parse().opto();
- assertEquals("return ((arg*2)+1);", code.print());
+ assertEquals("return ((arg<<1)+1);", code.print());
assertEquals("1", Eval2.eval(code, 0));
}
@@ -50,7 +50,7 @@ return arg+++arg++;
return -arg---arg--;
""");
code.parse().opto();
- assertEquals("return (-((arg*2)+-1));", code.print());
+ assertEquals("return (-((arg<<1)+-1));", code.print());
assertEquals("1", Eval2.eval(code, 0));
}
@@ -344,7 +344,7 @@ var b = arg ? new Bar : null;
return b ? b.x++ + b.x++ : -1;
""");
code.parse().opto();
- assertEquals("return Phi(Region,((.x*2)+1),-1);", code.print());
+ assertEquals("return Phi(Region,((.x<<1)+1),-1);", code.print());
assertEquals("-1", Eval2.eval(code, 0));
assertEquals("1", Eval2.eval(code, 1));
}
@@ -357,7 +357,7 @@ var b = arg ? new Bar;
return b ? b.x++ + b.x++ : -1;
""");
code.parse().opto();
- assertEquals("return Phi(Region,((.x*2)+1),-1);", code.print());
+ assertEquals("return Phi(Region,((.x<<1)+1),-1);", code.print());
assertEquals("-1", Eval2.eval(code, 0));
assertEquals("1", Eval2.eval(code, 1));
}
diff --git a/src/test/java/com/seaofnodes/simple/Chapter18Test.java b/src/test/java/com/seaofnodes/simple/Chapter18Test.java
index 7f2b92ac..f965edd4 100644
--- a/src/test/java/com/seaofnodes/simple/Chapter18Test.java
+++ b/src/test/java/com/seaofnodes/simple/Chapter18Test.java
@@ -9,30 +9,6 @@ import static org.junit.Assert.fail;
import org.junit.Ignore;
public class Chapter18Test {
- @Test public void testFloatArrays() {
- for( String type : new String[]{"flt","f64","f32"} ) {
- CodeGen zero = new CodeGen("var a = new "+type+"[arg]; return a[arg-1];")
- .parse().opto().typeCheck();
- assertEquals("0.0",Eval2.eval(zero,3));
- new CodeGen(type+" x = arg; var a = new "+type+"[arg]; a[0] = x; return a[0];")
- .parse().opto().typeCheck();
- CodeGen loop = new CodeGen("var a = new "+type+"[arg]; "+
- "for(int i=0; i<arg; i++) a[i] += i+0.25; return a[0]+a[arg-1];")
- .parse().opto().typeCheck();
- assertEquals("0.5",Eval2.eval(loop,1));
- assertEquals("3.5",Eval2.eval(loop,4));
-
- CodeGen rounded = new CodeGen("var a = new "+type+"[arg]; "+
- "for(int i=0; i<arg; i++) a[i] = 16777216.0+i; return a[arg-1];")
- .parse().opto().typeCheck();
- assertEquals(type.equals("f32") ? "1.6777216E7" : "1.6777217E7",Eval2.eval(rounded,2));
- }
- CodeGen rounded = new CodeGen("var a = new f32[arg]; "+
- "for(int i=0; i<arg; i++) a[i] = 16777217.0; return a[arg-1];")
- .parse().opto().typeCheck();
- assertEquals("1.6777216E7",Eval2.eval(rounded,2));
- }
-
@Test public void testNeverExitLoopDepth() {
String[] sources = {
"if(arg) while(1) {} return 7;",
@@ -204,7 +180,7 @@ return sq;
""");
code.parse().opto();
assertEquals("Stop[ return { sq}; return (Parm_x(sq,int)*x); ]", code._stop.toString());
- assertEquals("{ sq}", Eval2.eval(code, 3));
+ assertEquals("{ int -> int #1}", Eval2.eval(code, 3));
}
@Test
@@ -216,7 +192,7 @@ var sq = { int x ->
};
return sq(arg)+sq(3);
""");
- code.parse().opto();
+ code.parse().opto().typeCheck().GCM().localSched();
assertEquals("Stop[ return (sq( 3)+sq( arg)); return (Parm_x(sq,int,3,arg)*x); ]", code._stop.toString());
assertEquals("13", Eval2.eval(code, 2));
}
@@ -255,7 +231,7 @@ var fcn = arg ? { int x -> x*x; } : { int x -> x+x; };
return fcn(3);
""");
code.parse().opto();
- assertEquals("Stop[ return Phi(Region,{ int -> int #1},{ int -> int #2})( 3); return (Parm_x($fun,int,3)*x); return (Parm_x($fun,int,3)*2); ]", code._stop.toString());
+ assertEquals("Stop[ return Phi(Region,{ int -> int #1},{ int -> int #2})( 3); return (Parm_x($fun,int,3)*x); return (Parm_x($fun,int,3)<<1); ]", code._stop.toString());
assertEquals("6", Eval2.eval(code, 0));
assertEquals("9", Eval2.eval(code, 1));
}
@@ -264,7 +240,7 @@ return fcn(3);
@Test
public void testFcn5() {
CodeGen code = new CodeGen("val fact = { int x -> x <= 1 ? 1 : x*fact(x-1); }; return fact(arg);");
- code.parse().opto();
+ code.parse().opto().typeCheck();
assertEquals("Stop[ return fact( arg); return Phi(Region,1,(Parm_x(fact,int,arg,(x-1))*fact( Sub))); ]", code._stop.toString());
assertEquals( "1", Eval2.eval(code, 0));
assertEquals( "1", Eval2.eval(code, 1));
@@ -319,7 +295,6 @@ for(;;) {
assertEquals("3", Eval2.eval(code, 0));
}
-
@Test
public void testFcn9() {
CodeGen code = new CodeGen(
@@ -336,6 +311,31 @@ for(;;) {
assertEquals(null, Eval2.eval(code, 0));
}
+
+ @Test
+ public void testFcn10() {
+ CodeGen code = new CodeGen(
+"""
+struct Person {
+ int age;
+};
+
+val fcn = { !Person?[] !ps, int x ->
+ val tmp = ps[x];
+ if( ps[x] )
+ ps[x].age++;
+};
+
+var ps = new !Person?[2];
+ps[0] = new Person;
+ps[1] = new Person;
+fcn(ps,1);
+""");
+ code.parse().opto().typeCheck().GCM().localSched();
+ assertEquals("return 0;", code._stop.toString());
+ assertEquals("0", Eval2.eval(code, 0));
+ }
+
// Function break
@Test
public void testErr1() {
diff --git a/src/test/java/com/seaofnodes/simple/Chapter19Test.java b/src/test/java/com/seaofnodes/simple/Chapter19Test.java
new file mode 100644
index 00000000..1abda81f
--- /dev/null
+++ b/src/test/java/com/seaofnodes/simple/Chapter19Test.java
@@ -0,0 +1,529 @@
+package com.seaofnodes.simple;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.type.*;
+
+
+import org.junit.Test;
+import static org.junit.Assert.*;
+import static org.junit.Assert.fail;
+import org.junit.Ignore;
+
+public class Chapter19Test {
+ @Test public void testPrintingFunctionLookup() {
+ var code = new CodeGen("return 0;").parse();
+ var tfp = TypeFunPtr.make((byte)2,TypeTuple.make(TypeInteger.constant(987654)),TypeInteger.constant(123456),1L<<30);
+ var con = new ConstantNode(tfp);
+ var call = new CallNode(null,code._start,con,con);
+ // Diagnostic lookup may construct/intern the return-erased linker key.
+ con.toString();
+ org.junit.Assert.assertNull(call.name());
+
+ var fun = new FunNode(null,tfp);
+ fun._name = "printerTarget";
+ fun.addDef(code._start);
+ code.link(fun);
+ var otherReturn = tfp.makeFrom(TypeInteger.constant(654321));
+ org.junit.Assert.assertSame(fun,code.link(otherReturn));
+ org.junit.Assert.assertTrue(con.toString().contains("printerTarget"));
+ assertEquals("printerTarget",call.name());
+ }
+
+
+ @Ignore
+ @Test
+ public void testJig() {
+ CodeGen code = new CodeGen("""
+return 0;
+""");
+ code.parse().opto().typeCheck();
+ assertEquals("return 0;", code._stop.toString());
+ assertEquals("0", Eval2.eval(code, 2));
+ }
+
+
+ @Test
+ public void testString() {
+ CodeGen code = new CodeGen(
+"""
+struct String {
+ u8[] !cs;
+ int _hashCode;
+};
+
+val equals = { !String !self, !String s ->
+ if( self == s ) return true;
+ if( self.cs# != s.cs# ) return false;
+ for( int i=0; i< self.cs#; i++ )
+ if( self.cs[i] != s.cs[i] )
+ return false;
+ return true;
+};
+
+val hashCode = { !String self ->
+ self._hashCode
+ ? self._hashCode
+ : (self._hashCode = _hashCodeString(self));
+};
+
+val _hashCodeString = { !String self ->
+ int hash=0;
+ if( self.cs ) {
+ for( int i=0; i< self.cs#; i++ )
+ hash = hash*31 + self.cs[i];
+ }
+ if( !hash ) hash = 123456789;
+ return hash;
+};
+
+!String !s = new String { cs = new u8[17]; };
+s.cs[0] = 67; // C
+s.cs[1] = 108; // l
+hashCode(s);""");
+ code.parse().opto().typeCheck().GCM().localSched();
+ assertEquals("Stop[ return Phi(Region,123456789,Phi(Loop,0,(.[]+((Phi_hash<<5)-Phi_hash)))); return Phi(Region,1,0,0,1); ]", code._stop.toString());
+ assertEquals("-2449306563677080489", Eval2.eval(code, 2));
+ }
+
+ @Test
+ public void testBasic0() {
+ CodeGen code = new CodeGen("return 0;").parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched();
+ assertEquals("return 0;", code._stop.toString());
+ }
+
+ @Test
+ public void testBasic1() {
+ CodeGen code = new CodeGen("return arg+1;").parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched();
+ assertEquals("return (inc,arg);", code._stop.toString());
+ }
+
+ @Test
+ public void testBasic2() {
+ CodeGen code = new CodeGen("return -17;").parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched();
+ assertEquals("return -17;", code._stop.toString());
+ }
+
+
+ @Test
+ public void testBasic3() {
+ CodeGen code = new CodeGen("return arg==1;").parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched();
+ assertEquals("return (set==,(cmp,arg));", code._stop.toString());
+ }
+
+ @Test
+ public void testBasic4() {
+ CodeGen code = new CodeGen("return arg<<1;").parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched();
+ assertEquals("return (shli,arg);", code._stop.toString());
+ }
+
+ @Test
+ public void testBasic5() {
+ CodeGen code = new CodeGen("return arg >> 1;").parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched();
+ assertEquals("return (sari,arg);", code._stop.toString());
+ }
+
+ @Test
+ public void testBasic6() {
+ CodeGen code = new CodeGen("return arg >>> 1;").parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched();
+ assertEquals("return (shri,arg);", code._stop.toString());
+ }
+
+ @Test
+ public void testBasic7() {
+ CodeGen code = new CodeGen("return arg / 2;").parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched();
+ assertEquals("return (div,arg,2);", code._stop.toString());
+ }
+
+ @Test
+ public void testBasic8() {
+ CodeGen code = new CodeGen("return arg * 6;").parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched();
+ assertEquals("return (muli,arg);", code._stop.toString());
+ }
+
+ @Test
+ public void testBasic9() {
+ CodeGen code = new CodeGen("return arg & 2;").parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched();
+ assertEquals("return (andi,arg);", code._stop.toString());
+ }
+
+ @Test
+ public void testBasic10() {
+ CodeGen code = new CodeGen("return arg | 2;").parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched();
+ assertEquals("return (ori,arg);", code._stop.toString());
+ }
+
+ @Test
+ public void testBasic11() {
+ CodeGen code = new CodeGen("return arg ^ 2;").parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched();
+ assertEquals("return (xori,arg);", code._stop.toString());
+ }
+
+ @Test
+ public void testBasic12() {
+ CodeGen code = new CodeGen("return arg + 2.0;").parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched();
+ assertEquals("return (addf,(i2f8,arg),2.0f);", code._stop.toString());
+ }
+
+ @Test
+ public void testBasic13() {
+ CodeGen code = new CodeGen("return arg - 2.0;").parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched();
+ assertEquals("return (subf,(i2f8,arg),2.0f);", code._stop.toString());
+ }
+
+ @Test
+ public void testBasic14() {
+ CodeGen code = new CodeGen("return arg * 2.0;").parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched();
+ assertEquals("return (mulf,(i2f8,arg),2.0f);", code._stop.toString());
+ }
+
+ @Test
+ public void testBasic15() {
+ CodeGen code = new CodeGen("return arg / 2.0;").parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched();
+ assertEquals("return (divf,(i2f8,arg),2.0f);", code._stop.toString());
+ }
+
+ @Test
+ public void testBasic16() {
+ CodeGen code = new CodeGen(
+"""
+int arg1 = arg + 1;
+return arg1 / arg;""");
+ code.parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched();
+ assertEquals("return (div,(inc,arg),arg);", code._stop.toString());
+ }
+
+ @Test
+ public void testBasic17() {
+ CodeGen code = new CodeGen(
+"""
+int arg1 = arg + 1;
+return arg1 * arg;
+""");
+ code.parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched();
+ assertEquals("return (mul,(inc,arg),arg);", code._stop.toString());
+ }
+
+ @Test
+ public void testBasic18() {
+ CodeGen code = new CodeGen(
+ """
+ int arg1 = arg + 1;
+ return arg1 << arg;
+ """);
+ code.parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched();
+ assertEquals("return (shl,(inc,arg),arg);", code._stop.toString());
+ }
+
+ @Test
+ public void testBasic19() {
+ CodeGen code = new CodeGen(
+ """
+ int arg1 = arg + 1;
+ return arg1 >> arg;
+ """);
+ code.parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched();
+ assertEquals("return (sar,(inc,arg),arg);", code._stop.toString());
+ }
+
+ @Test
+ public void testBasic20() {
+ CodeGen code = new CodeGen(
+ """
+ int arg1 = arg + 1;
+ return arg1 >>> arg;
+ """);
+ code.parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched();
+ assertEquals("return (shr,(inc,arg),arg);", code._stop.toString());
+ }
+
+ @Test
+ public void testToFloat() {
+ CodeGen code = new CodeGen("""
+ int a = arg;
+ return a + 2.0;
+ """
+ ).parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched();
+ assertEquals("return (addf,(i2f8,arg),2.0f);", code._stop.toString());
+ }
+
+ @Test
+ public void testIfStmt() {
+ CodeGen code = new CodeGen(
+"""
+int a = 1;
+if (arg == 1)
+ a = arg+2;
+else {
+ a = arg-3;
+}
+return a;""");
+ code.parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched();
+ assertEquals("return Phi(Region,(addi,arg),(addi,arg));", code.print());
+ }
+
+ @Test
+ public void testIfMerge2() {
+ CodeGen code = new CodeGen(
+"""
+int a=arg+1;
+int b=arg+2;
+if( arg==1 )
+ b=b+a;
+else
+ a=b+1;
+return a+b;""");
+ code.parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched();
+ assertEquals("return (add,(add,Phi(Region,(shli,arg),arg),arg),Phi(Region,4,5));", code.print());
+ }
+
+ @Test
+ public void testLoop() {
+ CodeGen code = new CodeGen(
+"""
+int sum=0;
+for( int i=0; i<arg; i++ )
+ sum += i;
+return sum;""");
+ code.parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched();
+ assertEquals("return Phi(Loop,0,(add,Phi_sum,Phi(Loop,0,(inc,Phi_i))));", code.print());
+ }
+
+ @Test
+ public void testAlloc1() {
+ CodeGen code = new CodeGen(
+"""
+struct S { int a; !S? !c; };
+return new S;""");
+ code.parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched();
+ assertEquals("return S;", code.print());
+ }
+
+ @Test
+ public void testLea1() {
+ CodeGen code = new CodeGen("int x = arg/3; return arg+x+7;");
+ code.parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched();
+ assertEquals("return (lea,arg,(div,arg,3));", code.print());
+ }
+
+ @Test
+ public void testLea2() {
+ CodeGen code = new CodeGen("int x = arg/3; return arg+x*4+7;");
+ code.parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched();
+ assertEquals("return (lea,arg,(div,arg,3));", code.print());
+ }
+
+ @Test
+ public void testLea3() {
+ CodeGen code = new CodeGen("int x = arg/3; return x*4+arg;");
+ code.parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched();
+ assertEquals("return (lea,arg,(div,arg,3));", code.print());
+ }
+
+ @Test
+ public void testAlloc2() {
+ CodeGen code = new CodeGen("int[] !xs = new int[3]; xs[arg]=1; return xs[arg&1];");
+ code.parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched();
+ assertEquals("return .[];", code.print());
+ }
+
+ @Test
+ public void testAlloc3() {
+ CodeGen code = new CodeGen("int[] !xs = new int[3]; xs[arg]=1; return xs[arg&1]+3;");
+ code.parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched();
+ assertEquals("return .[];", code.print());
+ }
+
+ @Test
+ public void testArray1() {
+ CodeGen code = new CodeGen(
+"""
+int[] !ary = new int[arg];
+// Fill [0,1,2,3,4,...]
+for( int i=0; i<ary#; i++ )
+ ary[i] = i;
+// Fill [0,1,3,6,10,...]
+for( int i=0; i<ary#-1; i++ )
+ ary[i+1] += ary[i];
+return ary[1] * 1000 + ary[3]; // 1 * 1000 + 6
+""");
+ code.parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched();
+ assertEquals("return .[];", code.print());
+ }
+
+ @Test
+ public void testArray2() {
+ CodeGen code = new CodeGen(
+"""
+flt[] !A = new flt[arg], !B = new flt[arg];
+// Fill [0,1,2,3,4,...]
+for( int i=0; i<A#; i++ )
+ A[i] = i;
+for( int i=0; i<A#; i++ )
+ B[i] += A[i];
+""");
+ code.parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched();
+ assertEquals("return 0;", code.print());
+ }
+
+ @Test
+ public void testArray3() {
+ CodeGen code = new CodeGen(
+"""
+byte[] !A = new byte[arg];
+for( int i=0; i<A#; i++ )
+ A[i]++;
+return A[1];
+""");
+ code.parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched();
+ assertEquals("return .[];", code.print());
+ }
+
+ @Test
+ public void testNewton() {
+ CodeGen code = new CodeGen(
+"""
+// Newtons approximation to the square root
+val sqrt = { flt x ->
+ flt guess = x;
+ while( 1 ) {
+ flt next = (x/guess + guess)/2;
+ if( next == guess ) return guess;
+ guess = next;
+ }
+};
+flt farg = arg;
+return sqrt(farg);
+""");
+ code.parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched();
+ assertEquals("return Phi(Loop,(i2f8,arg),(divf,(addf,(divf,i2f8,Phi_guess),Phi_guess),2.0f));", code.print());
+ };
+
+
+ @Test
+ public void sieveOfEratosthenes() {
+ CodeGen code = new CodeGen(
+"""
+var ary = new bool[arg], primes = new int[arg];
+var nprimes=0, p=0;
+// Find primes while p^2 < arg
+for( p=2; p*p < arg; p++ ) {
+ // skip marked non-primes
+ while( ary[p] ) p++;
+ // p is now a prime
+ primes[nprimes++] = p;
+ // Mark out the rest non-primes
+ for( int i = p + p; i < ary#; i += p )
+ ary[i] = true;
+}
+// Now just collect the remaining primes, no more marking
+for( ; p < arg; p++ )
+ if( !ary[p] )
+ primes[nprimes++] = p;
+// Copy/shrink the result array
+var !rez = new int[nprimes];
+for( int j=0; j<nprimes; j++ )
+ rez[j] = primes[j];
+return rez;
+""");
+ code.parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched();
+ assertEquals("return [int];", code.print());
+ //assertEquals("int[ 2,3,5,7,11,13,17,19]",Eval2.eval(code, 20));
+ }
+
+
+ @Test
+ public void testFcn1() {
+ CodeGen code = new CodeGen(
+"""
+val fcn = arg ? { int x -> x*x; } : { int x -> x+x; };
+return fcn(2)*10 + fcn(3);
+""");
+ code.parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched();
+ assertEquals("Stop[ return (add,Phi(Region,{ int -> int #1},{ int -> int #2})( 3),(muli,Phi_( 2))); return (mul,Parm_x($fun,int,3,2),x); return (shli,Parm_x($fun,int,3,2)); ]", code.print());
+ }
+
+ @Test
+ public void testFcn2() {
+ CodeGen code = new CodeGen(
+"""
+val sq = { int x -> x*x; };
+return sq(arg) + sq(3);
+""");
+ code.parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched();
+ assertEquals("Stop[ return (add,sq(),sq()); return (mul,Parm_x(sq,int,3,arg),x); ]", code.print());
+ }
+
+ @Test public void testSelectedMemory() {
+ for( String cpu : new String[]{"x86_64_v2","riscv"} )
+ for( String src : new String[]{Chapter10Test.NESTED_MEMORY,Chapter16Test.CONSTRUCTOR_MEMORY,
+ Chapter18Test.CALL_MEMORY,Chapter18Test.RECURSIVE_MEMORY} ) {
+ var code = new CodeGen(src).parse().opto().typeCheck();
+ var aliases = memoryPhis(code._stop);
+ code.instSelect(cpu,"SystemV");
+ assertEquals(aliases,memoryPhis(code._stop));
+ code.GCM().localSched();
+ code._stop.walk(n -> {
+ if( !(n instanceof CFGNode) && !(n instanceof ProjNode) )
+ assertTrue(n.in(0) instanceof CFGNode);
+ if( n instanceof NewNode nn ) {
+ assertEquals(2,((TypeTuple)nn._type)._types.length);
+ assertTrue(nn.mem() instanceof MemMergeNode);
+ assertNull(nn.mem().in(1)); // Partial allocation input.
+ MachNode mach = (MachNode)nn;
+ assertNull(mach.regmap(1));
+ assertNotNull(mach.regmap(2));
+ assertNotNull(mach.outregmap(0));
+ assertNull(mach.outregmap(1));
+ }
+ if( n instanceof CallNode call ) {
+ assertEquals(4,call.nIns()); // ctrl, memory, two args; direct target is embedded.
+ }
+ return null;
+ });
+ }
+ }
+
+ private static java.util.ArrayList<String> memoryPhis(Node stop) {
+ var phis = new java.util.ArrayList<String>();
+ // Count only definitions reachable from Stop, as instruction selection does.
+ // A dead Phi cycle can still appear through the reverse use edges.
+ var seen = new java.util.BitSet();
+ var work = new java.util.ArrayList<Node>();
+ work.add(stop);
+ for( int i=0; i<work.size(); i++ ) {
+ Node n = work.get(i);
+ if( n==null || seen.get(n._nid) ) continue;
+ seen.set(n._nid);
+ for( Node def : n._inputs ) work.add(def);
+ if( n instanceof MemPhiNode phi ) phis.add("alias:"+phi._alias);
+ if( n instanceof BulkMemPhiNode phi ) phis.add("bulk:"+phi._aliases);
+ assertFalse(n instanceof PhiNode && n.isMem() &&
+ !(n instanceof MemPhiNode) && !(n instanceof BulkMemPhiNode) && !(n instanceof ParmNode));
+ }
+ java.util.Collections.sort(phis);
+ return phis;
+ }
+
+ @Test public void testFoldedReadBeforeWrites() {
+ var code = new CodeGen("""
+ struct S { int x; };
+ !S !a = new S { x=11; }; !S !b = new S { x=22; };
+ !S !p = a; if( arg ) p = b;
+ int before = p.x + arg;
+ a.x=33; b.x=44;
+ return before;
+ """).parse().opto().typeCheck().instSelect("x86_64_v2","SystemV").GCM().localSched();
+ var read = (MemOpNode)code._stop.walk(n ->
+ n instanceof com.seaofnodes.simple.node.cpus.x86_64_v2.AddMemX86 ? n : null);
+ assertNotNull(read);
+ int writes = 0;
+ for( Node use : read.antiDeps() )
+ if( use instanceof MemOpNode st && st.isMem() ) {
+ assertSame(read.cfg0(),st.cfg0());
+ assertTrue(st._inputs.find(read)>=0);
+ assertTrue(read.cfg0()._outputs.find(read)<st.cfg0()._outputs.find(st));
+ assertNull(((MachNode)st).regmap(st._inputs.find(read)));
+ writes++;
+ }
+ assertEquals(1,writes); // The next Store follows this first clobber through memory.
+ }
+}
diff --git a/src/test/java/com/seaofnodes/simple/Eval2.java b/src/test/java/com/seaofnodes/simple/Eval2.java
index 3f337e86..46251cef 100644
--- a/src/test/java/com/seaofnodes/simple/Eval2.java
+++ b/src/test/java/com/seaofnodes/simple/Eval2.java
@@ -179,7 +179,7 @@ public abstract class Eval2 {
int path = r._inputs.find( prior );
// Parameters read from prior frame, Phis from local frame
Frame frame = (r instanceof FunNode ? F._prior : F);
- boolean isMain = r instanceof FunNode fun && fun.sig().isa(TypeFunPtr.MAIN);
+ boolean isMain = r instanceof FunNode fun && fun.sig().isa(CodeGen.CODE._main);
// Parallel assign Phis. First parallel read and cache
int i;
@@ -349,7 +349,7 @@ public abstract class Eval2 {
int num = type._fields.length;
Object[] ptr = new Object[num];
for( int i=0; i<num; i++ )
- ptr[i] = val(alloc.in(3+i));
+ ptr[i] = con(type._fields[i]._type.makeZero());
return ptr;
}
@@ -365,6 +365,8 @@ public abstract class Eval2 {
}
private static Object store( StoreNode st ) {
+ if( st._name.equals("#") )
+ return "$mem"; // Eval stores the length in the java object[], no need to set it now
TypeMemPtr tmp = (TypeMemPtr)st.ptr()._type;
Object[] fs = (Object[])val(st.ptr());
int idx = tmp._obj.isAry()
diff --git a/src/test/java/com/seaofnodes/simple/FuzzerWrap.java b/src/test/java/com/seaofnodes/simple/FuzzerWrap.java
index 143828e6..b022bec9 100644
--- a/src/test/java/com/seaofnodes/simple/FuzzerWrap.java
+++ b/src/test/java/com/seaofnodes/simple/FuzzerWrap.java
@@ -7,10 +7,9 @@ import org.junit.Test;
import static org.junit.Assert.assertTrue;
/**
- * Normal tests run only this chapter's fixed regression seeds. Open failures
- * and exploratory fuzzing are opt-in: run their methods directly in the IDE,
- * or temporarily remove the relevant Ignore annotation when investigating.
- * Seeds belong to this chapter's generator; do not copy them between chapters.
+ * Normal tests run chapter-local regression seeds. Open failures and exploratory
+ * fuzzing are opt-in (run directly in the IDE or temporarily remove Ignore).
+ * Seeds depend on this chapter's generator; do not copy them between chapters.
*/
public class FuzzerWrap {
diff --git a/src/test/java/com/seaofnodes/simple/Main.java b/src/test/java/com/seaofnodes/simple/Main.java
new file mode 100644
index 00000000..523bd306
--- /dev/null
+++ b/src/test/java/com/seaofnodes/simple/Main.java
@@ -0,0 +1,17 @@
+package com.seaofnodes.simple;
+
+import java.nio.file.Files;
+import java.nio.file.Path;
+
+public class Main {
+ // Compile and run a simple program
+ public static void main( String[] args ) throws Exception {
+ // First arg is file, 2nd+ args are program args
+ String src = Files.readString(Path.of(args[0]));
+ int arg = Integer.valueOf(args[1]);
+ CodeGen code = new CodeGen(src);
+ code.parse().opto().typeCheck().GCM().localSched();
+ System.out.println(code._stop);
+ System.out.println(Eval2.eval(code,arg,100000));
+ }
+}
diff --git a/src/test/java/com/seaofnodes/simple/evaluator/Evaluator.java b/src/test/java/com/seaofnodes/simple/evaluator/Evaluator.java
index 7dbc551e..cb98971b 100644
--- a/src/test/java/com/seaofnodes/simple/evaluator/Evaluator.java
+++ b/src/test/java/com/seaofnodes/simple/evaluator/Evaluator.java
@@ -199,14 +199,17 @@ public class Evaluator {
assert elem instanceof TypeMemPtr;
}
// Length value
- body[0] = vall(alloc.in(3));
+ body[0] = n;
} else {
body = new Object[num = type._fields.length];
- for (int i=0; i<num; i++)
- body[i] = val(alloc.in(3+i));
+ for( int i=0; i<num; i++ )
+ body[i] = switch( alloc._ptr._obj._fields[i]._type ) {
+ case TypeInteger ti -> 0L;
+ case TypeFloat tf -> 0D;
+ default -> null;
+ };
}
return new Object[]{new Obj(type,body),null};
-
}
private Object load(LoadNode load) {
@@ -341,7 +344,7 @@ public class Evaluator {
assert block != null;
for (; i < block.nodes().length; i++) {
if (!(block.nodes()[i] instanceof PhiNode phi)) break;
- var val = region instanceof FunNode fun && "main".equals(fun.sig()._name) && ((ParmNode)phi)._idx==2 && exit==1
+ var val = region instanceof FunNode fun && "main".equals(fun._name) && ((ParmNode)phi)._idx==2 && exit==1
? parameter
: val(phi.in(exit));
phiCache.add(val);
diff --git a/src/test/java/com/seaofnodes/simple/evaluator/Scheduler.java b/src/test/java/com/seaofnodes/simple/evaluator/Scheduler.java
index 52d8f3e3..4bfa7628 100644
--- a/src/test/java/com/seaofnodes/simple/evaluator/Scheduler.java
+++ b/src/test/java/com/seaofnodes/simple/evaluator/Scheduler.java
@@ -508,7 +508,7 @@ public class Scheduler {
private static CFGNode findSingleCFGOut(Node node) {
if (node instanceof StartNode) {
for(var n:node._outputs)
- if (n instanceof FunNode fun && "main".equals(fun.sig()._name) )
+ if (n instanceof FunNode fun && "main".equals(fun._name) )
return fun;
return null;
}
diff --git a/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java b/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java
index a5634e82..851380cf 100644
--- a/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java
+++ b/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java
@@ -126,17 +126,16 @@ public class Fuzzer {
var code = new CodeGen(sb.toString());
code.parse();
- int parse_peeps= Node.ITER_CNT;
- int parse_nops = Node.ITER_NOP_CNT;
+ int nids = code.UID();
+ if( nids <= max_nids ) return max_nids; // Smaller, looking for trends as we grow
+ int parse_peeps= code._iter_cnt;
+ int parse_nops = code._iter_nop_cnt;
double parse_nop_ratio = (double)parse_nops/parse_peeps;
- int nids = Node.UID();
- if( nids <= max_nids ) return max_nids;
-
double parse_peeps_per_node = (double)parse_peeps/nids;
code.opto();
- int iter_peeps= Node.ITER_CNT;
- int iter_nops = Node.ITER_NOP_CNT;
+ int iter_peeps= code._iter_cnt;
+ int iter_nops = code._iter_nop_cnt;
double iter_nop_ratio = (double)iter_nops/iter_peeps;
double iter_peeps_per_node = (double)iter_peeps/nids;
@@ -173,7 +172,7 @@ public class Fuzzer {
return exceptions.isEmpty();
}
- /** Run a fixed seed without reducing or swallowing failures. */
+ /** Run a fixed chapter-local seed without reducing or swallowing failures. */
public void fuzzPeepsRegression(long seed) {
var rand = new Random(seed);
var sb = new StringBuilder();
diff --git a/src/test/java/com/seaofnodes/simple/fuzzer/FuzzerUtils.java b/src/test/java/com/seaofnodes/simple/fuzzer/FuzzerUtils.java
index 4509c6fc..5c89a485 100644
--- a/src/test/java/com/seaofnodes/simple/fuzzer/FuzzerUtils.java
+++ b/src/test/java/com/seaofnodes/simple/fuzzer/FuzzerUtils.java
@@ -4,6 +4,7 @@ import com.seaofnodes.simple.CodeGen;
import com.seaofnodes.simple.IterPeeps;
import com.seaofnodes.simple.node.Node;
import com.seaofnodes.simple.node.StopNode;
+import com.seaofnodes.simple.type.TypeInteger;
import java.io.OutputStream;
import java.io.PrintStream;
import java.lang.invoke.MethodHandle;
@@ -47,33 +48,10 @@ class FuzzerUtils {
}
/**
- * Copy of Node.WVISIST
- * Used to clear as exceptions might happen in the walk and left this bitset not cleared
- */
- private static final BitSet NodeWalkVisit;
- private static final java.util.Random WorkRandom;
-
- /**
- * Write access to Iterate.MID_ASSERT
- * Exception could happen mid assert and left this at true. Used to reset to false prior to parsing.
- */
- private static final MethodHandle set_MID_ASSERT;
-
- /**
* Some problems might trigger debug output. Suppress this output with a null print stream during fuzzing.
*/
public static final PrintStream NULL_PRINT_STREAM = new PrintStream(OutputStream.nullOutputStream());
- static {
- try {
- NodeWalkVisit = getFieldValue(Node.class, "WVISIT");
- WorkRandom = getFieldValue(getFieldValue(IterPeeps.class, "WORK"), "_R");
- set_MID_ASSERT = MethodHandles.lookup().unreflectSetter(getField( IterPeeps.class, "MID_ASSERT"));
- } catch (Exception e) {
- throw new ExceptionInInitializerError(e);
- }
- }
-
/**
* Check that the messages are similar.
*/
@@ -122,17 +100,9 @@ class FuzzerUtils {
try {
System.setErr(NULL_PRINT_STREAM);
System.setOut(NULL_PRINT_STREAM);
- try {
- set_MID_ASSERT.invokeExact(false);
- } catch (Throwable e) {
- throw rethrow(e);
- }
- var code = new CodeGen(script);
- code.parse();
- WorkRandom.setSeed(workListSeed);
- return code.opto().typeCheck().GCM().localSched();
+ var code = new CodeGen(script, TypeInteger.BOT, workListSeed);
+ return code.parse().opto().typeCheck().GCM().localSched();
} finally {
- NodeWalkVisit.clear();
System.setErr(err);
System.setOut(out);
}
diff --git a/src/test/java/com/seaofnodes/simple/fuzzer/ScriptGenerator.java b/src/test/java/com/seaofnodes/simple/fuzzer/ScriptGenerator.java
index d061a656..167a5b24 100644
--- a/src/test/java/com/seaofnodes/simple/fuzzer/ScriptGenerator.java
+++ b/src/test/java/com/seaofnodes/simple/fuzzer/ScriptGenerator.java
@@ -3,12 +3,13 @@ package com.seaofnodes.simple.fuzzer;
import com.seaofnodes.simple.Parser;
import com.seaofnodes.simple.node.ScopeNode;
import com.seaofnodes.simple.type.TypeInteger;
-
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Random;
import java.util.function.Predicate;
+import static com.seaofnodes.simple.Parser.KEYWORDS;
+
/**
* Generate a pseudo random script.
@@ -34,18 +35,6 @@ public class ScriptGenerator {
* Valid characters for identifiers. The last 10 are not valid for the first character.
*/
private static final String VAR_CHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_0123456789";
- /**
- * List of keywords not valid for identifiers.
- */
- private static final HashSet<String> KEYWORDS;
-
- static {
- try {
- KEYWORDS = FuzzerUtils.getFieldValue(new Parser(""), "KEYWORDS");
- } catch (Throwable e) {
- throw new ExceptionInInitializerError(e);
- }
- }
/**
* Flag for a statement that it does not pass on control flow to the next statement.
@@ -108,7 +97,6 @@ public class ScriptGenerator {
private static final List<TypeInt> INTTYPES = new ArrayList<>();
static {
- new Parser("", TypeInteger.BOT);
for( var e : Parser.defaultTypes().entrySet() ) {
if( e.getValue() instanceof TypeInteger ) {
INTTYPES.add(new TypeInt(e.getKey()));
diff --git a/src/test/java/com/seaofnodes/simple/progs/fcn.smp b/src/test/java/com/seaofnodes/simple/progs/fcn.smp
new file mode 100644
index 00000000..43d398b3
--- /dev/null
+++ b/src/test/java/com/seaofnodes/simple/progs/fcn.smp
@@ -0,0 +1,15 @@
+
+struct Person {
+ int age;
+};
+
+val fcn = { !Person?[] ps, int x ->
+ val tmp = ps[x];
+ if( ps[x] )
+ ps[x].age++;
+};
+
+!Person?[] !ps = new !Person?[2];
+ps[0] = new Person;
+ps[1] = new Person;
+fcn(ps,1);
diff --git a/src/test/java/com/seaofnodes/simple/progs/fcn2.smp b/src/test/java/com/seaofnodes/simple/progs/fcn2.smp
new file mode 100644
index 00000000..049132d9
--- /dev/null
+++ b/src/test/java/com/seaofnodes/simple/progs/fcn2.smp
@@ -0,0 +1,5 @@
+
+val fcn = (arg&1)
+ ? { int x -> x*x; }
+ : { int x -> x+x; };
+fcn(arg);
diff --git a/src/test/java/com/seaofnodes/simple/progs/fcn3.smp b/src/test/java/com/seaofnodes/simple/progs/fcn3.smp
new file mode 100644
index 00000000..55b74455
--- /dev/null
+++ b/src/test/java/com/seaofnodes/simple/progs/fcn3.smp
@@ -0,0 +1,42 @@
+
+//val fact = { int n -> n <= 1 ? 1 : fact(n-1)*n; }
+struct LL { !LL? !next; int payload; };
+
+
+struct String {
+
+ val _hashCodeString = { ->
+ int hash=0;
+ for( int i=0; i< cs#; i++ )
+ hash = hash*31 + cs[i];
+ if( !hash ) hash = 123456789;
+ return hash;
+ };
+
+ val equals = { !String s ->
+ if( self == s ) return true;
+ if( cs# != s.cs# ) return false;
+ for( int i=0; i< #; i++ )
+ if( [i] != s.cs[i] )
+ return false;
+ return true;
+ };
+
+ int _hashCode = hash();
+ u8[] cs;
+};
+
+
+
+val hashCode= { !String self ->
+ int hash = self._hashCode
+ ? self._hashCode
+ : self._hashCode = _hashCodeString(self);
+ return hash;
+};
+
+
+String s = new String { cs = new u8[17]; };
+s.cs[0] = 67; // C
+s.cs[1] = 108; // l
+hashCode(s);
diff --git a/src/test/java/com/seaofnodes/simple/progs/sieve.smp b/src/test/java/com/seaofnodes/simple/progs/sieve.smp
new file mode 100644
index 00000000..0788e6c5
--- /dev/null
+++ b/src/test/java/com/seaofnodes/simple/progs/sieve.smp
@@ -0,0 +1,32 @@
+
+int[] !ary = new int[arg], !primes = new int[arg];
+int nprimes = 0, p=2;
+// Find primes while p^2 < arg
+while( p*p < arg ) {
+ // skip marked non-primes
+ while( ary[p]==1 ) p = p + 1;
+ // p is now a prime
+ primes[nprimes] = p; nprimes = nprimes + 1;
+ // Mark out the rest non-primes
+ int i = p + p;
+ while( i < ary# ) {
+ ary[i] = 1;
+ i = i + p;
+ }
+ p = p + 1;
+}
+// Now just collect the remaining primes, no more marking
+while( p < arg ) {
+ if( ary[p] == 0 ) {
+ primes[nprimes] = p; nprimes = nprimes + 1;
+ }
+ p = p + 1;
+}
+// Copy/shrink the result array
+int[] !rez = new int[nprimes];
+int j = 0;
+while( j < nprimes ) {
+ rez[j] = primes[j];
+ j = j + 1;
+}
+return rez;