summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCliff Click <cliffc@acm.org>2025-05-06 13:44:10 -0700
committerCliff Click <cliffc@acm.org>2026-10-08 16:52:27 -0700
commite977ff8c1e069f08993384f1afe0a27c3d78094f (patch)
tree60fd16a416ff8827ca11721c2b06dfa67aa64665
parent2475516bee0ca64ffb1bff184e671db9b69bd617 (diff)
downloadSimple-e977ff8c1e069f08993384f1afe0a27c3d78094f.tar.gz
Simple-e977ff8c1e069f08993384f1afe0a27c3d78094f.zip
Chapter 23: Methods and Revisiting Types
Co-authored-by: Hsiang-Ying Fu <66062782+fuhsnn@users.noreply.github.com> Co-authored-by: Tamás Hadházy <85063808+Hels15@users.noreply.github.com> Co-authored-by: fuhsnn <66062782+fuhsnn@users.noreply.github.com> Co-authored-by: XmiliaH <45106915+XmiliaH@users.noreply.github.com> Co-authored-by: Symious <14933944+symious@users.noreply.github.com> Co-authored-by: Jun Kawasaki <jun784@gmail.com> Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
-rw-r--r--README.ja.md2
-rw-r--r--README.md2
-rw-r--r--docs/chapter23/23-grammar.ja.md163
-rw-r--r--docs/chapter23/23-grammar.md163
-rw-r--r--docs/chapter23/Makefile12
-rw-r--r--docs/chapter23/README.ja.md345
-rw-r--r--docs/chapter23/README.md463
-rw-r--r--docs/chapter23/examples/A_helloWorld.smp3
-rw-r--r--docs/chapter23/examples/BubbleSort.smp37
-rw-r--r--docs/chapter23/font-awesome-4.7.0.zipbin0 -> 669808 bytes
-rw-r--r--docs/chapter23/ifg_trim.pdfbin0 -> 155190 bytes
-rw-r--r--docs/chapter23/lattice.gv203
-rw-r--r--docs/chapter23/lattice.svg691
-rw-r--r--docs/chapter23/lattice2.gv85
-rw-r--r--docs/chapter23/lattice2.svg260
-rw-r--r--docs/chapter23/pardoe.docxbin0 -> 22583 bytes
-rw-r--r--docs/chapter23/webtcp.min.js65
-rw-r--r--src/main/java/com/seaofnodes/simple/IterPeeps.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/Parser.java499
-rw-r--r--src/main/java/com/seaofnodes/simple/Var.java1
-rw-r--r--src/main/java/com/seaofnodes/simple/codegen/Coalesce.java4
-rw-r--r--src/main/java/com/seaofnodes/simple/codegen/CodeGen.java66
-rw-r--r--src/main/java/com/seaofnodes/simple/codegen/ElfFile.java3
-rw-r--r--src/main/java/com/seaofnodes/simple/codegen/Encoding.java24
-rw-r--r--src/main/java/com/seaofnodes/simple/codegen/GlobalCodeMotion.java6
-rw-r--r--src/main/java/com/seaofnodes/simple/codegen/IFG.java6
-rw-r--r--src/main/java/com/seaofnodes/simple/codegen/LRG.java7
-rw-r--r--src/main/java/com/seaofnodes/simple/codegen/LinkMem.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/codegen/ListScheduler.java4
-rw-r--r--src/main/java/com/seaofnodes/simple/codegen/RegAlloc.java71
-rw-r--r--src/main/java/com/seaofnodes/simple/codegen/RegMask.java4
-rw-r--r--src/main/java/com/seaofnodes/simple/node/AddNode.java9
-rw-r--r--src/main/java/com/seaofnodes/simple/node/ArithNode.java9
-rw-r--r--src/main/java/com/seaofnodes/simple/node/BoolNode.java4
-rw-r--r--src/main/java/com/seaofnodes/simple/node/CFGNode.java6
-rw-r--r--src/main/java/com/seaofnodes/simple/node/CProjNode.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/CallEndMach.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/CallEndNode.java10
-rw-r--r--src/main/java/com/seaofnodes/simple/node/CallNode.java12
-rw-r--r--src/main/java/com/seaofnodes/simple/node/CastMach.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/CastNode.java17
-rw-r--r--src/main/java/com/seaofnodes/simple/node/ConFldOffNode.java41
-rw-r--r--src/main/java/com/seaofnodes/simple/node/ConstantNode.java34
-rw-r--r--src/main/java/com/seaofnodes/simple/node/ExternNode.java4
-rw-r--r--src/main/java/com/seaofnodes/simple/node/FunNode.java9
-rw-r--r--src/main/java/com/seaofnodes/simple/node/IfNode.java6
-rw-r--r--src/main/java/com/seaofnodes/simple/node/LoadNode.java29
-rw-r--r--src/main/java/com/seaofnodes/simple/node/LoopNode.java24
-rw-r--r--src/main/java/com/seaofnodes/simple/node/MachConcreteNode.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/MachNode.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/MemMergeNode.java8
-rw-r--r--src/main/java/com/seaofnodes/simple/node/MemOpNode.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/MemPhiNode.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/MinusNode.java3
-rw-r--r--src/main/java/com/seaofnodes/simple/node/MultiNode.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/NewNode.java17
-rw-r--r--src/main/java/com/seaofnodes/simple/node/Node.java7
-rw-r--r--src/main/java/com/seaofnodes/simple/node/NotNode.java4
-rw-r--r--src/main/java/com/seaofnodes/simple/node/ParmNode.java11
-rw-r--r--src/main/java/com/seaofnodes/simple/node/PhiNode.java76
-rw-r--r--src/main/java/com/seaofnodes/simple/node/ReadOnlyMach.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/RegionNode.java8
-rw-r--r--src/main/java/com/seaofnodes/simple/node/ReturnNode.java15
-rw-r--r--src/main/java/com/seaofnodes/simple/node/ScopeNode.java156
-rw-r--r--src/main/java/com/seaofnodes/simple/node/SplitNode.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/StartNode.java9
-rw-r--r--src/main/java/com/seaofnodes/simple/node/StopNode.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/StoreNode.java8
-rw-r--r--src/main/java/com/seaofnodes/simple/node/StructNode.java9
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/AddARM.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/AddFARM.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/AddIARM.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/AndARM.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/AndIARM.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/AsrARM.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/AsrIARM.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/BranchARM.java3
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/CallARM.java6
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/CallRRARM.java4
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/CmpARM.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/CmpFARM.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/CmpIARM.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/DivARM.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/DivFARM.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/ExtendARM.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/F8ToIARM.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/FloatARM.java4
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/FunARM.java4
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/I2F8ARM.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/IntARM.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/LoadARM.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/LslARM.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/LslIARM.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/LsrARM.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/LsrIARM.java3
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/MemOpARM.java5
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/MulARM.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/MulFARM.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/NegARM.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/NewARM.java3
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/NotARM.java3
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/OrARM.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/OrIARM.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/SetARM.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/SplitARM.java5
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/StoreARM.java4
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/SubARM.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/SubFARM.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/SubIARM.java5
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/TFPARM.java3
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/TMPARM.java3
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/UJmpARM.java3
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/XorARM.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/XorIARM.java7
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/arm.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/AUIPC.java3
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/AddFRISC.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/AddIRISC.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/AddRISC.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/AndIRISC.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/AndRISC.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/BranchRISC.java3
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/CallRISC.java6
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/CallRRISC.java4
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/DivFRISC.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/DivRISC.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/ExtendRISC.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/F8ToIRISC.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/FltRISC.java4
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/FunRISC.java4
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/I2F8RISC.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/ImmRISC.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/Int8RISC.java5
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/IntRISC.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/LUI.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/LoadRISC.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/MemOpRISC.java4
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/MulFRISC.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/MulRISC.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/NegRISC.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/NewRISC.java3
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/NotRISC.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/OrIRISC.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/OrRISC.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/RetRISC.java4
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/SetFRISC.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/SetIRISC.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/SetRISC.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/SllIRISC.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/SllRISC.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/SplitRISC.java4
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/SraIRISC.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/SraRISC.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/SrlIRISC.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/SrlRISC.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/StoreRISC.java4
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/SubFRISC.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/SubRISC.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/TFPRISC.java3
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/TMPRISC.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/UJmpRISC.java3
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/XorIRISC.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/XorRISC.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/riscv.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddFMemX86.java4
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddFX86.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddIX86.java4
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddMemX86.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CallRX86.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CallX86.java4
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CmpFX86.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CmpIX86.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CmpMemX86.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CmpX86.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/DivFX86.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/DivX86.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ExtendX86.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/F8ToIX86.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/FltX86.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/FunX86.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/I2f8X86.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ImmX86.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/IntX86.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/JmpX86.java3
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/LeaX86.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/LoadX86.java9
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MemAddX86.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MemOpX86.java4
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MulFX86.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MulIX86.java4
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MulX86.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/NegX86.java3
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/NewX86.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/NotX86.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/RegX86.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SarIX86.java5
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SarX86.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SetX86.java3
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShlIX86.java5
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShlX86.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShrIX86.java5
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShrX86.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SplitX86.java4
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/StoreX86.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SubFX86.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/TFPX86.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/TMPX86.java8
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/UJmpX86.java3
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/x86_64_v2.java5
-rw-r--r--src/main/java/com/seaofnodes/simple/print/ASMPrinter.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/print/IRPrinter.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/print/SimpleGraphAdapter.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/print/SimpleGraphObserver.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/sys/sys.smp58
-rw-r--r--src/main/java/com/seaofnodes/simple/type/Field.java124
-rw-r--r--src/main/java/com/seaofnodes/simple/type/Type.java370
-rw-r--r--src/main/java/com/seaofnodes/simple/type/TypeConAry.java51
-rw-r--r--src/main/java/com/seaofnodes/simple/type/TypeConAryB.java5
-rw-r--r--src/main/java/com/seaofnodes/simple/type/TypeConAryI.java4
-rw-r--r--src/main/java/com/seaofnodes/simple/type/TypeFloat.java55
-rw-r--r--src/main/java/com/seaofnodes/simple/type/TypeFunPtr.java254
-rw-r--r--src/main/java/com/seaofnodes/simple/type/TypeInteger.java83
-rw-r--r--src/main/java/com/seaofnodes/simple/type/TypeMem.java89
-rw-r--r--src/main/java/com/seaofnodes/simple/type/TypeMemPtr.java110
-rw-r--r--src/main/java/com/seaofnodes/simple/type/TypeNil.java14
-rw-r--r--src/main/java/com/seaofnodes/simple/type/TypePtr.java18
-rw-r--r--src/main/java/com/seaofnodes/simple/type/TypeRPC.java13
-rw-r--r--src/main/java/com/seaofnodes/simple/type/TypeStruct.java496
-rw-r--r--src/main/java/com/seaofnodes/simple/type/TypeTuple.java113
-rw-r--r--src/main/java/com/seaofnodes/simple/util/Ary.java (renamed from src/main/java/com/seaofnodes/simple/Ary.java)3
-rw-r--r--src/main/java/com/seaofnodes/simple/util/AryInt.java192
-rw-r--r--src/main/java/com/seaofnodes/simple/util/SB.java (renamed from src/main/java/com/seaofnodes/simple/SB.java)2
-rw-r--r--src/main/java/com/seaofnodes/simple/util/Utils.java (renamed from src/main/java/com/seaofnodes/simple/Utils.java)2
-rw-r--r--src/main/smp/sys.smp142
-rw-r--r--src/test/java/com/seaofnodes/simple/Chapter04Test.java2
-rw-r--r--src/test/java/com/seaofnodes/simple/Chapter06Test.java16
-rw-r--r--src/test/java/com/seaofnodes/simple/Chapter09Test.java10
-rw-r--r--src/test/java/com/seaofnodes/simple/Chapter10Test.java26
-rw-r--r--src/test/java/com/seaofnodes/simple/Chapter11Test.java2
-rw-r--r--src/test/java/com/seaofnodes/simple/Chapter12Test.java95
-rw-r--r--src/test/java/com/seaofnodes/simple/Chapter13Test.java2
-rw-r--r--src/test/java/com/seaofnodes/simple/Chapter15Test.java19
-rw-r--r--src/test/java/com/seaofnodes/simple/Chapter16Test.java2
-rw-r--r--src/test/java/com/seaofnodes/simple/Chapter17aTest.java10
-rw-r--r--src/test/java/com/seaofnodes/simple/Chapter17bTest.java27
-rw-r--r--src/test/java/com/seaofnodes/simple/Chapter18Test.java22
-rw-r--r--src/test/java/com/seaofnodes/simple/Chapter19Test.java10
-rw-r--r--src/test/java/com/seaofnodes/simple/Chapter20Test.java19
-rw-r--r--src/test/java/com/seaofnodes/simple/Chapter21AllocTest.java2
-rw-r--r--src/test/java/com/seaofnodes/simple/Chapter21Test.java65
-rw-r--r--src/test/java/com/seaofnodes/simple/Chapter22Test.java13
-rw-r--r--src/test/java/com/seaofnodes/simple/Chapter23AllocTest.java37
-rw-r--r--src/test/java/com/seaofnodes/simple/Chapter23Test.java319
-rw-r--r--src/test/java/com/seaofnodes/simple/Eval2.java47
-rw-r--r--src/test/java/com/seaofnodes/simple/LiteralTest.java2
-rw-r--r--src/test/java/com/seaofnodes/simple/MemoryEncodingTest.java2
-rw-r--r--src/test/java/com/seaofnodes/simple/MergeSortTest.java1
-rw-r--r--src/test/java/com/seaofnodes/simple/Simple.java8
-rw-r--r--src/test/java/com/seaofnodes/simple/SpillStats.java5
-rw-r--r--src/test/java/com/seaofnodes/simple/TestC.java24
-rw-r--r--src/test/java/com/seaofnodes/simple/TypeTest.java107
-rw-r--r--src/test/java/com/seaofnodes/simple/codegen/RegAllocTestSupport.java43
-rw-r--r--src/test/java/com/seaofnodes/simple/evaluator/Evaluator.java7
-rw-r--r--src/test/java/com/seaofnodes/simple/evaluator/Scheduler.java3
-rw-r--r--src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java8
-rw-r--r--src/test/java/com/seaofnodes/simple/fuzzer/FuzzerUtils.java4
-rw-r--r--src/test/java/com/seaofnodes/simple/fuzzer/Reducer.java11
-rw-r--r--src/test/java/com/seaofnodes/simple/fuzzer/ScriptGenerator.java207
-rw-r--r--src/test/java/com/seaofnodes/simple/progs/and1.smp10
-rw-r--r--src/test/java/com/seaofnodes/simple/progs/and2.smp16
-rw-r--r--src/test/java/com/seaofnodes/simple/progs/and3.smp14
-rw-r--r--src/test/java/com/seaofnodes/simple/progs/funptr.c4
-rw-r--r--src/test/java/com/seaofnodes/simple/progs/large.smp5344
-rw-r--r--src/test/java/com/seaofnodes/simple/progs/or1.smp12
-rw-r--r--src/test/java/com/seaofnodes/simple/progs/or2.smp23
-rw-r--r--src/test/java/com/seaofnodes/simple/progs/print.smp8
-rw-r--r--src/test/java/com/seaofnodes/simple/progs/ptrand.smp10
-rw-r--r--src/test/java/com/seaofnodes/simple/progs/stringHash.smp5
-rw-r--r--src/test/java/com/seaofnodes/simple/progs/stringHash21.smp31
279 files changed, 11531 insertions, 1469 deletions
diff --git a/README.ja.md b/README.ja.md
index 5a0a2893..df9119a1 100644
--- a/README.ja.md
+++ b/README.ja.md
@@ -60,7 +60,7 @@ Simple 言語のスタイルは、C または Java のサブセットに倣っã�
* [第20章](docs/chapter20/README.ja.md):グラフ彩色によるレジスタ割り当て。
* [第21章](docs/chapter21/README.ja.md):命令エンコーディングと ELF。
* [第22章](docs/chapter22/README.ja.md):Simple の Hello, World!
-* 第23章:メソッドと型の再検討。
+* [第23章](docs/chapter23/README.ja.md):メソッドと型の再検討。
* 第24章:連鎖した条件分岐と SCCP。
* 第25章:モジュール、分割コンパイル、不完全な型を使う SSA の構築。
diff --git a/README.md b/README.md
index 67d042fb..dd2482b8 100644
--- a/README.md
+++ b/README.md
@@ -69,7 +69,7 @@ The Simple language is styled after a subset of C or Java.
* [Chapter 20](docs/chapter20/README.md): Graph Coloring Register Allocation
* [Chapter 21](docs/chapter21/README.md): Instruction Encodings & ELF
* [Chapter 22](docs/chapter22/README.md): A Simple Hello, World!
-* Chapter 23: Methods and Types Revisited
+* [Chapter 23](docs/chapter23/README.md): Methods and Types Revisited
* Chapter 24: Chained conditionals and SCCP
* Chapter 25: Modules, Separate Compilation, and SSA Construction with Incomplete Types
diff --git a/docs/chapter23/23-grammar.ja.md b/docs/chapter23/23-grammar.ja.md
new file mode 100644
index 00000000..ff7b5d56
--- /dev/null
+++ b/docs/chapter23/23-grammar.ja.md
@@ -0,0 +1,163 @@
+# 第23章の文法
+
+[English](23-grammar.md) | 日本語
+
+```antlrv4
+grammar SimpleLanguage;
+
+// Name lookup, assignable operands, type compatibility, and access permissions
+// are checked by the compiler in addition to these syntax rules.
+program : statement* EOF ;
+
+block : '{' statement* '}' ;
+
+statement
+ : block
+ | returnStatement
+ | ifStatement
+ | whileStatement
+ | forStatement
+ | breakStatement
+ | continueStatement
+ | structDeclaration
+ | declaration ';'
+ | assignment ';'
+ | ';'
+ ;
+
+returnStatement : 'return' assignment ';' ;
+ifStatement : 'if' '(' assignment ')' statement ('else' statement)? ;
+whileStatement : 'while' '(' assignment ')' statement ;
+breakStatement : 'break' ';' ;
+continueStatement : 'continue' ';' ;
+
+forStatement
+ : 'for' '(' (declaration | assignment)? ';' assignment? ';' assignment? ')' statement
+ ;
+
+structDeclaration : 'struct' IDENTIFIER '{' structMember* '}' ';' ;
+structMember : methodDeclaration | statement ;
+
+// A method is an ordinary fixed function binding in a struct definition.
+// The compiler supplies self; it is not written in parameterList.
+// The general declaration rule also admits explicitly typed function fields.
+methodDeclaration : 'val' bindingModifier? IDENTIFIER '=' functionExpression ';' ;
+
+declaration : declarationType binding (',' binding)* ;
+declarationType : type | 'var' | 'val' ;
+binding : bindingModifier? IDENTIFIER ('=' initializer)? ;
+bindingModifier : '!' | '~' ;
+// A declaration initializer consisting of the string "C" declares an extern
+// symbol. Ordinary strings elsewhere are array-valued expressions.
+initializer : assignment ;
+
+// The prefix qualifies the named struct reference, not an enclosing array.
+// Each [] / [~] suffix qualifies its own array layer. ? requires a reference.
+type
+ : functionType
+ | primitiveType typeSuffix*
+ | accessModifier? typeName typeSuffix*
+ ;
+accessModifier : '!' | '~' ;
+typeSuffix : '?' | '[]' | '[~]' ;
+typeName : IDENTIFIER ('.' IDENTIFIER)* ;
+
+primitiveType
+ : 'int' | 'i8' | 'i16' | 'i32' | 'i64'
+ | 'u1' | 'u8' | 'u16' | 'u32' | 'byte' | 'bool'
+ | 'flt' | 'f32' | 'f64'
+ ;
+
+assignment : expression (assignmentOperator assignment)? ;
+assignmentOperator
+ : '=' | '+=' | '-=' | '*=' | '/=' | '&=' | '|=' | '^='
+ | '<<=' | '>>=' | '>>>='
+ ;
+
+expression : logicalExpression ('?' assignment (':' assignment)?)? ;
+
+// Both logical operators short-circuit and associate to the right here.
+logicalExpression : bitwiseExpression (('&&' | '||') logicalExpression)? ;
+
+bitwiseExpression
+ : comparisonExpression (('&' | '|' | '^') comparisonExpression)*
+ ;
+
+comparisonExpression
+ : shiftExpression (('==' | '!=' | '<' | '<=' | '>' | '>=') shiftExpression)*
+ ;
+
+shiftExpression
+ : additiveExpression (('<<' | '>>' | '>>>') additiveExpression)*
+ ;
+
+additiveExpression
+ : multiplicativeExpression (('+' | '-') multiplicativeExpression)*
+ ;
+
+multiplicativeExpression
+ : unaryExpression (('*' | '/') unaryExpression)*
+ ;
+
+unaryExpression
+ : ('-' | '!') unaryExpression
+ | updateOperator IDENTIFIER
+ | postfixExpression
+ ;
+
+// Prefix updates require a local name; postfix updates also allow fields
+// and array elements. All updates require an assignable operand.
+updateOperator : '++' | '--' ;
+postfixExpression : primaryExpression postfixSuffix* updateOperator? ;
+postfixSuffix : '.' IDENTIFIER | '[' assignment ']' | '#' | callSuffix | methodCallSuffix ;
+
+// For a fixed function-valued member, object.name(args) passes object as self.
+// This overlaps ordinary field selection followed by a call syntactically;
+// name/type resolution determines which call form applies.
+methodCallSuffix : '.' IDENTIFIER callSuffix ;
+
+primaryExpression
+ : INTEGER_LITERAL
+ | FLOAT_LITERAL
+ | STRING_LITERAL
+ | CHARACTER_LITERAL
+ | 'true'
+ | 'false'
+ | 'null'
+ | IDENTIFIER
+ | '(' assignment ')'
+ | newExpression
+ | functionExpression
+ ;
+
+// A function is an expression bound with an ordinary declaration.
+// Its last statement supplies the implicit result; return can exit earlier.
+functionExpression : '{' parameterList? '->' statement* '}' ;
+parameterList : (parameter ','?)+ ;
+parameter : type bindingModifier? IDENTIFIER ;
+
+// Function types have unnamed, space-separated argument types.
+// {int} is the zero-argument type; its function expression still uses ->.
+functionType
+ : '{' type+ '->' type '}' '?'?
+ | '{' type '}' '?'?
+ ;
+
+callSuffix : '(' argumentList? ')' ;
+argumentList : assignment (',' assignment)* ','? ;
+
+// Struct initialization uses a block; array allocation supplies a length.
+newExpression : 'new' type ('[' assignment ']' | block)? ;
+
+INTEGER_LITERAL : '0' | [1-9] DIGIT* ;
+FLOAT_LITERAL : DIGIT+ ('.' DIGIT* ('e' DIGIT+)? | 'e' '-'? DIGIT+) ;
+STRING_LITERAL : '"' (ESCAPE | ~["\\])* '"' ;
+CHARACTER_LITERAL : '\'' (ESCAPE | ~['\\]) '\'' ;
+fragment ESCAPE : '\\' [0ntr\\'"] ;
+IDENTIFIER : NON_DIGIT (NON_DIGIT | DIGIT)* ;
+fragment NON_DIGIT : [a-zA-Z_] ;
+fragment DIGIT : [0-9] ;
+WHITESPACE : [ \t\r\n]+ -> skip ;
+LINE_COMMENT : '//' ~[\r\n]* -> skip ;
+BLOCK_COMMENT : '/*' .*? '*/' -> skip ;
+```
diff --git a/docs/chapter23/23-grammar.md b/docs/chapter23/23-grammar.md
new file mode 100644
index 00000000..1d52d5a6
--- /dev/null
+++ b/docs/chapter23/23-grammar.md
@@ -0,0 +1,163 @@
+# Grammar for Chapter 23
+
+English | [日本語](23-grammar.ja.md)
+
+```antlrv4
+grammar SimpleLanguage;
+
+// Name lookup, assignable operands, type compatibility, and access permissions
+// are checked by the compiler in addition to these syntax rules.
+program : statement* EOF ;
+
+block : '{' statement* '}' ;
+
+statement
+ : block
+ | returnStatement
+ | ifStatement
+ | whileStatement
+ | forStatement
+ | breakStatement
+ | continueStatement
+ | structDeclaration
+ | declaration ';'
+ | assignment ';'
+ | ';'
+ ;
+
+returnStatement : 'return' assignment ';' ;
+ifStatement : 'if' '(' assignment ')' statement ('else' statement)? ;
+whileStatement : 'while' '(' assignment ')' statement ;
+breakStatement : 'break' ';' ;
+continueStatement : 'continue' ';' ;
+
+forStatement
+ : 'for' '(' (declaration | assignment)? ';' assignment? ';' assignment? ')' statement
+ ;
+
+structDeclaration : 'struct' IDENTIFIER '{' structMember* '}' ';' ;
+structMember : methodDeclaration | statement ;
+
+// A method is an ordinary fixed function binding in a struct definition.
+// The compiler supplies self; it is not written in parameterList.
+// The general declaration rule also admits explicitly typed function fields.
+methodDeclaration : 'val' bindingModifier? IDENTIFIER '=' functionExpression ';' ;
+
+declaration : declarationType binding (',' binding)* ;
+declarationType : type | 'var' | 'val' ;
+binding : bindingModifier? IDENTIFIER ('=' initializer)? ;
+bindingModifier : '!' | '~' ;
+// A declaration initializer consisting of the string "C" declares an extern
+// symbol. Ordinary strings elsewhere are array-valued expressions.
+initializer : assignment ;
+
+// The prefix qualifies the named struct reference, not an enclosing array.
+// Each [] / [~] suffix qualifies its own array layer. ? requires a reference.
+type
+ : functionType
+ | primitiveType typeSuffix*
+ | accessModifier? typeName typeSuffix*
+ ;
+accessModifier : '!' | '~' ;
+typeSuffix : '?' | '[]' | '[~]' ;
+typeName : IDENTIFIER ('.' IDENTIFIER)* ;
+
+primitiveType
+ : 'int' | 'i8' | 'i16' | 'i32' | 'i64'
+ | 'u1' | 'u8' | 'u16' | 'u32' | 'byte' | 'bool'
+ | 'flt' | 'f32' | 'f64'
+ ;
+
+assignment : expression (assignmentOperator assignment)? ;
+assignmentOperator
+ : '=' | '+=' | '-=' | '*=' | '/=' | '&=' | '|=' | '^='
+ | '<<=' | '>>=' | '>>>='
+ ;
+
+expression : logicalExpression ('?' assignment (':' assignment)?)? ;
+
+// Both logical operators short-circuit and associate to the right here.
+logicalExpression : bitwiseExpression (('&&' | '||') logicalExpression)? ;
+
+bitwiseExpression
+ : comparisonExpression (('&' | '|' | '^') comparisonExpression)*
+ ;
+
+comparisonExpression
+ : shiftExpression (('==' | '!=' | '<' | '<=' | '>' | '>=') shiftExpression)*
+ ;
+
+shiftExpression
+ : additiveExpression (('<<' | '>>' | '>>>') additiveExpression)*
+ ;
+
+additiveExpression
+ : multiplicativeExpression (('+' | '-') multiplicativeExpression)*
+ ;
+
+multiplicativeExpression
+ : unaryExpression (('*' | '/') unaryExpression)*
+ ;
+
+unaryExpression
+ : ('-' | '!') unaryExpression
+ | updateOperator IDENTIFIER
+ | postfixExpression
+ ;
+
+// Prefix updates require a local name; postfix updates also allow fields
+// and array elements. All updates require an assignable operand.
+updateOperator : '++' | '--' ;
+postfixExpression : primaryExpression postfixSuffix* updateOperator? ;
+postfixSuffix : '.' IDENTIFIER | '[' assignment ']' | '#' | callSuffix | methodCallSuffix ;
+
+// For a fixed function-valued member, object.name(args) passes object as self.
+// This overlaps ordinary field selection followed by a call syntactically;
+// name/type resolution determines which call form applies.
+methodCallSuffix : '.' IDENTIFIER callSuffix ;
+
+primaryExpression
+ : INTEGER_LITERAL
+ | FLOAT_LITERAL
+ | STRING_LITERAL
+ | CHARACTER_LITERAL
+ | 'true'
+ | 'false'
+ | 'null'
+ | IDENTIFIER
+ | '(' assignment ')'
+ | newExpression
+ | functionExpression
+ ;
+
+// A function is an expression bound with an ordinary declaration.
+// Its last statement supplies the implicit result; return can exit earlier.
+functionExpression : '{' parameterList? '->' statement* '}' ;
+parameterList : (parameter ','?)+ ;
+parameter : type bindingModifier? IDENTIFIER ;
+
+// Function types have unnamed, space-separated argument types.
+// {int} is the zero-argument type; its function expression still uses ->.
+functionType
+ : '{' type+ '->' type '}' '?'?
+ | '{' type '}' '?'?
+ ;
+
+callSuffix : '(' argumentList? ')' ;
+argumentList : assignment (',' assignment)* ','? ;
+
+// Struct initialization uses a block; array allocation supplies a length.
+newExpression : 'new' type ('[' assignment ']' | block)? ;
+
+INTEGER_LITERAL : '0' | [1-9] DIGIT* ;
+FLOAT_LITERAL : DIGIT+ ('.' DIGIT* ('e' DIGIT+)? | 'e' '-'? DIGIT+) ;
+STRING_LITERAL : '"' (ESCAPE | ~["\\])* '"' ;
+CHARACTER_LITERAL : '\'' (ESCAPE | ~['\\]) '\'' ;
+fragment ESCAPE : '\\' [0ntr\\'"] ;
+IDENTIFIER : NON_DIGIT (NON_DIGIT | DIGIT)* ;
+fragment NON_DIGIT : [a-zA-Z_] ;
+fragment DIGIT : [0-9] ;
+WHITESPACE : [ \t\r\n]+ -> skip ;
+LINE_COMMENT : '//' ~[\r\n]* -> skip ;
+BLOCK_COMMENT : '/*' .*? '*/' -> skip ;
+```
diff --git a/docs/chapter23/Makefile b/docs/chapter23/Makefile
new file mode 100644
index 00000000..12659b8f
--- /dev/null
+++ b/docs/chapter23/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/chapter23/README.ja.md b/docs/chapter23/README.ja.md
new file mode 100644
index 00000000..5c5427f5
--- /dev/null
+++ b/docs/chapter23/README.ja.md
@@ -0,0 +1,345 @@
+# 第23章: メソッドと型の再検討
+
+[English](README.md) | 日本語
+
+この文書は [原文](README.md) の日本語訳です。
+
+[前の章: 第22章](../chapter22/README.ja.md) |
+[次の章: 第24章](../chapter24/README.ja.md)
+
+この章では *メソッド* を追加します。構造体内で定義し、隠れた `self` 引数を取り、構造体のフィールドへアクセスできる関数です。文字列のようなクラスの `indexOf` メソッドを示します。
+
+```java
+// A String-like class, with methods
+struct String {
+ u8[~] buf; // Buffer of read-only characters
+
+ // Return the index of the first character 'c' or -1
+ val indexOf = { u8 c -> // Hidden 'self' argument
+ for( int i=0; i<buf#; i++ ) // Direct access to `buf` field
+ if( buf[i]==c )
+ return i;
+ return -1; // 'c' not in 'self'
+ };
+};
+```
+
+この章では型も再検討し、大きく変更します。
+
+- 型は *循環* するようになります。型は間接的に自身を指せます。
+- 型は引き続き [*インターン*](https://en.wikipedia.org/wiki/Interning_(computer_science)) または [*ハッシュコンシング*](https://en.wikipedia.org/wiki/Hash_consing) されます。
+- 型オブジェクトはオブジェクトプールで管理します。
+- 新しく作った型はインターン表を検索し、見つかれば元のものを使い、新しい型をプールへ返します。
+- インターン表のヒット率は99.9%ほどです。作られる型のほとんどは、すでに作られたことがあるものです。この点は興味深い性能向上につながります。最適化コンパイラの作業の多くは型の操作なので、型の数を小さく保ち、高速な階層のキャッシュに収めることには大きな見返りがあります。
+- インターン処理の多くは、何らかの「訪問ビット」で循環を扱う必要があります。簡単な方法の1つは、各型に *一意な ID*(UID)を追加することです。小さな密な整数をビット集合で使い、同じ型を複数回訪問しないようにします。UID が小さければビット集合も小さくなり、これも高速なキャッシュ階層に収まります。
+- 結果として、型は循環し(これが目標です)、**高速** になり、実装は複雑になります。型の **使い方** は同じです。作成、MEET、読み取り専用化、表示などでは同じ Type API を使い、実装の詳細だけが変わります。
+
+第11章の遅延メモリモデルは、新しい型システムでも続きます。BulkMemPhi/MemPhi が最適化中にエイリアスを発見します。各 New はインスタンスフィールドだけを覆い、クラス全体の(`_one`)フィールドは部分メモリから除外します。フィールドオフセットは配置まで記号的なままであり、Load は正確なエイリアスの内容を問い合わせながら、循環するフィールド型と深い final 情報を保持します。
+
+[linear](https://github.com/SeaOfNodes/Simple/tree/linear) ブランチ上の直線的な Git リビジョン履歴で [この章](https://github.com/SeaOfNodes/Simple/tree/linear-chapter23) を読み、前の章と [比較](https://github.com/SeaOfNodes/Simple/compare/linear-chapter22...linear-chapter23) することもできます。
+
+この章の[完全な言語文法](23-grammar.ja.md)はこちらです。
+
+## なぜ循環する型なのか
+
+循環する型は代替手法より精密な解析を可能にし、より多くのプログラムや、よりよい最適化、あるいは両方を許します。Simple では型を型検査にも使うので、より精密な型により、より多くの正しいプログラムを許容します。
+
+**なぜ今、循環する型なのか**。構造体を引数に取る関数定義を構造体内で許すとすぐ、循環が現れるからです。つまり *self* 引数を取る *メソッド* です。
+
+上の `String.indexOf` の例を見ましょう。ここでは `indexOf` メソッドを持つ `String` を定義します。隠れた文字列引数 `self` が渡され、検索されます。`struct String` の型は何でしょうか。
+
+`struct String { u8[~] buf; { !String !self, u8 c -> int } indexOf; }`
+
+これは `String` という名前の型で、フィールド `u8[~] buf` と、final で代入済みの定数フィールド `indexOf` を持ちます。後者自身の型は `{ !String !self, u8 c -> int }` です。つまり `String` の型は、`indexOf` の型の内側に自分自身への参照を持ちます。`String` の型は *循環* しています。
+
+## 循環の少ない型
+
+循環する型の問題は古くからあり、対処のための実績ある方法がいくつもあります。簡単なものの1つは、型の *定義* と、それとは別の型の *参照* を持つ方法です。参照は何らかの検索で型を参照します。たとえば型名とパーサーのシンボル表を使う、ハッシュ表検索です。
+
+このモデルでは循環を実質的に回避します。型の循環内の「後退辺」は、すべて参照辺、場合によってはハッシュ表検索で実現します。
+
+では、なぜこの方法を採用しないのでしょうか。
+
+すべての型参照が同じ型定義へ戻るためです。型情報を特化しても失われます。*すべての* 参照が同じ定義へ戻るため、*すべての* 経路の MEET を取ることになるのです。
+
+Java の `Object`、または C の `void*` をデータとして持つ連結リストの例を示します。
+
+```java
+struct List {
+ !List !next;
+ Object payload; // equivalently for C: void*
+}
+// Then walk a collection of ints and build a List:
+List nums = null;
+for( int x : ary_ints )
+ nums = new List{next=nums; payload=x; }
+
+// Again for strings:
+List strs = null;
+for( String x : ary_strs )
+ strs = new List{next=strs; payload=x; }
+```
+
+`nums` の型は何でしょうか。`*List`、つまり次の型です。
+
+```
+struct List { List *next; Object payload }
+```
+
+`Object` を `int` や `String` に精密化できません。すべての List オブジェクトのデータ型は `Object` で、`Object` MEET `int` は `Object` に戻るためです。
+
+真に循環する型を許せば、このような場面で `*List<int> { *List<int> next, int payload }` という精密な型を発見、あるいは推論できます。基本的に最適化器内で *ジェネリクス* を構築するのです。言語のジェネリクスや型変数そのものではありませんが、それにかなり近づきます。
+
+## 循環する型の扱い
+
+この章で Simple は循環する型を直接扱います。子の型が元の型へ戻る `Type` オブジェクトを持つため、型に対して [*再帰下降*](https://en.wikipedia.org/wiki/Recursive_descent_parser) がそのままでは使えなくなります。
+
+これまでは型構築に再帰下降を使ってきました。子の型を先に作ってインターンし、インターン済みの子で次の階層を構築します。各階層がインターンされ、再帰下降がインターン済みの型で止まるので、共有する型での重複作業を避けられます。このため現在の型に対する演算は、旧型と新型の *差分* に対して線形となり、増分的で高速です。
+
+新たな循環する型では、循環全体を見るまでインターンできない問題に直面します。またハッシュ表は等値検査を必要としますが、循環を等値比較するのに通常の再帰下降は使えません。
+
+グラフの循環を扱う標準的な方法は、「以前ここに来た」ことを示す *訪問済み* の概念です。一意な型 ID をキーにするハッシュ表を使います。「訪問済み」は、一意な ID が表のキーにあるという意味です。各 Type は大域カウンタから一意な ID を得ます。型をインターンするので、必要な一意 ID 数は一意な型の数と、循環を構築・インターンするためのわずかな予備だけです。過去の経験では通常数千程度が最大なので、小さな整数で十分です。
+
+もう1つ、既存の基盤をできる限り残したいので、既存の再帰下降による巡回を少しだけ修正してすべて保持します。
+
+すべての型循環には `TypeStruct` が存在します。ただし関数が *自身* を引数に取る純粋な関数型の循環は例外です。それは奇妙なので、どう扱うかはそれほど気にしません。各 `TypeStruct` 生成器の中核で大域 `VISIT` ハッシュ表に要素を設定し、循環型作成の開始を示します。そしてその要素を検査し、循環を止めます。
+
+例を見ましょう。
+
+異なるデータ型の循環を持つかもしれない連結リストを使います。ただし、ここでは文字列だけです。連結リストは、簡単な代替と比べてひどく非効率なので、ほぼどんな仕事でも間違った構造であることが多いのですが、教材用のデータ構造としては優れています。
+
+```java
+struct List {
+ !List? !next; // Next pointer or null
+ str !name; // Payload
+};
+```
+
+この構造体の型は宣言に一致します。
+
+`struct List { List*? next, str !name };`
+
+これで文字列リストを構築できます。
+
+```java
+var list = new List{ name="Hello"; };
+list = new List { list, name="World"; };
+```
+
+### 循環する型の読み取り専用/final の検査
+
+どこかで読み取り専用の不変な版を作りたいとします。
+
+```java
+val hello = list; // Make a read-only version
+print(hello); // Pass along the read-only version
+```
+
+`hello` の型は何でしょうか。`List` の読み取り専用型です。しかし `List` は循環する型です。`Type.makeRO` を見ましょう。
+
+> 訳注:原文では上の `val hello = list` を読み取り専用版の作成と説明しています。一方、第17b章では `val` は束縛を固定し、初期化式のアクセス権限を保持すると説明しています。この訳では、原文の例と説明を保持しています。
+
+```java
+public final Type makeRO() {
+ if( isFinal() ) return this; // First check if already read-only
+ return recurOpen()._makeRO().recurClose();
+}
+```
+
+そして `isFinal()`:
+
+```java
+// Are all reachable struct Fields are final?
+public final boolean isFinal() { return recurClose(recurOpen()._isFinal()); }
+boolean _isFinal() { assert _type < TCYCLIC; return true; }
+```
+
+`recurOpen()` を呼んで再帰走査を開始/開き、`_isFinal()` を呼びながら再帰し、走査を終了/閉じて、真偽値を返します。`_isFinal()` は Java の final ではなく、Type のサブクラスでオーバーライドします。大半のサブクラスは単に問題を転送します。
+
+`@Override boolean _isFinal() { return _obj._isFinal(); }`
+
+そして `TypeStruct` に到達します。
+
+```java
+@Override boolean _isFinal() {
+ if( _open ) return false; // May have more non-final fields
+ if( VISIT.containsKey(_uid) ) // Test: been here before?
+ return true; // Cycles assume final
+ VISIT.put(_uid,this); // Set: don't do this again
+ for( Field fld : _fields )
+ if( !fld._isFinal() )
+ return false;
+ return true;
+}
+```
+
+ここには循環処理の共通パターン、**test-and-set** があります。まず確定できる答えを調べます。開いた構造体は部分的にしか定義されず、未解析の non-final フィールドが現れる可能性があるので、決して読み取り専用ではありません。次に以前訪問したかを **検査** し、そうなら再帰を止めて何らかの答えを返します。`isFinal` では、循環のほかのものもすべて final なら「final である」を返します。
+
+まだこの型を調べていなければ、ここを訪れたことを示す訪問ビットを **設定** し、通常の再帰下降でフィールドに同じ質問をします。
+
+この例では、`VISIT` に番兵を置いて再帰を開き、`struct List` に `makeRO` を呼びます。それが `_isFinal` を呼び、`_open`(開いていない)、`VISIT`(未訪問)を確認し、UID を `VISIT` に設定してフィールドを走査します。フィールドの検査は次のとおりです。
+
+`@Override boolean _isFinal() { return _final && _t._isFinal(); }`
+
+最初のフィールド `List*? !next` は non-final なので、すぐ `false` を返します。これで `List` の `_isFinal` が止まり、`isFinal()` 全体が `false` を返して再帰を終了/閉じます。`VISIT` もクリアします。
+
+### 循環する読み取り専用/final 型の作成
+
+構築してからインターンする手順を明確にするため、すべての型に一意 ID を付けましょう。一意な型の後ろに UID の例を付けた `List` 型を示します。フィールドも型なので UID を持ちます。
+
+```
+struct List#2 { // struct List has UID#2
+ List#2*?#3 !next#4, // Field next has UID#3, type ptr-or-null#3 to List#2
+ str#1 !name#5 // Field name has UID#5, type str#1
+};
+```
+
+同じ UID が繰り返されることから、`List#2` が2回現れています。List が循環しているためです。
+
+今度は実際に循環する型を作る必要があり、`makeRO()` は次の行に進みます。
+
+`return recurOpen()._makeRO().recurClose();`.
+
+再び `recurOpen` が再帰走査を開始/開き、それから子の Type クラスそれぞれでオーバーライドする `_makeRO()` を呼びます。`TypeStruct` 版を見ます。
+
+```java
+// Make a read-only version
+@Override TypeStruct _makeRO() {
+ // Check for already visited
+ TypeStruct ts = (TypeStruct)VISIT.get(_name);
+ if( ts!=null ) return ts; // Already visited
+ ts = recurPre(_name,_open); // Make a new type with blank fields
+ Field[] flds = ts._fields;
+ for( Field fld : flds ) fld._final = true;
+
+ // Now start the recursion
+ for( int i=0; i<flds.length; i++ )
+ flds[i].setType(_fields[i]._t._makeRO());
+
+ return ts;
+}
+```
+
+最初は **test-and-set** です。**検査** は型名に基づきます。`VISIT` から `List` TypeStruct の取得を試し、成功したら再帰せず早期に返します。つまり読み取り専用の型循環内では `List` オブジェクトは1つだけです。「整数の List」や「List の List の……」を含む複数の入れ子循環の絡み合いも考えられますが、ここでは簡単さのため、`List` のインスタンスを1つだけに近似します。
+
+この例で初めて `List` を訪問すると `VISIT` に見つかりません。次の `ts = recurPre(_name,_open)` は共通の再帰前処理です。新しい `TypeStruct` を作り、`VISIT` 表に **設定** します。新しいものは、すべてのフィールドを備え、まだその型が接続されていない、空の `List` 版です。すぐにフィールドを final にしますが、型はまだありません。
+
+> 訳注:原文の「all !fields !attached」は、続く説明と図に合わせて、フィールドの型が未設定である状態として訳しています。
+
+```
+struct List#12 {
+ ____ next#14,
+ ____ name#15
+}
+```
+
+最後にすべてのフィールド型で `_makeRO` を再帰呼び出しし、設定します。`TypeMemPtr` に `_fields[i]._t._makeRO()` を呼ぶと、再帰する `List` の `_makeRO()` を呼んだ後に、新しい TMP を作ります。
+
+この再帰は `TypeStruct._makeRO()` へ戻り、同じ型名で `VISIT` 表にヒットします。最初のフィールド設定を巻き戻すと、次になります。
+
+```
+struct List#12 {
+ List#12 *? #13 next#14, // Recursive next field makes a cycle
+ ____ name#15
+}
+```
+
+すべての型が新しい UID になっていることに注目してください。まだインターンできないので、以前の型にはできません。2番目のフィールド `str name` はすでに存在するため、次になります。
+
+```
+struct List#12 {
+ List#12 *? #13 next#14, // Recursive next field makes a cycle
+ str#2 name#15 // Reuse the interned type str#2
+}
+```
+
+ここで再帰を巻き戻し、複雑な `Type.recurClose()` に入ります。いくつかの例でコードを1ステップずつ実行してみるとよいでしょう。`TypeTest.testList` がこれらの型を構築します。高水準での概要は次のとおりです。
+
+管理を容易にするため、訪問したすべての型を `VISIT` からコピーします。すでにインターン済みのものを発見したら後続の処理から除去しますが、HashMap 内にない方が簡単です。
+
+**走査 #1:** 循環に属さない既存のインターン済み部分を探し、置き換えます。新しい型循環から古いインターン済み型へポインタが出ているのに、そのコピーを作ってしまっている場合があります。この段階の後も新しい循環は残りますが、出力辺は可能なら以前の型を指します。置き換えた型は、遅延した `FREES` リストへ入ります。
+
+**走査 #2:** すべての型は *双対* を事前計算して直接使えるようにしています。ここで再帰的な循環する双対を作ります。
+
+**走査 #3:** 新しい型を登録します。新しくインターンされた型への共通ポインタをここで発見する *可能性がある* ので、進みながら再び検査・置換し、使わない型を再度遅延 `FREES` リストへ入れます。この段階はハッシュ表検索を必要とし、それには循環ハッシュと循環 equals が必要です。
+
+**走査 #4:** 遅延解放するすべての型をここで解放し、将来の型生成で再利用します。
+
+最終的に、新しい `List` の循環型は完全にインターンされます。読み書き可能版 `List#2` は同じ読み書き可能版 `List#2` を指し、読み取り専用版 `List#12` は同じ読み取り専用版 `List#12` を指します。
+
+### 循環する HashCode と Equals
+
+循環する `hashCode` と `equals` はさらに説明が必要です。2つの循環を等価比較するとき、比較の開始位置に関係なく等価であってほしいのです。つまり循環 `A<->B` は循環 `B<->A` と等しいべきです。両方のハッシュコードは同じでなければならず、順序で変化するハッシュは使えません。
+
+たとえば A の静的ハッシュ値が1で、さらに B のハッシュから再帰的な成分があり、逆に B の静的値が3で A からの再帰成分があるとします。`parent.hash*7 + child.hash` で計算すると、A からは `A.hash * 7 + B.hash`、つまり `1 * 7 + 3 == 10` です。後で B から始めると `B.hash * 7 + A.hash`、つまり `3 * 7 + 1 == 22` になります。ハッシュが異なるため、ハッシュ表が等価性を見逃す可能性があります。また循環で単純に再帰下降すれば、スタックオーバーフローまで再帰してクラッシュします。
+
+`TypeStruct.hash()` が再帰しないようにして解決します。そのためハッシュ関数はやや弱く、フィールド名とエイリアスだけに依存します。
+
+*循環 equals* は `VISIT` 表が空でないことにより発動し、`VISIT` と関心を分離するため、別の訪問表 `CEQUALS` を使います。循環 equals 検査は通常の `eq` と同様、構成部分を再帰下降し、最初に確定的な静的検査を行い、再帰前にはおなじみの **test-and-set** を使います。
+
+同じ2型を再び循環 equals で比較したら、ほかがすべて等しい限り、循環についても等しいと仮定します。
+
+## メソッド、final フィールド、クラス
+
+メソッドは、構造体内で宣言され、暗黙の `self` 引数を取る、単なる関数値(final フィールド)です。
+
+元の構造体定義で宣言した final フィールドは *値* であり、変化できません。すべてのインスタンスで同じです。構造体のメモリ占有部分から、final フィールドの値の集まりである *クラス* へ移動します。クラスは単なる名前空間で具体的な実装を持たず、実体化されません。
+
+```java
+struct vecInt {
+ u32 !len; // Actual number of elements
+ int[] !buf; // Array of integers.
+
+ // Since "add" is declared with `val` in the original struct definition,
+ // it is a final field and is moved out of here to the class.
+
+ // Method: Internal: copy buf to a new size
+ val _grow = { int sz ->
+ var buf2 = new int[sz];
+ for( int i=0; i<len; i++ )
+ buf2[i] = buf[i];
+ buf = buf2;
+ };
+
+ // Method: Add an element, growing the backing array when needed.
+ val add = { int e ->
+ if( len >= buf# ) _grow(buf#*2);
+ buf[len++] = e;
+ return self;
+ };
+
+
+};
+
+val primes = new vecInt{buf = new int[4];}.add(2).add(3).add(5).add(7).add(11);
+```
+
+ここで `val add` フィールドは、定数関数値である *値* フィールドです。基本的な `vecInt` オブジェクトから `vecInt` の *クラス* へ移り、オブジェクト内で記憶領域を占めません。`_grow` も同様です。そのため `vecInt` インスタンスのサイズは、`u32` の長さと配列ポインタに、配置調整のパディングを加えたものです。配列ヘッダと要素は別の割り当てを占めます。拡張後の古いバッファの回収は、この例の範囲外です。`return primes.len*100+primes.buf[4];` を追加すると、RISC-V と ARM の両方で511になります。
+
+## RegAlloc の改良: 多数の使用のグループ化
+
+第22章では色の優先度と安いスピルの順序を改善しました。この章では、使用が互換性のないレジスタを要求する、よく使う値を分割する方法を追加します。たとえば複数のシフトが同じカウントレジスタを必要とし、別の使用が異なるレジスタを必要とする場合です。互換グループごとに1コピーを共有すると、使用ごとに別のコピーを挿入するのを避けられます。
+
+割り当て器は、空のレジスタマスクと、2つより多い固定レジスタ使用を持つ、単一定義の区間にこれを適用します。重なる使用マスクの共通部分を取り、グループ化します。グループを狭めても以前の使用と互換なので、1走査で十分です。交わらないマスクは別グループにします。入力を接続し直す前に異なる使用側のスナップショットを取り、スケジューリング専用辺は無視し、値が複数引数を供給しても呼び出しを1回だけ数えます。
+
+複数の呼び出しで使う値は、レジスタ破壊が共有コピーの利点を打ち消しがちなので、グループ化しません。すべての使用のループ深さが同じ場合に既存コピーを分割することも含め、通常のループ境界分割を代替として保持します。低頻度側を先にするループ分割は第24章、面積/コストのスピル順位付けは第25章に残します。
+
+第21章の隣接コピー転送も引き継ぎます。彩色後、次の命令に唯一の使用がある場合、オペランドマスクが許し、2アドレスの結び付きを壊さなければコピー元レジスタを読めます。以下の表は、この整理と遅延メモリの前方移植を含みます。第22章の群には、以前の監査後に追加した移動0の C 戻り値 ABI 確認2件も含むようになりました(24ではなく26項目)。
+
+このディレクトリで `make spill-stats` を実行してください。以下の全行は **この章のコンパイラ**、シード123、各群の固定されたソース/対象の組み合わせを使います。Windows 実行は x86 SystemV/Win64 と RISC-V/ARM SystemV を合わせます。診断マシングラフは別に確認し、数には寄与しません。
+
+| プログラム群 | コンパイル数 | 残存する移動 | ループ重み付き移動 |
+|---|---:|---:|---:|
+| 第20章 | 39 | 323 | 442 |
+| 第21章 | 52 | 433 | 965 |
+| 第22章 | 26 | 67 | 67 |
+| 第23章 | 30 | 78 | 225 |
+| **合計** | **147** | **901** | **1,699** |
+
+`_spills` はレジスタ移動を含む残存 SplitNode を数え、`_spillScaled` は `8^loopDepth` で重み付けします。生成移動のコンパイラによる推定であり、実行時メモリアクセスではありません。報告器は個々のコンパイルと CPU/ABI 合計を表示し、スピル期待値や実行確認が失敗すれば、依然として失敗を報告します。
+
+以前の監査では、隣接コピー転送前にグループ化を無効にしても、**同じ933移動/1,745重み付き移動** でした。元のグループ化コードも同じ合計です。したがってこのスイートには、グループ化によるスピル改善は見られません。縮小したマシングラフの回帰テストは、旧実装の null マスクによるクラッシュも含め、互換性と呼び出しの規則を検証します。もっともらしい割り当て器のヒューリスティックであっても、効果を主張するには、それが実際に適用される処理対象での測定が必要です。
+
+以前の群だけでは、第22章コンパイラは838移動/1,496重み付き移動で、このコンパイラは849/1,514でした。この比較は割り当て以外の変更を含みます。特に第20章の固定した String 入力は、ここでは最適化後も残り16重み付き移動を要しますが、第22章はこのシードで除去しました。その差をグループ化に帰するのは誤解を招きます。
+
+元の64ビット `person21` とガード付き `stringHash21` 入力は、第21章の群を維持します。改訂した String 入力とこの章で有効にした2つのエンコーディングテストは第23章の群に属します。群を固定することで、言語とテストが成長しても、以後の比較に意味が保たれます。
diff --git a/docs/chapter23/README.md b/docs/chapter23/README.md
new file mode 100644
index 00000000..747b37e3
--- /dev/null
+++ b/docs/chapter23/README.md
@@ -0,0 +1,463 @@
+# Chapter 23: Methods and Revisiting Types
+
+English | [日本語](README.ja.md)
+
+[Previous: Chapter 22](../chapter22/README.md) |
+[Next: Chapter 24](../chapter24/README.md)
+
+In this chapter we add *methods*, functions defined in structs which take a
+hidden `self` argument and can access the struct fields. Here is an
+`indexOf` method in a String-like class:
+
+```java
+// A String-like class, with methods
+struct String {
+ u8[~] buf; // Buffer of read-only characters
+
+ // Return the index of the first character 'c' or -1
+ val indexOf = { u8 c -> // Hidden 'self' argument
+ for( int i=0; i<buf#; i++ ) // Direct access to `buf` field
+ if( buf[i]==c )
+ return i;
+ return -1; // 'c' not in 'self'
+ };
+};
+```
+
+Also in this chapter we revisit our Types and make some major changes:
+- Types are now *cyclic*: a Type can (indirectly) point to itself.
+- Types remain
+ [*interned*](https://en.wikipedia.org/wiki/Interning_(computer_science)) or
+ [*hash-consed*](https://en.wikipedia.org/wiki/Hash_consing).
+- Type objects are managed via an object pool.
+- Newly created types probe the interning table, and if a hit is found, the
+ original is used, and the new type is returned to the object pool.
+- The intern table hit rate is something like 99.9%; nearly all types created
+ have already been created! This last point turns into an interesting
+ performance win; much of the work in an optimizing compiler deals with
+ manipulating Types, keeping the count of Types small and fitting in the
+ faster levels of cache is a nice payoff.
+- Many of the interning algorithms require cycle-handling via some kind of
+ "visit bit". One easy way to do this is add a *unique id* UID to every type,
+ a small dense integer that is used in bit-sets to avoid visiting a type more
+ than once. Smaller UIDs use smaller bit-sets, which again fit in the faster
+ cache levels.
+- As a consequence, our types are cyclic (the goal!), and **faster** and have a
+ more complex implementation. **Using** the types remains the same; the same
+ Type API is used when creating them, MEETing, making Read-Only, printing,
+ etc; only the implementation details change.
+
+
+You can also read [this chapter](https://github.com/SeaOfNodes/Simple/tree/linear-chapter23) 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-chapter22...linear-chapter23) it to the previous chapter.
+
+
+Here is the [complete language grammar](23-grammar.md) for this chapter.
+
+## Why Cyclic Types?
+
+Cyclic types give us a sharper analysis than the alternative, and thus admit
+more programs or better optimizations or both. In Simple's case Types are also
+used for type-checking, so sharper types means we allow more valid programs.
+
+**Why Cyclic Types Now**? Because we hit them as soon as we allow a
+function definition inside of a struct which takes the struct as an argument,
+i.e. *methods* taking a *self* argument.
+
+Let's look at our `String.indexOf` example above. Here we define a `String`
+with an `indexOf` method; a hidden argument string `self` is passed in
+and searched. What is the type of `struct String`?
+
+`struct String { u8[~] buf; { !String !self, u8 c -> int } indexOf; }`
+
+It is the type named `String` with a field `u8[~] buf` and a final assigned
+constant field `indexOf`, itself with type `{ !String !self, u8 c -> int }`.
+i.e., the type of `String` has a reference to itself, nested inside the type of
+`indexOf`.... i.e. `String`'s type is *cyclic*.
+
+
+## Less Cyclic Types
+
+This problem of cyclic types has been around for a long time, and there are a
+number of tried and true methods for dealing with them. One easy one is to
+have a type *definition* and a separate type *reference*. The reference refers
+to the type by doing some kind of lookup; an easy one is via the type name and
+the parser's symbol table i.e., some kind of hash table lookup.
+
+In this model the cycle is effectively avoided; all "back edges" in the type
+cycle are really done by the reference edge (itself possibly a hash table
+lookup).
+
+So why not go down this route?
+
+Because all type references lead back to the same type definition - which
+means any specialization of type information is lost, because *all* type
+references lead back to the same definition and you end up taking the MEET
+over *all* paths.
+
+Here's an example, a Linked List with a Java `Object` or a C `void*` payload:
+
+```java
+struct List {
+ !List !next;
+ Object payload; // equivalently for C: void*
+}
+// Then walk a collection of ints and build a List:
+List nums = null;
+for( int x : ary_ints )
+ nums = new List{next=nums; payload=x; }
+
+// Again for strings:
+List strs = null;
+for( String x : ary_strs )
+ strs = new List{next=strs; payload=x; }
+```
+
+What is the type of `nums`? It's `*List`... which is:
+
+```
+struct List { List *next; Object payload }
+```
+
+No sharpening of `Object` to `int` nor `String` because every List object
+has a payload of `Object` and `Object` MEET `int` is back to `Object`.
+
+If we allow truly cyclic types then in these kinds of places we can discover
+(or infer) a sharper type: `*List<int> { *List<int> next, int payload }`
+basically building up *generics* in the optimizer. This is not generics in the
+language nor type-variables per-se, but it goes a long way towards them.
+
+
+## Handling cyclic types
+
+In this Chapter Simple handles cyclic types directly. This means we have
+`Type` objects whose child types can point back to the original - and that
+means
+[*recursive descent*](https://en.wikipedia.org/wiki/Recursive_descent_parser)
+no longer works on Types.
+
+Up till now we've used recursive descent when building up Types: child types
+are built first, then interned, then the interned children are used to build up
+the next layer. Because each layer is interned and because recursive descent
+stops at interned Types we avoid repeating work at shared Types. This makes
+operations on the current Types linear in the *delta* between the old and new
+Types, i.e. incremental and fast.
+
+With the new cyclic types we now run into the problem that we cannot intern a
+cycle until we see the whole cycle. Also hash tables require an equality
+check and we cannot use recursive descent to compare a cycle for equality.
+
+The standard way to deal with cycles in a graph is with some kind of *visited*
+notion, a "I have been here before" indicator. We will use a hashtable keyed
+from a unique type ID; "visited" just means your unique ID is a key in the
+table. Every Type thus picks up a unique ID from a global counter. Since we
+will be interning Types we only need as many unique IDs as we have unique Types
+(plus a few spare to build and intern cycles). From prior history I know this
+number typically maxes out in the low thousands so a small integer will do.
+
+Another thing - we want to keep as much of our existing infrastructure as
+we can, so we are going to keep all our existing recursive descent visitations
+with some small modifications.
+
+`TypeStructs` exist in every Type cycle (except pure function type cycles,
+where a function takes *itself* as an argument, which are weird and we kinda
+don't care how they get handled). So in the core part of every `TypeStruct`
+generator we'll set an element in a global `VISIT` hashtable to signal the
+start of a cyclic Type creation - and we'll check for those elements to stop
+cycling.
+
+
+
+
+Time for an example!
+
+Let's go with a linked list, where we might have cycles of unrelated payloads
+(but here only strings). Mind you, a linked list is almost always the wrong
+structure for any given job (being wildly inefficient compared to easy
+alternatives) but it is a great tutorial data structure.
+
+
+```java
+struct List {
+ !List? !next; // Next pointer or null
+ str !name; // Payload
+};
+```
+
+And the type of this struct matches its declaration:
+
+`struct List { List*? next, str !name };`
+
+We can use this to build up a list of strings:
+
+```java
+var list = new List{ name="Hello"; };
+list = new List { list, name="World"; };
+```
+
+
+### Checking a cyclic type for Read-Only / Final
+
+Suppose at some point we want to make a read-only immutable version:
+```java
+val hello = list; // Make a read-only version
+print(hello); // Pass along the read-only version
+```
+
+What is the type of `hello`? It is the read-only type of `List`... and `List` is
+a cyclic type. Follow the implementation of
+[`Type.makeRO`, `isFinal`, and `_isFinal`](../../src/main/java/com/seaofnodes/simple/type/Type.java).
+
+We call `recurOpen()` to start/open the recursive walk, do the recursion
+calling `_isFinal()` and end/close the recursive walk, and return a boolean
+answer. `_isFinal()` is NOT Java final, and is overridden in the Type subclasses.
+Subclasses implement the check for their own representation. For example,
+[`TypeMemPtr._isFinal`](../../src/main/java/com/seaofnodes/simple/type/TypeMemPtr.java)
+reads the pointer's access permission, while
+[`TypeStruct._isFinal`](../../src/main/java/com/seaofnodes/simple/type/TypeStruct.java)
+walks the fields and handles cycles.
+
+Here we see a common pattern in dealing with cycles: the **test-and-set**. We
+check for any hard and fast answers (open structs are never read-only; they are
+only partially defined and as-yet-unparsed non-final fields may appear), then
+we **test** if we have been here before, and if so stop the recursion and
+return some kind of answer. For "isFinal" the returned answer is "yes final"
+as long as everything else in the cycle is also final.
+
+If we have not already checked this Type, we **set** the "visit" bit indicating
+we've been here before, and then proceed to do normal recursive-descent on the
+fields asking the same question recursively.
+
+For our example, we open the recursion (set a sentinel in `VISIT`), call
+`makeRO` on `struct List`, which then calls `_isFinal` which then checks
+the `_open` flag (not open), checks the `VISIT` (not visited), sets the
+UID in `VISIT` and starts walking the fields.
+[`Field._isFinal`](../../src/main/java/com/seaofnodes/simple/type/Field.java)
+checks both the field's binding and its type.
+
+The first field is `List*? !next`, which is not-final, so immediately returns
+`false`, which stops `_isFinal` on `List` and the whole `isFinal()` call returns
+`false`, and ends/closes the recursion (clears `VISIT`).
+
+
+### Making a cyclic type for Read-Only / Final
+
+To make the build-then-intern more obvious, let's put unique IDs on all our
+Types. Here is the Type of `List` with sample UIDs following each unique type.
+Fields are also just types so also get UIDs.
+
+```
+struct List#2 { // struct List has UID#2
+ List#2*?#3 !next#4, // Field next has UID#3, type ptr-or-null#3 to List#2
+ str#1 !name#5 // Field name has UID#5, type str#1
+};
+```
+
+You can see from the repeated UIDs that `List#2` happens twice... because List
+is cyclic.
+
+Now we do really need to make a cyclic type, and so our `makeRO()` call falls
+into the recursive path in
+[`Type.makeRO`](../../src/main/java/com/seaofnodes/simple/type/Type.java).
+
+Once again `recurOpen` starts/opens our recursive walk, and then we call
+`_makeRO()` which is overridden in the child Type classes. Follow
+[`TypeStruct._makeRO` and `recurPre`](../../src/main/java/com/seaofnodes/simple/type/TypeStruct.java)
+for the implementation.
+
+First up is the **test-and-set**. The **test** is by type name; we attempt to
+get a `List` TypeStruct from `VISIT`, and if successful we return early -
+without recursion. This means we can only have one `List` object in a R/O
+cycle of types. You can imagine a tangle of multiple nested cyclic types where
+some are "List-of-int" and some are "List-of-Lists-of-", but here in the name
+of simplicity we choose to approximate our types to only a single instance of
+`List`.
+
+In this example on first visit of `List` we will miss in `VISIT`. The next call
+to `ts = recurPre(_name,_open)` does common shared pre-recursive work: we make
+a new `TypeStruct` and **set** it in the `VISIT` table. The new `TypeStruct`
+is a blank version of `List` with all !fields !attached; we force these fields to
+be final right away but they are missing their types:
+
+```
+struct List#12 {
+ ____ next#14,
+ ____ name#15
+}
+```
+
+
+Finally, we recursively call `_makeRO` on all the field types and set them in.
+We call `_fields[i]._t._makeRO()` on a `TypeMemPtr` which makes a new
+TMP after calling `_makeRO()` on the recursive `List`.
+
+This recursion comes right back to `TypeStruct._makeRO()`, and hits in the
+`VISIT` table on the same type name. Unwinding the first field set, we have:
+
+```
+struct List#12 {
+ List#12 *? #13 next#14, // Recursive next field makes a cycle
+ ____ name#15
+}
+```
+
+You should notice all new UIDs for all the Types. Since we cannot intern yet,
+we cannot end up with prior types. The second field `str name` does already
+exist, so we end up with:
+
+```
+struct List#12 {
+ List#12 *? #13 next#14, // Recursive next field makes a cycle
+ str#2 name#15 // Reuse the interned type str#2
+}
+```
+
+At this point the recursion unwinds and we head into the complex
+[`Type.recurClose()`](../../src/main/java/com/seaofnodes/simple/type/Type.java).
+You might try single-stepping through
+[`TypeTest.testList`](../../src/test/java/com/seaofnodes/simple/TypeTest.java),
+which builds these types. Here is a high
+level summary:
+
+Copy all the visited types out of `VISIT` for easier management; we might find
+some of these types already interned, and then we'll remove them from further
+processing; this is easier if they are not in a HashMap.
+
+**Pass#1:** look for pre-existing interned parts not in any cycle, and just
+replace them. We could have a new cycle of Types with pointers off to older
+interned types... that we made copies of. After this step, we'll still have
+new cycles but the output edges will point to prior types if possible.
+Replaced types hit the delayed `FREES` list.
+
+**Pass#2:** All types have their *duals* pre-computed and directly available.
+Make the recursive cyclic duals now.
+
+**Pass#3:** Install the new types. It *is* possible to now discover common
+pointers to newly interned types, and so again we need to test and replace as
+we go (and put the unused types on the delayed `FREES` list again). This step
+requires hash table probe - which requires a cyclic hash and cyclic equals.
+
+**Pass#4:** Free up all the delayed-free Types now, to be recycled in future
+Type productions.
+
+In the end, the new `List` cyclic type is wholly interned - with a R/W version
+of `List#2` pointing back to the R/W `List#2` and the R/O version `List#12`
+pointing back to the same R/O `List#12`.
+
+
+
+### Cyclic HashCode and Equals
+
+Cyclic `hashCodes` and `equals` deserve more discussion. When comparing two
+cycles for equivalence we want to be equivalent regardless of where the
+comparison starts. i.e. cycle `A<->B` should be equal to cycle `B<->A`. This
+means they both need the same hashcode, and this means we cannot use
+order-varying hashes!
+
+E.g. Suppose A has static hash value 1 (plus something recursively from B's
+hash), and vice-versa B has static hash value 3 (and something recursive from
+A), and we compute hashes as `parent.hash*7 + child.hash`. If we compute A's
+hash as `A.hash * 7 + B.hash` we get `1 * 7 + 3 == 10`, and then later we start
+from B we get: `B.hash * 7 + A.hash` and which then becomes `3 * 7 + 1 ==
+22`... and their hashes differ so the hash table might miss the equivalence.
+Also, if we simply use recursive descent in the cycle we'll
+recurse until stack overflow and crash.
+
+We fix this by having `TypeStruct.hash()` *not* recurse - thus its hash
+function is somewhat weak, depending only on the field names and aliases.
+
+*Cyclic-equals* is triggered by the `VISIT` table being not-empty, and uses
+another unrelated *visit* table, the `CEQUALS` table, to separate concerns from
+`VISIT`. The cycle-equals check proceeds like the normal `eq` with recursive
+descent on the parts, and some up-front hard-and-fast static checks - and the
+ubiquitous **test-and-set** pattern before the recursion.
+
+If we ever compare the same two types for cycle-equals again, we assume they
+will be cycle-equals (all other things being equals).
+
+
+
+## Methods and Final Fields and Classes
+
+Methods are simply function values (final fields) declared inside a struct that
+take an implicit `self` argument.
+
+Final fields declared in the original struct definition are *values* and cannot
+change; they are the same for all instances. They get moved out of the
+struct's memory footprint and into a *class* - a collection of values from the
+final fields. Classes are just plain namespaces and have no concrete
+implementation; they are not reified.
+
+```java
+struct vecInt {
+ u32 !len; // Actual number of elements
+ int[] !buf; // Array of integers.
+
+ // Since "add" is declared with `val` in the original struct definition,
+ // it is a final field and is moved out of here to the class.
+
+ // Method: Internal: copy buf to a new size
+ val _grow = { int sz ->
+ var buf2 = new int[sz];
+ for( int i=0; i<len; i++ )
+ buf2[i] = buf[i];
+ buf = buf2;
+ };
+
+ // Method: Add an element, growing the backing array when needed.
+ val add = { int e ->
+ if( len >= buf# ) _grow(buf#*2);
+ buf[len++] = e;
+ return self;
+ };
+
+
+};
+
+val primes = new vecInt{buf = new int[4];}.add(2).add(3).add(5).add(7).add(11);
+```
+
+Here the `val add` field is a *value* field, a constant function value. It is
+moved out of the basic `vecInt` object into `vecInt`'s *class*, and takes no
+storage space in `vecInt` objects. The same happens for `_grow`. The size of
+a `vecInt` instance is thus its `u32` length and array pointer, plus alignment
+padding. The array header and elements occupy a separate allocation. Reclaiming
+the old buffer after growth is outside this example. Appending
+`return primes.len*100+primes.buf[4];` produces 511 on both RISC-V and ARM.
+
+## RegAlloc improvements: grouping popular uses
+
+Chapter 22 improved color preferences and cheap-spill ordering. This chapter
+adds a way to split a popular value whose uses demand incompatible registers.
+For example, several shifts may need the same count register while another use
+needs a different register. Sharing one copy for each compatible group can
+avoid inserting a separate copy at every use.
+
+The allocator applies this to a single-definition live range with an empty
+register mask and more than two fixed-register uses. It intersects overlapping
+use masks into groups; narrowing a group keeps it compatible with its earlier
+users, so one pass suffices. Disjoint masks start separate groups. It snapshots
+distinct users before rewiring their inputs, ignores scheduling-only edges,
+and counts a call once even when the value supplies several arguments.
+
+Grouping declines values used by multiple calls, whose register kills tend to
+undo the benefit of a shared copy. The usual loop-boundary splitting remains
+the fallback, including splitting an existing copy when all uses have the same
+loop depth. Cold-first loop splitting is left for Chapter 24; area/cost spill
+ranking remains for Chapter 25.
+
+Rows are fixed test cohorts; columns are compiler chapters. Each cell gives
+**Ops / RA / X**: executed ARM+RISC-V instructions, the subset emitted for
+allocator copies/rematerializations, and x86's loop-weighted split-move estimate.
+**Cases D / X** counts measured dynamic program/target cases and x86 compilations;
+membership and inputs stay fixed across columns. Native-library work is excluded.
+Chapter 20 has no encoder, hence no dynamic counts. Lower is better within each
+metric; see the [measurement details](../regalloc-spills.md) for coverage.
+
+<!-- spill-matrix:start -->
+| Test cohort | Cases D / X | Ch 20: Ops / RA / X | Ch 21: Ops / RA / X | Ch 22: Ops / RA / X | Ch 23: Ops / RA / X |
+|---|---:|---:|---:|---:|---:|
+| Ch 20 | 22 / 13 | — / — / 161 | 55,173 / 5,407 / 174 | 58,751 / 9,099 / 175 | 58,736 / 9,084 / 184 |
+| Ch 21 | 20 / 18 | | 6,207 / 261 / 461 | 6,324 / 379 / 475 | 6,326 / 381 / 475 |
+| Ch 22 | 14 / 8 | | | 198 / 60 / 7 | 194 / 56 / 7 |
+| Ch 23 | 14 / 8 | | | | 630 / 160 / 36 |
+<!-- spill-matrix:end -->
diff --git a/docs/chapter23/examples/A_helloWorld.smp b/docs/chapter23/examples/A_helloWorld.smp
new file mode 100644
index 00000000..07ff935f
--- /dev/null
+++ b/docs/chapter23/examples/A_helloWorld.smp
@@ -0,0 +1,3 @@
+// -*- mode: java; -*-
+sys.io.p("Hello, World!");
+return 0;
diff --git a/docs/chapter23/examples/BubbleSort.smp b/docs/chapter23/examples/BubbleSort.smp
new file mode 100644
index 00000000..938e3181
--- /dev/null
+++ b/docs/chapter23/examples/BubbleSort.smp
@@ -0,0 +1,37 @@
+// -*- mode: java; -*-
+
+// An implementation of
+// https://sampleprograms.io/projects/bubble-sort/
+//
+// Bubble sort, with reading (parsing) and writing (formatting) the data.
+
+val sort = { !sys.aryi64 nums ->
+ var done = false;
+ while( !done ) {
+ done = true;
+ for( var i = 0; i < nums.len-1; i++ )
+ if( nums.buf[i] > nums.buf[i+1] ) {
+ int tmp = nums.buf[i];
+ nums.buf[i] = nums.buf[i+1];
+ nums.buf[i+1] = tmp; // Swap
+ done = false;
+ }
+ }
+};
+
+// Read from stdin a line of the form "[ 4, 3, 2, 1]" and print it sorted.
+val main = { ->
+ // Parse array of numbers from stdin
+ var nums = sys.parseAryI64( sys.io.stdin() );
+ if( !nums ) {
+ sys.io.p("stdin must look like [4, 3, 2, 1]");
+ return 1;
+ }
+
+ // Bubble sort array of integers
+ sort(nums);
+
+ // Print a list of numbers to stdout
+ nums.str().write(1);
+ return 0;
+};
diff --git a/docs/chapter23/font-awesome-4.7.0.zip b/docs/chapter23/font-awesome-4.7.0.zip
new file mode 100644
index 00000000..b6bdb4bd
--- /dev/null
+++ b/docs/chapter23/font-awesome-4.7.0.zip
Binary files differ
diff --git a/docs/chapter23/ifg_trim.pdf b/docs/chapter23/ifg_trim.pdf
new file mode 100644
index 00000000..d952fd4f
--- /dev/null
+++ b/docs/chapter23/ifg_trim.pdf
Binary files differ
diff --git a/docs/chapter23/lattice.gv b/docs/chapter23/lattice.gv
new file mode 100644
index 00000000..8661082d
--- /dev/null
+++ b/docs/chapter23/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/chapter23/lattice.svg b/docs/chapter23/lattice.svg
new file mode 100644
index 00000000..e72b473b
--- /dev/null
+++ b/docs/chapter23/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/chapter23/lattice2.gv b/docs/chapter23/lattice2.gv
new file mode 100644
index 00000000..6aa37fa0
--- /dev/null
+++ b/docs/chapter23/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/chapter23/lattice2.svg b/docs/chapter23/lattice2.svg
new file mode 100644
index 00000000..8b682aa7
--- /dev/null
+++ b/docs/chapter23/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/chapter23/pardoe.docx b/docs/chapter23/pardoe.docx
new file mode 100644
index 00000000..24fde921
--- /dev/null
+++ b/docs/chapter23/pardoe.docx
Binary files differ
diff --git a/docs/chapter23/webtcp.min.js b/docs/chapter23/webtcp.min.js
new file mode 100644
index 00000000..bf23ee5a
--- /dev/null
+++ b/docs/chapter23/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/IterPeeps.java b/src/main/java/com/seaofnodes/simple/IterPeeps.java
index 7c2f1b0c..c4b96c5c 100644
--- a/src/main/java/com/seaofnodes/simple/IterPeeps.java
+++ b/src/main/java/com/seaofnodes/simple/IterPeeps.java
@@ -1,6 +1,7 @@
package com.seaofnodes.simple;
import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.util.Ary;
import com.seaofnodes.simple.node.*;
import java.util.Arrays;
import java.util.BitSet;
@@ -102,6 +103,7 @@ public class IterPeeps {
// there's a new user), and replace in the graph.
if( x != n ) {
for( Node z : n. _inputs ) _work.push(z);
+ for( Node z : x._outputs ) _work.push(z);
n.subsume(x);
}
}
diff --git a/src/main/java/com/seaofnodes/simple/Parser.java b/src/main/java/com/seaofnodes/simple/Parser.java
index ea618757..6084eaa5 100644
--- a/src/main/java/com/seaofnodes/simple/Parser.java
+++ b/src/main/java/com/seaofnodes/simple/Parser.java
@@ -3,7 +3,9 @@ package com.seaofnodes.simple;
import com.seaofnodes.simple.codegen.CodeGen;
import com.seaofnodes.simple.node.*;
import com.seaofnodes.simple.type.*;
-
+import com.seaofnodes.simple.util.Ary;
+import com.seaofnodes.simple.util.Utils;
+import com.seaofnodes.simple.node.ScopeNode.Kind;
import java.util.*;
/**
@@ -81,8 +83,7 @@ public class Parser {
FunNode _fun; // Current function being parsed
// Mapping from a type name to a Type. The string name matches
- // `type.str()` call. No TypeMemPtrs are in here, because Simple does not
- // have C-style '*ptr' references.
+ // `type.str()` call.
public static HashMap<String, Type> TYPES = defaultTypes();
// Mapping from a type name to the constructor for a Type.
@@ -152,7 +153,7 @@ public class Parser {
_xScopes.push(_scope);
// Parse whole program, as-if function header "{ int arg -> body }"
- parseFunctionBody(_code._main,loc(),"arg");
+ parseFunctionBody( _code._main,loc(),"arg");
// Kill an empty default main. Keep only if it was explicitly defined
// (programmer asked for a "main") or it has stuff (i.e. beginner
@@ -188,14 +189,20 @@ public class Parser {
/**
* Parses a function body, assuming the header is parsed.
*/
- private ReturnNode parseFunctionBody(TypeFunPtr sig, Lexer loc, String... ids) {
+ private ReturnNode parseFunctionBody( TypeFunPtr sig, Lexer loc, String... ids) {
return parseFunctionBody(sig, loc, null, ids);
}
- private ReturnNode parseFunctionBody(TypeFunPtr sig, Lexer loc, java.util.BitSet finals, String... ids) {
+ private ReturnNode parseFunctionBody( TypeFunPtr sig, Lexer loc, java.util.BitSet finals, String... ids) {
+ // If this is a method, record & restore the existing struct fields,
+ // which are also the struct initializers. The method might update
+ // mutable fields, so when the function parse is done reset back to
+ // pre-function-parse values.
+ Node[] preParse = new Node[_scope.nIns()];
+ for( int i=0; i<_scope.nIns(); i++ )
+ preParse[i] = _scope.in(i)==null ? null : _scope.in(i).keep();
+
// Stack parser state on the local Java stack, and unstack it later
- Node oldctrl = ctrl().keep();
- Node oldmem = _scope.mem().keep();
FunNode oldfun = _fun;
ScopeNode breakScope = _breakScope; _breakScope = null;
ScopeNode continueScope = _continueScope; _continueScope = null;
@@ -210,7 +217,7 @@ public class Parser {
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((Lexer)null,null,null).init();
+ RegionNode r = new RegionNode( 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();
@@ -219,10 +226,11 @@ public class Parser {
assert ret.inProgress();
_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
// external calls.
- _scope.push(ScopeNode.Kind.Function);
+ _scope.push(new Kind.Func());
ctrl(fun); // Scope control from function
// Unknown callers supply whole memory; field aliases are discovered lazily.
_scope.mem(new ParmNode(ScopeNode.MEM0,1,TypeMem.BOT,fun,con(TypeMem.BOT)).peephole());
@@ -265,9 +273,10 @@ public class Parser {
_fun = oldfun;
_breakScope = breakScope;
_continueScope = continueScope;
- // Reset control and memory to pre-function parsing days
- ctrl(oldctrl.unkeep());
- _scope.mem(oldmem.unkeep());
+
+ // Reset all fields to pre-parse days
+ for( int i=0; i<preParse.length; i++ )
+ _scope.setDef(i,preParse[i]==null ? null : preParse[i].unkeep());
return ret;
}
@@ -281,7 +290,7 @@ public class Parser {
* Does not parse the opening or closing '{}'
* @return a {@link Node} or {@code null}
*/
- private Node parseBlock(ScopeNode.Kind kind) {
+ private Node parseBlock(Kind kind) {
// Enter a new scope
_scope.push(kind);
Node last = ZERO;
@@ -313,7 +322,7 @@ public class Parser {
// Break ambiguity around leading function types and starting a block
else if (peek('{') && !isTypeFun() ) {
match("{");
- return require(parseBlock(ScopeNode.Kind.Block),"}");
+ return require(parseBlock(new Kind.Block()),"}");
}
// Declaration or normal assignment/expression
else return parseDeclarationStatement();
@@ -349,8 +358,8 @@ public class Parser {
// }
// }
require("(");
- _scope.push(ScopeNode.Kind.Block); // Scope for the index variables
- if( !match(";") ) // Can be empty init "for(;test;next) body"
+ _scope.push(new Kind.Block()); // Scope for the index variables
+ if( !match(";") ) // Can be empty init "for(;test;next) body"
parseDeclarationStatement(); // Non-empty init
Node rez = parseLooping(true);
_scope.pop(); // Exit index variable scope
@@ -467,7 +476,7 @@ public class Parser {
ctrl(XCTRL); // Kill current scope
// Prune nested lexical scopes that have depth > than the loop head
// We use _breakScope as a proxy for the loop head scope to obtain the depth
- while( cur._lexSize.size() > _breakScope._lexSize.size() )
+ while( cur.depth() > _breakScope.depth() )
cur.pop();
// If this is a continue then first time the target is null
// So we just use the pruned current scope as the base for the
@@ -475,7 +484,7 @@ public class Parser {
if( toScope == null )
return cur;
// toScope is either the break scope, or a scope that was created here
- assert toScope._lexSize.size() <= _breakScope._lexSize.size();
+ assert toScope.depth() <= _breakScope.depth();
toScope.ctrl(toScope.mergeScopes(cur,loc()).peephole());
return toScope;
}
@@ -526,11 +535,11 @@ public class Parser {
// Parse predicate
require("(");
var pred = require(parseAsgn(), ")");
- return parseTrinary(pred,true,"else");
+ return parseTrinary(pred,"else");
}
// Parse a conditional expression, merging results.
- private Node parseTrinary( Node pred, boolean stmt, String fside ) {
+ private Node parseTrinary( Node pred, String fside ) {
pred.keep();
// IfNode takes current control and predicate
@@ -547,7 +556,15 @@ public class Parser {
// Parse the true side
ctrl(ifT.unkeep()); // set ctrl token to ifTrue projection
_scope.addGuards(ifT,pred,false); // Up-cast predicate
- Node lhs = (stmt ? parseStatement() : parseAsgn()).keep(); // Parse true-side
+ // Parse true-side flavor
+ Node lhs = switch( fside ) {
+ case "else" -> parseStatement(); // if( pred ) stmts;
+ case ":" -> parseAsgn(); // pred ? asgn;
+ case "&&" -> parseLogical(); // pred && expr
+ case "||" -> pred; // pred || expr_is_ignored
+ default -> throw Utils.TODO();
+ };
+ lhs.keep();
_scope.removeGuards(ifT);
// See if a one-sided def was made: "if(pred) int x = 1;" and throw.
@@ -563,17 +580,23 @@ public class Parser {
// Up-cast predicate, even if not else clause, because predicate can
// remain true if the true clause exits: `if( !ptr ) return 0; return ptr.fld;`
_scope.addGuards(ifF,pred,true);
- boolean doRHS = match(fside);
- Node rhs = (doRHS
- ? (stmt ? parseStatement() : parseAsgn())
- : con(lhs._type.makeZero())).keep();
+ // Parse false-side flavor
+ boolean doRHS = false; // RHS is optional for if/else and trinary
+ Node rhs = switch( fside ) {
+ case "else" -> (doRHS=match(fside)) ? parseStatement() : con(lhs._type.makeZero());
+ case ":" -> (doRHS=match(fside)) ? parseAsgn() : con(lhs._type.makeZero());
+ case "&&" -> rhs = pred;
+ case "||" -> rhs = parseLogical();
+ default -> throw Utils.TODO();
+ };
+ rhs.keep();
_scope.removeGuards(ifF);
- if( doRHS )
+ if( doRHS || fside.equals("||") )
fScope = _scope;
pred.unkeep();
// Check the trinary widening int/flt
- if( !stmt ) {
+ if( !fside.equals("else") ) {
rhs = widenInt( rhs.unkeep(), lhs._type ).keep();
lhs = widenInt( lhs.unkeep(), rhs._type ).keep();
}
@@ -587,10 +610,10 @@ public class Parser {
// Merge results
RegionNode r = ctrl(tScope.mergeScopes(fScope,loc()));
- Node ret = peep(new PhiNode("",lhs._type.meet(rhs._type),r,lhs.unkeep(),rhs.unkeep()));
+ Node ret = peep(new PhiNode("",lhs._type.meet(rhs._type).glb(false),r,lhs.unkeep(),rhs.unkeep()));
// Immediately fail e.g. `arg ? 7 : ptr`
ParseException err;
- if( !stmt && (err=ret.err()) !=null ) throw err;
+ if( !fside.equals("else") && (err=ret.err()) !=null ) throw err;
r.peephole();
return ret;
}
@@ -619,27 +642,37 @@ public class Parser {
private Node parseAsgn() {
int old = pos();
String name = _lexer.matchId();
+ int old_pre_equals = pos();
// Just a plain expression, no assignment.
// Distinguish `var==expr` from `var=expr`
if( name==null || KEYWORDS.contains(name) || !matchOpx('=','=') )
{ pos(old); return parseExpression(); }
- // Parse assignment expression
- Node expr = parseAsgn();
-
- // Final variable to update
+ // Find variable to update
Var def = _scope.lookup(name);
if( def==null )
throw error("Undefined name '" + name + "'");
+ // If a method var, allow nested blocks and methods, but not fcns or other structs.
+ // Else safety check for accessing fields out of function scope.
+ if( instance(def) ) {
+ pos(old_pre_equals); // setup for `self.name = expr`
+ Node self = _scope.in(_scope.lookup("self")); // Insert self reference
+ return parsePostfixName(self,name);
+ }
+
// TOP fields are for late-initialized fields; these have never
// been written to, and this must be the final write. Other writes
// outside the constructor need to check the final bit.
if( _scope.in(def._idx)._type!=Type.TOP && def._final &&
- // Inside a constructor, final assign is OK, outside nope
+ // Inside an allocation, final assign is OK, outside nope.
+ // The alloc() call added the allocation scope
!_scope.canInit(def) )
throw error("Cannot reassign final '"+name+"'");
+ // Parse assignment expression
+ Node expr = parseAsgn();
+
// Lift expression, based on type
Node lift = liftExpr(expr.keep(), def.type(), true);
// Update
@@ -651,15 +684,17 @@ public class Parser {
// Apply declared access permissions; widen floats and narrow integers.
// Early error if types do not match variable.
private Node liftExpr( Node expr, Type t, boolean isLoad ) {
- if( expr._type instanceof TypeMemPtr tmp && tmp.isFRef() )
+ if( expr._type instanceof TypeMemPtr tmp && TYPES.get(tmp._obj._name).isFRef() )
throw error("Must define forward ref "+tmp._obj._name);
// Read-only access is deep; a fixed binding alone does not remove access.
if( t instanceof TypeMemPtr tmp && tmp.isFinal() ) {
expr = peep(new ReadOnlyNode(expr));
}
+ if( !expr._type.accessISA(t) )
+ throw error("Type " + expr._type.str() + " is not of declared type " + t.str());
// Auto-widen array to i64 (cast ptr to raw int bits)
if( t == TypeInteger.BOT && expr._type instanceof TypeMemPtr tmp && tmp._obj.isAry() )
- expr = peep(new AddNode(peep(new CastNode(t,ctrl(),expr)),con(tmp._obj.aryBase())));
+ expr = peep(new AddNode(peep(new CastNode(t,ctrl(),expr)),off(tmp._obj,"[]")));
// Auto-widen int to float
expr = widenInt( expr, t );
// Auto-narrow wide ints to narrow ints. For loads, emit code to force
@@ -673,7 +708,7 @@ public class Parser {
// Type is sane
if( et!=Type.BOTTOM && (!et.shallowISA(t) || !et.accessISA(t)) )
- throw error("Type " + et.str() + " is not of declared type " + t.str());
+ expr = peep(new CastNode(t,null,expr));
return expr;
}
@@ -707,6 +742,12 @@ public class Parser {
// permissions. Lattice glb includes readonly memory and is too broad here.
private Type inferredType(Type t) {
if( t instanceof TypeMemPtr tmp ) {
+ if( tmp._obj.isAry() ) {
+ Type elem = tmp._obj._fields[1]._t;
+ if( elem instanceof TypeConAry )
+ elem = ((TypeMemPtr)TYPES.get(tmp._obj._name))._obj._fields[1]._t;
+ return typeAry(elem,tmp._obj._fields[1]._final).withAccess(tmp._ro).makeNullable();
+ }
Type base = TYPES.get(tmp._obj._name);
TypeMemPtr ptr = (TypeMemPtr)base;
return ptr.withAccess(tmp._ro).makeNullable();
@@ -732,8 +773,10 @@ public class Parser {
Lexer loc = loc();
String name = requireId();
// Optional initializing expression follows
+ boolean fld_final = false; // Fixed field independent of pointee access
+ boolean initialized = match("=");
Node expr;
- if( match("=") ) {
+ if( initialized ) {
expr = isExternDecl()
? externDecl(name,t)
: parseAsgn();
@@ -767,6 +810,10 @@ public class Parser {
// the constructor and that's when we'll discover the type.
case Type tt -> { assert tt==Type.BOTTOM; yield con(tt); }
};
+ // Nullable fields are set in the constructor, but remain shallow final.
+ // e.g. final pointer to a r/w array
+ if( t instanceof TypeNil tn && !tn.nullable() && xfinal )
+ fld_final = true;
}
// Lift expression, based on type
@@ -777,8 +824,9 @@ public class Parser {
if( !lift._type.isa(t) )
lift = peep(new CastNode(t,null,lift));
// Define a new name
- if( !_scope.define(name,t,xfinal,lift, loc) )
+ if( !_scope.define(name,t,xfinal || fld_final,lift, loc) )
throw error("Redefining name '" + name + "'", loc);
+ _scope.lookup(name)._init = initialized;
return lift;
}
@@ -797,30 +845,46 @@ public class Parser {
String typeName = requireId();
if( old!=null ) typeName = old+"."+typeName;
_nestedType = typeName;
+ // New declared or forward-ref type
+ TypeStruct ts = TYPES.get(typeName) instanceof TypeMemPtr tmp ? tmp._obj : TypeStruct.open(typeName);
+ TYPES.put(typeName,TypeMemPtr.make(ts));
// A Block scope parse, and inspect the scope afterward for fields.
- _scope.push(ScopeNode.Kind.Constructor);
+ _scope.push(new Kind.Define(TypeMemPtr.make(ts)));
require("{");
- while (!peek('}') && !_lexer.isEOF())
+ int lexlen = _scope._kinds.last()._lexSize, nvar=lexlen;
+ while (!peek('}') && !_lexer.isEOF()) {
parseStatement().isKill();
+ while( nvar < _scope._vars._len ) {
+ Var v = _scope.var(nvar++);
+ if( !v.isFRef() ) {
+ ts = ts.add( Field.make( v._name, v.type(), _code.getALIAS(), v._final, v._final && v._init && _scope.in( v._idx )._type != Type.TOP ) );
+ TYPES.put( typeName, TypeMemPtr.make( ts ) );
+ }
+ }
+ }
- // Grab the declarations and build fields and a Struct
- int lexlen = _scope._lexSize.last();
+ // Count struct declarations
int varlen = _scope._vars._len;
- StructNode s = new StructNode();
- Ary<Field> fs = new Ary<>(Field.class);
- for( int i=lexlen; i<varlen; i++ ) {
- Var v = _scope.var(i);
- if( v.isFRef() ) continue; // Promote to outer scope, not defined here
- s.addDef(_scope.in(i)); // Keep the program code to generate instance
- fs.push(Field.make(v._name,v.type(),_code.getALIAS(),v._final));
- }
+ ts = ts.close();
+ TYPES.put(typeName,TypeMemPtr.make(ts));
+
+ // Generate an initializing struct
+ StructNode s = new StructNode(ts);
+ for( int i=lexlen; i<varlen; i++ )
+ if( !_scope.var(i).isFRef() ) // Promote to outer scope, not defined here
+ s.addDef(_scope.in(i)); // Keep the program code to generate instance
+
// Save instance default constructor
- TypeStruct ts = s._ts = TypeStruct.make(typeName, fs.asAry());
s.setType(s.compute()); // Do not call peephole, in case the entire struct is a big plain constant
- TYPES.put(typeName, TypeMemPtr.make(ts));
INITS.put(typeName,s.keep());
+ // Having finalized the struct, upgrade all the 'self' pointers
+ for( Node n : s._inputs )
+ if( n._type instanceof TypeFunPtr tfp && _code.link(tfp) instanceof FunNode fun )
+ _code.addAll( fun._outputs );
+
+
// Done with struct/block scope
_nestedType = old;
require("}");
@@ -867,7 +931,7 @@ public class Parser {
}
// Still no type found? Assume forward reference
- Type t1 = t0 == null ? TypeMemPtr.make(TypeStruct.makeFRef(tname)) :t0; // Null: assume a forward ref type
+ Type t1 = t0 == null ? TypeMemPtr.make(TypeStruct.open(tname)) :t0; // Null: assume a forward ref type
// A qualifier before the name applies to the struct reference, not
// to an enclosing array. Arrays have their own [] / [~] qualifier.
if( t1 instanceof TypeMemPtr tmp ) {
@@ -884,10 +948,9 @@ public class Parser {
if( tmp.nullable() ) throw error("Type "+t2+" already allows null");
t2 = tmp.makeNullable();
} else if( match("[~]") ) {
- TypeMemPtr ary = typeAry(t2);
- t2 = ary.makeFrom(ary._obj.makeRO()).makeRO();
+ t2 = typeAry(t2,true).makeRO();
} else if( match("[]") ) {
- t2 = typeAry(t2);
+ t2 = typeAry(t2,false);
} else
break;
}
@@ -909,25 +972,17 @@ public class Parser {
}
// Make an array type of t
- private TypeMemPtr typeAry( Type t ) {
+ private TypeMemPtr typeAry( Type t, boolean efinal ) {
if( t instanceof TypeMemPtr tmp && tmp.notNull() )
throw error("Arrays of reference types must always be nullable");
- String tname = "["+t.str()+"]";
- Type ta = TYPES.get(tname);
- if( ta != null ) {
- TypeStruct ary = ((TypeMemPtr)ta)._obj;
- // Keep the original shallow struct reference. Resolving a recursive
- // struct must not expand its array element type on each lookup.
- 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(tname,TypeInteger.U32,ary._fields[0]._alias,t,ary._fields[1]._alias));
- }
- TypeStruct ts = TypeStruct.makeAry(tname,TypeInteger.U32,_code.getALIAS(),t,_code.getALIAS());
- assert ts.str().equals(tname);
- TypeMemPtr tary = TypeMemPtr.make(ts);
- TYPES.put(tname,tary);
- return tary;
+ String tname = "[]"+t.str();
+ TypeMemPtr ta = (TypeMemPtr)TYPES.get(tname);
+ if( ta==null )
+ // Remember final version
+ TYPES.put(tname, ta = TypeMemPtr.make( TypeStruct.makeAry(tname,TypeInteger.U32,_code.getALIAS(),t,_code.getALIAS(),true) ));
+ // Reuse aliases, but preserve this occurrence's element permissions.
+ TypeStruct ary = ta._obj;
+ return TypeMemPtr.make(TypeStruct.makeAry(tname,TypeInteger.U32,ary._fields[0]._alias,t,ary._fields[1]._alias,efinal));
}
// A function type is `{ type... -> type }` or `{ type }`.
@@ -937,7 +992,7 @@ public class Parser {
Type t0 = type(); // Either return or first arg
if( t0==null ) return posT(old); // Not a function
if( match("}") ) // No-arg function { -> type }
- return TypeFunPtr.make(match("?"),TypeTuple.BOT,t0);
+ return TypeFunPtr.make(match("?"),false,TypeFunPtr.TEMPTY,t0);
Ary<Type> ts = new Ary<>(Type.class);
ts.push(t0); // First argument
while( true ) {
@@ -945,7 +1000,7 @@ public class Parser {
Type ret = type();
if( ret==null || !match("}") )
return posT(old); // Not a function
- return TypeFunPtr.make(match("?"),TypeTuple.make(ts.asAry()),ret);
+ return TypeFunPtr.make(match("?"),false,ts.asAry(),ret);
}
Type t1 = type();
if( t1==null ) return posT(old); // Not a function
@@ -970,8 +1025,8 @@ public class Parser {
* @return an expression {@link Node}, never {@code null}
*/
private Node parseExpression() {
- Node expr = parseBitwise();
- return match("?") ? parseTrinary(expr,false,":") : expr;
+ Node expr = parseLogical();
+ return match("?") ? parseTrinary(expr,":") : expr;
}
/**
@@ -982,13 +1037,25 @@ public class Parser {
* </pre>
* @return a bitwise expression {@link Node}, never {@code null}
*/
+
+ private Node parseLogical() {
+ Node lhs = parseBitwise();
+ while (true) {
+ if( false ) ;
+ else if( match("&&") ) lhs = parseTrinary(lhs, "&&");
+ else if( match("||") ) lhs = parseTrinary(lhs, "||");
+ else break;
+ }
+ return lhs;
+ }
+
private Node parseBitwise() {
Node lhs = parseComparison();
while( true ) {
if( false ) ;
- else if( match("&") ) lhs = new AndNode(loc(),lhs,null);
- else if( match("|") ) lhs = new OrNode(loc(),lhs,null);
- else if( match("^") ) lhs = new XorNode(loc(),lhs,null);
+ else if( matchOp('&') ) lhs = new AndNode(loc(),lhs,null);
+ else if( matchOp('|') ) lhs = new OrNode(loc(),lhs,null);
+ else if( match ("^") ) lhs = new XorNode(loc(),lhs,null);
else break;
lhs.setDef(2,parseComparison());
lhs = peep(lhs);
@@ -1124,14 +1191,17 @@ public class Parser {
}
if (match("-")) return peep(new MinusNode(parseUnary()).widen());
if (match("!")) return peep(new NotNode(parseUnary()));
- return parsePostfix(parsePrimary());
+ // Else be a primary expression
+ return parsePrimary();
}
/**
* Parse a primary expression:
*
* <pre>
- * primaryExpr : integerLiteral | true | false | null | new Type | '(' expression ')' | Id['++','--']
+ * primaryExpr : integerLiteral | "string" | 'char' | true | false | null |
+ * new Type | '(' expression ')' | Id['++','--'] |
+ * [static.]* Id
* </pre>
* @return a primary {@link Node}, never {@code null}
*/
@@ -1142,43 +1212,99 @@ public class Parser {
if( matchx("false") ) return ZERO;
if( matchx("null" ) ) return NIL;
if( match ("'" ) ) return parseChar();
- if( match ("(" ) ) return require(parseAsgn(), ")");
- if( matchx("new" ) ) return alloc();
- if( match ("{" ) ) return require(func(),"}");
+ if( match ("(" ) ) return parsePostfix(require(parseAsgn(), ")"));
+ if( matchx("new" ) ) return parsePostfix(alloc());
+ if( match ("{" ) ) return parsePostfix(require(func(),"}"));
// Parse static variable lookup: `type.fld` in the type namespace
+ // CNC: not a full type parse, just a typename parse
+ int pos = pos();
Type t = type();
- if( t!=null && match(".") ) {
- String fld = requireId();
- TypeMemPtr tmp = ((TypeMemPtr)t);
- String tname = tmp._obj._name;
- StructNode init = INITS.get(tname);
- int idx = tmp._obj.find(fld);
- if( idx == -1 )
- throw error("Accessing unknown STATIC field");
- return init.in(idx);
+ if( t!=null ) {
+ if( peek('.') )
+ // A namespace lookup is not a reference declaration.
+ return parsePostfix(con(TYPES.get(((TypeMemPtr)t)._obj._name)));
+ //pos(pos); // TODO: not a `type.fld`
+ //return null;
+ throw Utils.TODO();
}
// Expect an identifier now
- Var n = requireLookupId();
- Node rvalue = _scope.in(n);
+
+ // - if null, make fref, normal var handling
+ // - if final constant, normal var handling
+ // - if method var, scan skipping blocks & methods, must hit correct struct no fcns
+ // - if normal var, scan skipping blocks, allowing last fcn
+ // - NO fcns nested in methods, NO methods nested in fcns
+
+ String id = _lexer.matchId();
+ if( id == null || KEYWORDS.contains(id) )
+ throw errorSyntax("an identifier or expression");
+ Var var = _scope.lookup(id);
+
+ if( var==null ) {
+ // If missing, assume a forward reference
+ _scope.define(id, FRefNode.FREF_TYPE, true, XCTRL, loc());
+ var = _scope.lookup(id);
+ }
+
+ // If a method var, allow nested blocks and methods, but not fcns or other structs.
+ // Else safety check for accessing fields out of function scope.
+ if( instance(var) ) {
+ Node self = _scope.in(_scope.lookup("self")); // Insert self reference
+ return parsePostfixName(self,id);
+ }
+
+ // Load local value
+ Node rvalue = _scope.in(var);
if( rvalue._type == Type.BOTTOM )
- if( rvalue instanceof FRefNode ) return rvalue;
- else throw error("Cannot read uninitialized field '"+n._name+"'");
+ if( rvalue instanceof FRefNode )
+ return parsePostfix(rvalue);
+ else throw error("Cannot read uninitialized field '"+id+"'");
+
// Check for assign-update, x += e0;
char ch = _lexer.matchOperAssign();
- if( ch==0 ) return rvalue;
- Node op = opAssign(ch,rvalue,n._final,n.type(),n._name);
- _scope.update(n,op);
+ if( ch==0 ) // Normal primary, check for postfix updates
+ return parsePostfix(rvalue);
+ // Assign-update direct into Scope
+ if( var._final )
+ throw error("Cannot reassign final '"+var._name+"'");
+ Node op = opAssign(ch,rvalue, var.type() );
+ _scope.update(var,op);
return postfix(ch) ? rvalue.unkeep() : op;
}
+ // true if instance variable lookup (var is in any number of nested blocks,
+ // then an instance function then inside a struct declaration).
+ private boolean instance(Var var ) {
+ // If a method var, allow nested blocks and methods, but not fcns or other structs
+ Kind kind = _scope.kind(var);
+ if( kind instanceof Kind.Define ) {
+ for( int i = _scope.depth()-1; i>=0 && var._idx < _scope._kinds.at(i)._lexSize; i-- ) {
+ Kind xkind = _scope._kinds.at(i);
+ if( xkind instanceof Kind.Block ) continue;
+ if( xkind instanceof Kind.Func ) {
+ if( _scope._kinds.at(i-1)==kind )
+ return true; // Instance variable: lookup in a method in a decl
+ }
+ throw Utils.TODO();
+ }
+ } else {
+ // If a normal var, allow any nested blocks, and a last fcn -
+ // but not functions further out (which involves capture).
+ Node def = _scope.in(var._idx);
+ for( int i = _scope.depth()-1; i>=0 && var._idx < _scope._kinds.at(i)._lexSize; i-- )
+ if( _scope._kinds.at(i) instanceof Kind.Func &&
+ !(def instanceof FRefNode) && !(var._final && def._type.isConstant()) )
+ throw error("Variable '"+var._name+"' is out of function scope and must be a final constant");
+ }
+ return false;
+ }
+
// Check for assign-update, "x += e0".
// Returns "x + e0"; the caller assigns value.
// if ch is postfix, then lhs is kept and caller will unkeep and return it.
- private Node opAssign(char ch, Node lhs, boolean xfinal, Type t, String name ) {
- if( xfinal )
- throw error("Cannot reassign final '"+name+"'");
+ private Node opAssign( char ch, Node lhs, Type t ) {
// RHS of the update.
lhs.keep(); // Alive across parseAsgn
Node rhs =
@@ -1211,30 +1337,6 @@ public class Parser {
}
- // Expect an ID here. If not found, assume a forward reference function
- Var requireLookupId() {
- // Try in the normal variable namespace
- String id = _lexer.matchId();
- if( id == null || KEYWORDS.contains(id) )
- throw errorSyntax("an identifier or expression");
- Var n = _scope.lookup(id);
- // If missing, assume a forward reference
- if( n==null ) {
- _scope.define(id, FRefNode.FREF_TYPE, true, XCTRL, loc());
- n = _scope.lookup(id);
- } else {
- // Lookup worked on an out-of-function value.
- if( _scope.outOfFunction(n) ) {
- // No closures, so this has to be a final constant (which
- // includes forward refs)
- Node def = _scope.in(n._idx);
- if( !(def instanceof FRefNode) && !(n._final && def._type.isConstant()) )
- throw error("Variable '"+n._name+"' is out of function scope and must be a final constant");
- }
- }
- return n;
- }
-
/**
Parse an allocation
*/
@@ -1249,7 +1351,7 @@ public class Parser {
if( !(len._type instanceof TypeInteger) )
throw error("Cannot allocate an array with length "+len._type);
require("]");
- TypeMemPtr tmp = typeAry(t);
+ TypeMemPtr tmp = typeAry(t,false);
return newArray(tmp._obj,len);
}
@@ -1268,19 +1370,23 @@ public class Parser {
if( hasConstructor ) {
idx = _scope.nIns();
// Push a scope, and pre-assign all struct fields.
- _scope.push(ScopeNode.Kind.Block);
+ _scope.push(new Kind.Block());
Lexer loc = loc();
for( int i=0; i<fs.length; i++ )
- _scope.define(fs[i]._fname, fs[i]._type, fs[i]._final, s.in(i)._type==Type.TOP ? con(Type.BOTTOM) : s.in(i), loc);
+ // An initial TOP means the field needs to be initialized. We
+ // store a BOT initially; any partial init will fall to BOT
+ // (merge BOT and the partial) and be obviously only a partial
+ // init. To be initialized the field needs a full clobber.
+ _scope.define(fs[i]._fname, fs[i]._t, fs[i]._final, s.in(i)._type==Type.TOP ? con(Type.BOTTOM) : s.in(i), loc);
// Parse the constructor body
- require(parseBlock(ScopeNode.Kind.Constructor),"}");
+ require(parseBlock(new Kind.Alloc(tmp)),"}");
init = _scope._inputs;
}
// Check that all fields are initialized
for( int i=idx; i<init.size(); i++ )
if( init.at(i)._type == Type.TOP || init.at(i)._type == Type.BOTTOM )
throw error("'"+tmp._obj._name+"' is not fully initialized, field '" + fs[i-idx]._fname + "' needs to be set in a constructor");
- Node ptr = newStruct(tmp._obj, con(tmp._obj.offset(fs.length)), idx, init );
+ Node ptr = newStruct(tmp._obj, off(tmp._obj, " len"), idx, init );
if( hasConstructor )
_scope.pop();
return ptr;
@@ -1298,7 +1404,7 @@ public class Parser {
throw error("Unknown struct type '" + obj._name + "'");
Node prior = mem().keep();
MemMergeNode input = new MemMergeNode((Node)null);
- for( Field f : fs ) input.alias(f._alias,prior);
+ for( Field f : fs ) if( !f._one ) input.alias(f._alias,prior);
// Allocation zeroes its fields; nonzero initializers become explicit Stores.
Node[] ns = new Node[3];
ns[0] = ctrl();
@@ -1308,16 +1414,16 @@ public class Parser {
Node ptr = new ProjNode(nnn,0,obj._name).peephole().keep();
Node out = new ProjNode(nnn,1,"$mem").peephole().keep();
MemMergeNode after = new MemMergeNode(prior);
- for( Field f : fs ) after.alias(f._alias,out);
+ for( Field f : fs ) if( !f._one ) after.alias(f._alias,out);
mem(after.peephole());
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() ) {
+ if( !fs[i]._one && 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();
+ Node st = new StoreNode(loc(),f._fname,f._alias,f._t,mem(),ptr,off(obj,f._fname),val,true).peephole();
mem(new MemMergeNode(mem(),f._alias,st).peephole());
}
}
@@ -1326,14 +1432,14 @@ public class Parser {
private static final Ary<Node> ALTMP = new Ary<>(Node.class);
private Node newArray(TypeStruct ary, Node len) {
- int base = ary.aryBase ();
+ ConFldOffNode base = off(ary, "[]");
int scale= ary.aryScale();
- Node size = peep(new AddNode(con(base),peep(new ShlNode(null,len.keep(),con(scale)))));
- ALTMP.clear(); ALTMP.add(len.unkeep()); ALTMP.add(con(ary._fields[1]._type.makeZero()));
+ Node size = peep(new AddNode(base,peep(new ShlNode(null,len.keep(),con(scale)))));
+ ALTMP.clear(); ALTMP.add(len.unkeep()); ALTMP.add(con(ary._fields[1]._t.makeZero()));
return newStruct(ary,size,0,ALTMP);
}
private Node newString(String s) {
- TypeMemPtr tmp = typeAry(TypeInteger.U8);
+ TypeMemPtr tmp = typeAry(TypeInteger.U8,true);
int lenAlias = tmp._obj.field("#" )._alias;
int elemAlias = tmp._obj.field("[]")._alias;
TypeConAryB con = TypeConAryB.make(s);
@@ -1341,7 +1447,8 @@ public class Parser {
// Make a TMP, not-null (byte)2, singleton (true) (requires text
// strings are hash-interned), with a TypeStruct having a constant
// array body.
- TypeMemPtr str = TypeMemPtr.make((byte)2,TypeStruct.makeAry("[u8]", TypeInteger.U32, lenAlias, elem, elemAlias, true, con),true);
+ TypeMemPtr str = TypeMemPtr.make((byte)2,TypeStruct.makeAry("[]u8", TypeInteger.constant(s.length()), lenAlias, con, elemAlias, true),true);
+ assert str.isConstant();
return con(str.makeRO());
}
@@ -1377,9 +1484,24 @@ public class Parser {
if( match(".") ) name = requireId();
else if( match("#") ) name = "#";
else if( match("[") ) name = "[]";
- else if( match("(") ) return parsePostfix(require(functionCall(expr),")"));
+ else if( match("(") ) return parsePostfix(require(functionCall(expr,null),")"));
else return expr; // No postfix
+ return parsePostfixName(expr,name);
+ }
+
+ /**
+ * Parse postfix expression; this can be a field expression, an array
+ * lookup or a postfix operator like '#'
+ *
+ * <pre>
+ * expr ['++' | '--' ] // Postfix unary write operator
+ * expr [ [op]= expr ] // Field reference
+ * expr ('[' expr ']')* [ [op]= expr ] // Array reference
+ * </pre>
+ */
+ private Node parsePostfixName(Node expr, String name) {
+
if( expr._type==Type.NIL )
throw error("Accessing unknown field '" + name + "' from 'null'");
@@ -1388,14 +1510,17 @@ public class Parser {
throw error( "Expected "+(name=="#" || name=="[]" ? "array" : "reference")+" but found " + expr._type.str() );
// Find the field from the Type. Lookup in the base object field names.
+ TypeMemPtr ptr2 = (TypeMemPtr)TYPES.get(ptr._obj._name);
+ if( !ptr._obj.isAry() )
+ ptr = ptr.makeFrom((TypeStruct)ptr._obj.join(ptr2._obj)); // Resolve the shape, retaining access.
TypeStruct base = ptr._obj;
- assert !base.isFRef();
int fidx = base.find(name);
- if( fidx == -1 ) throw error("Accessing unknown field '" + name + "' from '" + ptr.str() + "'");
+ if( fidx == -1 )
+ throw error("Accessing unknown field '" + name + "' from '" + ptr.str() + "'");
// Get field type and layout offset from base type and field index fidx
Field f = base._fields[fidx]; // Field from field index
- Type tf = f._type;
+ Type tf = f._t;
if( tf instanceof TypeMemPtr ftmp && ftmp.isFRef() )
tf = ftmp.makeFrom(((TypeMemPtr)(TYPES.get(ftmp._obj._name)))._obj);
@@ -1403,9 +1528,9 @@ public class Parser {
// Field offset; fixed for structs, computed for arrays
Node off = (name.equals("[]") // If field is an array body
// Array index math
- ? peep(new AddNode(con(base.aryBase()),peep(new ShlNode(null,require(parseAsgn(),"]"),con(base.aryScale())))))
+ ? peep(new AddNode(off(base,"[]"),peep(new ShlNode(null,require(parseAsgn(),"]"),con(base.aryScale())))))
// Struct field offsets are hardwired
- : con(base.offset(fidx))).keep();
+ : off(base,name)).keep();
// Disambiguate "obj.fld==x" boolean test from "obj.fld=x" field assignment
if( matchOpx('=','=') ) {
@@ -1416,7 +1541,10 @@ public class Parser {
return val.unkeep(); // "obj.a = expr" returns the expression while updating memory
}
- Node load = new LoadNode(loc(),name, f._alias, tf, mem(), expr, off);
+ // Load field
+ Node load = f._one && INITS.containsKey( base._name )
+ ? INITS.get(base._name).in(base.find(name))
+ : new LoadNode(loc(),name, f._alias, tf, mem(), expr, off);
// 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.
@@ -1426,12 +1554,29 @@ public class Parser {
// Check for assign-update, "ptr.fld += expr" or "ary[idx]++"
char ch = _lexer.matchOperAssign();
if( ch!=0 ) {
- Node op = opAssign(ch,load,f._final,tf,name);
- store(name,f._alias,tf,expr.unkeep(),off,op,false,base.isAry() ? ctrl() : null);
+ Node op = opAssign(ch,load,tf);
+ store(name,f._alias,tf,expr.unkeep(),off.unkeep(),op,false,base.isAry() ? ctrl() : null);
load = postfix(ch) ? load.unkeep() : op;
// And use the original loaded value as the result
- } else expr.unkill();
- off.unkill();
+ return load;
+ //throw Utils.TODO(); // NO POSTFIX AFTER STORE, JUST RETURN
+ } else {
+ off.unkill();
+ // Method call: Loaded a function from a named field, AND the base
+ // field type is final.
+ if( load._type instanceof TypeFunPtr && name!="[]" &&
+ !(load instanceof ExternNode) &&
+ ((TypeMemPtr)TYPES.get(base._name))._obj.field(name)._final &&
+ // And calling a function
+ match("(") ) {
+ return parsePostfix(require(functionCall(load,expr),")"));
+ }
+
+ // Drop expr, keeping load alive
+ load.keep();
+ expr.unkill();
+ load.unkeep();
+ }
return parsePostfix(load);
}
@@ -1470,6 +1615,17 @@ public class Parser {
Ary<Type> ts = new Ary<>(Type.class);
Ary<String> ids = new Ary<>(String.class);
java.util.BitSet finals = new java.util.BitSet();
+
+ // Defined in constructor? Add `self` argument. "static" calls still
+ // add a `self` they just ignore it.
+ if( _scope._kinds.last() instanceof Kind.Define define ) {
+ // Upgrade defined struct to latest fields
+ TypeMemPtr tmp = (TypeMemPtr)TYPES.get(define._tmp._obj._name);
+ ts .push(tmp);
+ ids.push("self");
+ }
+
+ // Parse other arguments
_lexer.skipWhiteSpace();
Lexer loc = loc(); // First argument location
while( true ) {
@@ -1483,7 +1639,7 @@ public class Parser {
}
require("->");
// Make a concrete function type, with a fidx
- TypeFunPtr tfp = _code.makeFun(TypeTuple.make(ts.asAry()),Type.BOTTOM);
+ TypeFunPtr tfp = _code.makeFun(TypeFunPtr.make(false,false,ts.asAry(),Type.BOTTOM));
ReturnNode ret = parseFunctionBody(tfp,loc,finals,ids.asAry());
return new FunPtrNode(ret._fun.sig(),ret).peephole();
}
@@ -1494,16 +1650,18 @@ public class Parser {
* ( arg* )
* </pre>
*/
- private Node functionCall(Node expr) {
- if( expr._type == Type.NIL )
+ private Node functionCall(Node fcn, Node self) {
+ if( fcn._type == Type.NIL )
throw error("Calling a null function pointer");
- if( !(expr instanceof FRefNode) && !expr._type.isa(TypeFunPtr.BOT) )
- throw error("Expected a function but got "+expr._type.glb(false).str());
- expr.keep(); // Keep while parsing args
+ if( !(fcn instanceof FRefNode) && !fcn._type.isa(TypeFunPtr.BOT) )
+ throw error("Expected a function but got "+fcn._type.glb(false).str());
+ fcn.keep(); // Keep while parsing args
Ary<Node> args = new Ary<>( Node.class );
args.push(null); // Space for ctrl,mem
args.push(null);
+ if( self != null ) // Method call has a self
+ args.push(self);
while( !peek(')') ) {
Node arg = parseAsgn();
if( arg==null ) break;
@@ -1513,7 +1671,7 @@ public class Parser {
// Control & memory after parsing args
args.set(0,ctrl().keep());
args.set(1,_scope.mem().keep());
- args.push(expr); // Function pointer
+ args.push(fcn); // Function pointer
// Unkeep them all
for( Node arg : args )
arg.unkeep();
@@ -1549,8 +1707,8 @@ public class Parser {
}
// External linked constant
private Node externDecl( String ex, Type t ) {
- if( t instanceof TypeFunPtr tfp ) // Generic TFP from type parse
- t = _code.makeFun(tfp._sig,tfp._ret); // Get a FIDX, becomes a constant
+ if( t instanceof TypeFunPtr tfp ) // Generic TFP from type parse
+ t = _code.makeFun(tfp); // Get a FIDX, becomes a constant
return new ExternNode(t,ex).peephole();
}
@@ -1565,6 +1723,7 @@ public class Parser {
private ConstantNode parseLiteral() { return con(_lexer.parseNumber()); }
public static Node con( long con ) { return con==0 ? ZERO : con(TypeInteger.constant(con)); }
public static ConstantNode con( Type t ) { return (ConstantNode)new ConstantNode(t).peephole(); }
+ public static ConFldOffNode off( TypeStruct base, String fname ) { return (ConFldOffNode)(new ConFldOffNode(base._name,fname).peephole()); }
public Node peep( Node n ) {
// Peephole, then improve with lexically scoped guards
return _scope.upcastGuard(n.peephole());
@@ -1621,6 +1780,7 @@ public class Parser {
private boolean match (String syntax) { return _lexer.match (syntax); }
// Match must be "exact", not be followed by more id letters
private boolean matchx(String syntax) { return _lexer.matchx(syntax); }
+ private boolean matchOp(char c0 ) { return _lexer.matchOp(c0); }
private boolean matchOpx(char c0, char c1) { return _lexer.matchOpx(c0,c1); }
// Return true and do NOT skip if 'ch' is next
private boolean peek(char ch) { return _lexer.peek(ch); }
@@ -1796,6 +1956,16 @@ public class Parser {
_position -= syntax.length();
return false;
}
+ // Match this char, and the next char must be different.
+ // Handles '&&' vs '&'
+ boolean matchOp( char c0 ) {
+ skipWhiteSpace();
+ if( _position+1 >= _input.length || _input[_position]!=c0 || _input[_position+1]==c0 )
+ return false;
+ inc();
+ return true;
+ }
+ // Match these two characters in a row
boolean matchOpx(char c0, char c1) {
skipWhiteSpace();
if( _position+1 >= _input.length || _input[_position]!=c0 || _input[_position+1]==c1 )
@@ -1923,4 +2093,5 @@ public class Parser {
//String msg = "src:"+_line_number+":"+(_position-_line_start)+" "+errorMessage;
ParseException( String msg, Lexer loc ) { super(msg); _loc = loc; }
}
+
}
diff --git a/src/main/java/com/seaofnodes/simple/Var.java b/src/main/java/com/seaofnodes/simple/Var.java
index 14295395..e13b85c5 100644
--- a/src/main/java/com/seaofnodes/simple/Var.java
+++ b/src/main/java/com/seaofnodes/simple/Var.java
@@ -13,6 +13,7 @@ public class Var {
// promotions.
public int _idx; // index in containing scope
private Type _type; // Declared type
+ public boolean _init; // Explicit declaration initializer
public boolean _final; // Final field
public boolean _fref; // Forward ref
public Parser.Lexer _loc; // Source location
diff --git a/src/main/java/com/seaofnodes/simple/codegen/Coalesce.java b/src/main/java/com/seaofnodes/simple/codegen/Coalesce.java
index e2fac779..689ceceb 100644
--- a/src/main/java/com/seaofnodes/simple/codegen/Coalesce.java
+++ b/src/main/java/com/seaofnodes/simple/codegen/Coalesce.java
@@ -1,8 +1,8 @@
package com.seaofnodes.simple.codegen;
-import com.seaofnodes.simple.Ary;
-import com.seaofnodes.simple.Utils;
import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.util.Ary;
+import com.seaofnodes.simple.util.Utils;
import java.util.BitSet;
import java.util.IdentityHashMap;
diff --git a/src/main/java/com/seaofnodes/simple/codegen/CodeGen.java b/src/main/java/com/seaofnodes/simple/codegen/CodeGen.java
index 8a3d6322..9bf1aa61 100644
--- a/src/main/java/com/seaofnodes/simple/codegen/CodeGen.java
+++ b/src/main/java/com/seaofnodes/simple/codegen/CodeGen.java
@@ -2,14 +2,15 @@ package com.seaofnodes.simple.codegen;
import com.seaofnodes.graph.GraphObserver;
-import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.IterPeeps;
+import com.seaofnodes.simple.Parser;
import com.seaofnodes.simple.node.*;
import com.seaofnodes.simple.print.*;
import com.seaofnodes.simple.type.*;
+import com.seaofnodes.simple.util.Ary;
+import com.seaofnodes.simple.util.SB;
import java.io.IOException;
-import java.util.BitSet;
-import java.util.HashMap;
-import java.util.IdentityHashMap;
+import java.util.*;
@SuppressWarnings("unchecked")
public class CodeGen {
@@ -40,9 +41,13 @@ public class CodeGen {
public final TypeInteger _arg;
// ---------------------------
- public CodeGen( String src ) { this(src, TypeInteger.BOT, 123L ); }
- public CodeGen( String src, TypeInteger arg, long workListSeed ) {
+ public CodeGen( String src ) { this(src, TypeInteger.BOT, 123L, true ); }
+ public CodeGen( String src, TypeInteger arg) { this(src, arg==null ? TypeInteger.BOT : arg, 123L, true ); }
+
+ public CodeGen( String src, TypeInteger arg, long workListSeed, boolean reset ) {
CODE = this;
+ if( reset ) Type.reset();
+ _main = makeFun(TypeFunPtr.MAIN);
_phase = null;
_callingConv = null;
_start = new StartNode(arg);
@@ -112,18 +117,15 @@ public class CodeGen {
// 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
+ //
+ private int _fidx =0;
+ public TypeFunPtr makeFun( TypeFunPtr fun ) {
+ int fidx = _fidx++;
assert fidx<64; // TODO: need a larger FIDX space
- return TypeFunPtr.make((byte)2,sig,ret, 1L<<fidx );
+ return fun.makeFrom(fidx);
}
// Signature for MAIN
- public final TypeFunPtr _main = makeFun(TypeTuple.MAIN,Type.BOTTOM);
+ public final TypeFunPtr _main;
// Reverse from a constant function pointer to the IR function being called
public FunNode link( TypeFunPtr tfp ) {
assert tfp.isConstant();
@@ -179,24 +181,44 @@ public class CodeGen {
// Pessimistic peephole optimization on a worklist
_iter.iterate(this);
+ // OPTIMISTIC PASS GOES HERE
+
// Not really a true optimistic pass, but look for unlinked functions.
// This can be removed, which may trigger another round of pessimistic.
// This is the point where we flip from a virtual Call Graph (any call
// can call any function) to having a correct (but conservative) CG.
+ int progress = 0;
+ while( progress != _start.nOuts() ) {
+ progress = _start.nOuts();
+ FunNode main = link(_main);
+ for( int i=0; i<_start.nOuts(); i++ ) {
+ Node use = _start.out(i);
+ if( use instanceof FunNode fun &&
+ fun.nIns()==2 && fun.in(1)==_start && fun != main && !fun.hasPtr() &&
+ (fun._name==null || fun._name.startsWith("sys.")) ) {
+ add(fun).setDef(1,Parser.XCTRL);
+ addAll(fun._outputs);
+ i--;
+ }
+ }
+ _iter.iterate(this);
+ }
- FunNode main = link(_main);
+ // Freeze field sizes; do struct layouts; convert field offsets into
+ // constants.
for( int i=0; i<_start.nOuts(); i++ ) {
Node use = _start.out(i);
- if( use instanceof FunNode fun &&
- fun.nIns()==2 && fun.in(1)==_start && fun != main && !fun.hasPtr() &&
- (fun._name==null || fun._name.startsWith("sys.")) ) {
- add(fun).setDef(1,Parser.XCTRL);
- addAll(fun._outputs);
+ if( use instanceof ConFldOffNode off ) {
+ TypeMemPtr tmp = (TypeMemPtr) Parser.TYPES.get(off._name);
+ off.subsume( off.asOffset(tmp._obj) );
i--;
}
}
_iter.iterate(this);
+ // To help with testing, sort StopNode returns by NID
+ Arrays.sort(_stop._inputs._es,0,_stop.nIns(),(x,y) -> x._nid - y._nid );
+
_tOpto = (int)(System.currentTimeMillis() - t0);
// TODO:
@@ -206,7 +228,7 @@ public class CodeGen {
if( _obs != null ) _obs.phase("Opto");
return this;
}
- public <N extends Node> N add( N n ) { return (N)_iter.add(n); }
+ public <N extends Node> N add( N n ) { return _iter.add(n); }
public void addAll( Ary<Node> ary ) { _iter.addAll(ary); }
diff --git a/src/main/java/com/seaofnodes/simple/codegen/ElfFile.java b/src/main/java/com/seaofnodes/simple/codegen/ElfFile.java
index 88af3f27..b6267600 100644
--- a/src/main/java/com/seaofnodes/simple/codegen/ElfFile.java
+++ b/src/main/java/com/seaofnodes/simple/codegen/ElfFile.java
@@ -1,9 +1,10 @@
package com.seaofnodes.simple.codegen;
import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.codegen.Encoding.BAOS;
import com.seaofnodes.simple.node.*;
import com.seaofnodes.simple.type.*;
-import com.seaofnodes.simple.codegen.Encoding.BAOS;
+import com.seaofnodes.simple.util.Ary;
import java.io.*;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
diff --git a/src/main/java/com/seaofnodes/simple/codegen/Encoding.java b/src/main/java/com/seaofnodes/simple/codegen/Encoding.java
index aad19090..5099c02a 100644
--- a/src/main/java/com/seaofnodes/simple/codegen/Encoding.java
+++ b/src/main/java/com/seaofnodes/simple/codegen/Encoding.java
@@ -1,11 +1,11 @@
package com.seaofnodes.simple.codegen;
-import com.seaofnodes.simple.Ary;
-import com.seaofnodes.simple.SB;
-import com.seaofnodes.simple.Utils;
import com.seaofnodes.simple.node.*;
import com.seaofnodes.simple.print.IRPrinter;
import com.seaofnodes.simple.type.*;
+import com.seaofnodes.simple.util.Ary;
+import com.seaofnodes.simple.util.SB;
+import com.seaofnodes.simple.util.Utils;
import java.io.ByteArrayOutputStream;
import java.util.*;
@@ -45,6 +45,9 @@ public class Encoding implements com.seaofnodes.isa.CodeSink {
public int [] _opStart; // Start of opcodes, by _nid
public byte[] _opLen; // Length of opcodes, by _nid
+ // Function headers now padded when printing
+ public boolean _padFunHeads;
+
// Big Constant relocation info.
public static class Relo {
public final Node _op;
@@ -294,6 +297,9 @@ public class Encoding implements com.seaofnodes.isa.CodeSink {
// Fall/false into a full block, Jump/true to an empty block.
if( f.nOuts()>1 && t.nOuts()==1 ) return false;
if( t.nOuts()>1 && f.nOuts()==1 ) return true ;
+ // Jump to a merge point, assuming other things are jumping there as well
+ if( f.out(0) instanceof RegionNode ) return true ;
+ if( t.out(0) instanceof RegionNode ) return false;
// Everything else equal, use pre-order
return t._pre > f._pre;
}
@@ -401,7 +407,7 @@ public class Encoding implements com.seaofnodes.isa.CodeSink {
if( n instanceof MachNode && !(n instanceof CFGNode) )
_opStart[n._nid] += slide;
}
-
+ _padFunHeads = true;
// Copy/slide the bits to make space for all the longer branches
int grow = _opStart[_code._cfg.at(len-1)._nid] - oldStarts[len-1];
@@ -418,6 +424,7 @@ public class Encoding implements com.seaofnodes.isa.CodeSink {
}
_bits.set(bits,bits.length);
}
+
}
@@ -496,12 +503,11 @@ public class Encoding implements com.seaofnodes.isa.CodeSink {
while( off < foff ) { bits.write(0); off++; };
// Constant array fields are special
if( f._fname=="[]" ) { // Must be a constant array
- ts._con.write(bits); // Write the constant array bits
- off += ts._con.len();
+ ((TypeConAry)f._t).write(bits);
+ off += ((TypeConAry)f._t).len();
} else {
- int log = f._type.log_size();
- if( f._fname=="#" ) addN(log,ts._con.len(),bits); // Must be a constant array
- else addN(log,f._type ,bits);
+ int log = f._t.log_size();
+ addN(log,f._t,bits);
off += 1<<log;
}
}
diff --git a/src/main/java/com/seaofnodes/simple/codegen/GlobalCodeMotion.java b/src/main/java/com/seaofnodes/simple/codegen/GlobalCodeMotion.java
index 530336d6..80d5d9bb 100644
--- a/src/main/java/com/seaofnodes/simple/codegen/GlobalCodeMotion.java
+++ b/src/main/java/com/seaofnodes/simple/codegen/GlobalCodeMotion.java
@@ -1,10 +1,10 @@
package com.seaofnodes.simple.codegen;
-import com.seaofnodes.simple.Ary;
import com.seaofnodes.simple.IterPeeps.WorkList;
-import com.seaofnodes.simple.Utils;
import com.seaofnodes.simple.node.*;
import com.seaofnodes.simple.type.*;
+import com.seaofnodes.simple.util.Ary;
+import com.seaofnodes.simple.util.Utils;
import java.util.*;
public abstract class GlobalCodeMotion {
@@ -36,7 +36,7 @@ public abstract class GlobalCodeMotion {
return; // Been there, done that
if( def instanceof ReturnNode && use instanceof CallEndNode )
return;
- assert !( def instanceof CallNode && use instanceof FunNode );
+ assert !( def instanceof CallNode && use instanceof FunNode ); // All calls unwired now
visit.set(cfg._nid);
for( Node useuse : cfg._outputs )
_rpo_cfg(cfg,useuse,visit,rpo);
diff --git a/src/main/java/com/seaofnodes/simple/codegen/IFG.java b/src/main/java/com/seaofnodes/simple/codegen/IFG.java
index 89850c8c..bcd1957d 100644
--- a/src/main/java/com/seaofnodes/simple/codegen/IFG.java
+++ b/src/main/java/com/seaofnodes/simple/codegen/IFG.java
@@ -1,9 +1,9 @@
package com.seaofnodes.simple.codegen;
-import com.seaofnodes.simple.Ary;
-import com.seaofnodes.simple.Utils;
import com.seaofnodes.simple.node.*;
import com.seaofnodes.simple.type.Type;
+import com.seaofnodes.simple.util.Ary;
+import com.seaofnodes.simple.util.Utils;
import java.util.BitSet;
import java.util.IdentityHashMap;
@@ -496,7 +496,7 @@ abstract public class IFG {
private static int biasable(Node split) {
if( split instanceof SplitNode ) return 1; // Yes biasable, advance is slot 1
- if( split instanceof PhiNode phi ) return phi.region() instanceof LoopNode ? 2 : 1; // Prefer the backedge in loops
+ if( split instanceof PhiNode phi ) return phi.region() instanceof LoopNode ? 2 : 1; // Prefer the backedge in loops
if( !(split instanceof MachNode mach) ) return 0; // Not biasable
return mach.twoAddress(); // Only biasable if 2-addr
}
diff --git a/src/main/java/com/seaofnodes/simple/codegen/LRG.java b/src/main/java/com/seaofnodes/simple/codegen/LRG.java
index ec254a2e..009ddcdb 100644
--- a/src/main/java/com/seaofnodes/simple/codegen/LRG.java
+++ b/src/main/java/com/seaofnodes/simple/codegen/LRG.java
@@ -1,10 +1,9 @@
package com.seaofnodes.simple.codegen;
-import com.seaofnodes.simple.Ary;
-import com.seaofnodes.simple.SB;
-import com.seaofnodes.simple.Utils;
import com.seaofnodes.simple.node.*;
-
+import com.seaofnodes.simple.util.Ary;
+import com.seaofnodes.simple.util.SB;
+import com.seaofnodes.simple.util.Utils;
import java.util.IdentityHashMap;
/** A Live Range
diff --git a/src/main/java/com/seaofnodes/simple/codegen/LinkMem.java b/src/main/java/com/seaofnodes/simple/codegen/LinkMem.java
index e1e849e6..2415203d 100644
--- a/src/main/java/com/seaofnodes/simple/codegen/LinkMem.java
+++ b/src/main/java/com/seaofnodes/simple/codegen/LinkMem.java
@@ -1,7 +1,7 @@
package com.seaofnodes.simple.codegen;
-import com.seaofnodes.simple.Utils;
import com.seaofnodes.simple.node.Node;
+import com.seaofnodes.simple.util.Utils;
// In-memory linking
public class LinkMem {
diff --git a/src/main/java/com/seaofnodes/simple/codegen/ListScheduler.java b/src/main/java/com/seaofnodes/simple/codegen/ListScheduler.java
index de4fadc4..ddf5a679 100644
--- a/src/main/java/com/seaofnodes/simple/codegen/ListScheduler.java
+++ b/src/main/java/com/seaofnodes/simple/codegen/ListScheduler.java
@@ -1,10 +1,10 @@
package com.seaofnodes.simple.codegen;
-import com.seaofnodes.simple.Ary;
import com.seaofnodes.simple.IterPeeps.WorkList;
-import com.seaofnodes.simple.Utils;
import com.seaofnodes.simple.node.*;
import com.seaofnodes.simple.type.*;
+import com.seaofnodes.simple.util.Ary;
+import com.seaofnodes.simple.util.Utils;
import java.util.*;
public abstract class ListScheduler {
diff --git a/src/main/java/com/seaofnodes/simple/codegen/RegAlloc.java b/src/main/java/com/seaofnodes/simple/codegen/RegAlloc.java
index 5a641490..6518f3b3 100644
--- a/src/main/java/com/seaofnodes/simple/codegen/RegAlloc.java
+++ b/src/main/java/com/seaofnodes/simple/codegen/RegAlloc.java
@@ -1,7 +1,7 @@
package com.seaofnodes.simple.codegen;
-import com.seaofnodes.simple.Ary;
-import com.seaofnodes.simple.Utils;
+import com.seaofnodes.simple.util.Ary;
+import com.seaofnodes.simple.util.Utils;
import com.seaofnodes.simple.node.*;
import com.seaofnodes.simple.type.Type;
import java.util.Arrays;
@@ -256,7 +256,11 @@ public class RegAlloc {
// A fixed-register clone with flexible uses must split at the uses.
!(lrg._1regDefCnt==1 && lrg._machDef.isClone() && lrg._1regUseCnt==0) )
return splitEmptyMaskSimple(round,lrg);
- // More complex conflicts use loop-boundary splitting.
+ // Repeated single-reg uses from a single def. Special for archs
+ // with more fixed regs.
+ if( !lrg._multiDef && lrg._1regDefCnt <= 1 && lrg._1regUseCnt > 2 )
+ if( splitEmptyMaskByUse(round,lrg) )
+ return true;
}
// Generic split-by-loop depth.
@@ -286,6 +290,57 @@ public class RegAlloc {
return true;
}
+ // Popular single-def value: share a split among uses with compatible masks.
+ boolean splitEmptyMaskByUse( byte round, LRG lrg ) {
+ Node def = (Node)lrg._machDef;
+ // Snapshot distinct users: rewriting an input changes def's output list,
+ // and a call can use the same value in more than one argument.
+ Ary<Node> uses = new Ary<>(Node.class);
+ for( Node use : def._outputs )
+ if( uses.find(use)==-1 ) uses.push(use);
+
+ Ary<RegMask> rclass = new Ary<>(RegMask.class);
+ int ncalls=0;
+ for( Node use : uses )
+ if( use instanceof MachNode mach ) {
+ // Sharing copies across several calls usually loses to their kills.
+ if( use instanceof CallNode && ++ncalls > 1 ) return false;
+ for( int i=1; i<use.nIns(); i++ )
+ if( use.in(i)==def && mach.regmap(i)!=null )
+ putIntoRegClass(rclass,mach.regmap(i));
+ }
+ if( rclass._len <= 1 ) return false;
+
+ for( RegMask rmask : rclass ) {
+ Node split = makeSplit(def,"popular",round,lrg,rmask);
+ split.insertAfter(def);
+ if( split.nIns()>1 ) split.setDef(1,def);
+ for( Node use : uses )
+ if( use instanceof MachNode mach )
+ for( int i=1; i<use.nIns(); i++ ) {
+ RegMask mask = mach.regmap(i);
+ if( use.in(i)==def && mask!=null && mask.overlap(rmask) ) {
+ assert rmask.and(mask)==rmask;
+ use.setDefOrdered(i,split);
+ }
+ }
+ }
+ return true;
+ }
+
+ // Narrowing preserves all earlier users of a class; new classes are disjoint.
+ // Each use contains its first overlapping class, so one pass suffices.
+ private static void putIntoRegClass( Ary<RegMask> rclass, RegMask rmask ) {
+ for( int i=0; i<rclass._len; i++ ) {
+ RegMask omask = rclass.at(i);
+ if( !omask.overlap(rmask) ) continue;
+ if( omask.and(rmask)!=omask )
+ rclass.set(i,new RegMask(omask.copy().and(rmask)));
+ return;
+ }
+ rclass.push(rmask);
+ }
+
// Self conflicts require Phis (or two-address).
// Insert a split after every def.
boolean splitSelfConflict( byte round, LRG lrg ) {
@@ -361,8 +416,8 @@ public class RegAlloc {
// If the minLoopDepth is less than the maxLoopDepth: for-all defs and
// uses, if at minLoopDepth or lower, split after def and before use.
for( Node n : _ns ) {
- if( n instanceof SplitNode ) continue; // Ignoring splits; since spilling need to split in a deeper loop
- if( n.isDead() ) continue; // Some Clonable went dead by other spill changes
+ if( n instanceof SplitNode && min!=max ) continue; // Ignoring splits; since spilling need to split in a deeper loop
+ if( n.isDead() ) continue; // Some Cloneable went dead by other spill changes
// If this is a 2-address commutable op (e.g. AddX86, MulX86) and the rhs has only a single user,
// commute the inputs... which chops the LHS live ranges' upper bound to just the RHS.
if( n instanceof MachNode mach && lrg(n)==lrg && mach.twoAddress()==1 && mach.commutes() && n.in(2).nOuts()==1 )
@@ -504,9 +559,9 @@ public class RegAlloc {
}
}
- private Node makeSplit( Node def, String kind, byte round, LRG lrg, RegMask use ) {
- // A clone must be able to satisfy the use's register class.
- Node split = def instanceof MachNode mach && mach.isClone() && (use==null || mach.outregmap().overlap(use))
+ private Node makeSplit( Node def, String kind, byte round, LRG lrg, RegMask umask ) {
+ // Clone simple constants if possible
+ Node split = def instanceof MachNode mach && mach.isClone() && (umask==null || mach.outregmap().overlap(umask))
? mach.copy()
: _code._mach.split(kind,round,lrg);
_lrgs.put(split,lrg);
diff --git a/src/main/java/com/seaofnodes/simple/codegen/RegMask.java b/src/main/java/com/seaofnodes/simple/codegen/RegMask.java
index f73cd9a1..303bba9e 100644
--- a/src/main/java/com/seaofnodes/simple/codegen/RegMask.java
+++ b/src/main/java/com/seaofnodes/simple/codegen/RegMask.java
@@ -1,7 +1,7 @@
package com.seaofnodes.simple.codegen;
-import com.seaofnodes.simple.Utils;
-import com.seaofnodes.simple.SB;
+import com.seaofnodes.simple.util.SB;
+import com.seaofnodes.simple.util.Utils;
/** RegMask
* A "register mask" - 1 bit set for each allowed register. In addition,
diff --git a/src/main/java/com/seaofnodes/simple/node/AddNode.java b/src/main/java/com/seaofnodes/simple/node/AddNode.java
index 3a5528ef..dfc7d7b3 100644
--- a/src/main/java/com/seaofnodes/simple/node/AddNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/AddNode.java
@@ -1,10 +1,7 @@
package com.seaofnodes.simple.node;
-import com.seaofnodes.simple.Parser;
import com.seaofnodes.simple.type.*;
-import java.util.BitSet;
-
import static com.seaofnodes.simple.Parser.con;
public class AddNode extends ArithNode {
@@ -129,7 +126,7 @@ public class AddNode extends ArithNode {
for( int i=1; i<ns.length; i++ )
ns[i] = op.copy(lphi.in(i), rhs instanceof PhiNode ? rhs.in(i) : rhs).peephole();
String label = lphi._label + (rhs instanceof PhiNode rphi ? rphi._label : "");
- Node phi = new PhiNode(label,lphi._declaredType,ns).peephole();
+ Node phi = new PhiNode(label,lphi._minType.meet(op._type),ns).peephole();
// Rotate needs another op, otherwise just the phi
return lhs==lphi ? phi : op.copy(lhs.in(1),phi);
}
@@ -151,8 +148,8 @@ public class AddNode extends ArithNode {
// Ties with in a category sort by node ID.
// TRUE if swapping hi and lo.
static boolean spine_cmp( Node hi, Node lo, Node dep ) {
- if( lo._type.isConstant() ) return false;
- if( hi._type.isConstant() ) return true ;
+ if( lo._type.isConstant() || lo instanceof ConFldOffNode ) return false;
+ if( hi._type.isConstant() || hi instanceof ConFldOffNode ) return true ;
if( lo instanceof PhiNode lphi && lphi.region()._type==Type.XCONTROL ) return false;
if( hi instanceof PhiNode hphi && hphi.region()._type==Type.XCONTROL ) return false;
diff --git a/src/main/java/com/seaofnodes/simple/node/ArithNode.java b/src/main/java/com/seaofnodes/simple/node/ArithNode.java
index e94ccbc6..124acbd7 100644
--- a/src/main/java/com/seaofnodes/simple/node/ArithNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/ArithNode.java
@@ -3,10 +3,10 @@ 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.TypeInteger;
+
public abstract class ArithNode extends Node {
// Source location for late reported errors
Parser.Lexer _loc;
@@ -53,7 +53,10 @@ public abstract class ArithNode extends Node {
if( in(1) instanceof PhiNode lhs &&
in(2) instanceof PhiNode rhs &&
lhs.nIns() >= 2 && !lhs.inProgress() &&
- lhs.region()==rhs.region() ) {
+ lhs.region()==rhs.region() &&
+ lhs.nIns()>2 && // A 1-input Phi will collapse already
+ // Disallow with self-looping phi; these will collapse
+ (lhs.in(2)!=lhs && rhs.in(2)!=rhs) ) {
// Profit check: only 1 instance of `this` will remain, all the
// others will fold to constants.
int cnt=0;
@@ -70,7 +73,7 @@ public abstract class ArithNode extends Node {
: copy(lhs.in(i), rhs.in(i)).peephole();
}
String label = lhs._label==rhs._label ? lhs._label : lhs._label + rhs._label;
- return new PhiNode(label,lhs._declaredType,ns).peephole();
+ return new PhiNode(label,lhs._minType,ns).peephole();
}
}
return null;
diff --git a/src/main/java/com/seaofnodes/simple/node/BoolNode.java b/src/main/java/com/seaofnodes/simple/node/BoolNode.java
index 21ac59d3..0d82c5b9 100644
--- a/src/main/java/com/seaofnodes/simple/node/BoolNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/BoolNode.java
@@ -3,8 +3,8 @@ 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.*;
+import com.seaofnodes.simple.util.Utils;
import static com.seaofnodes.simple.type.TypeInteger.*;
abstract public class BoolNode extends Node {
@@ -28,7 +28,7 @@ abstract public class BoolNode extends Node {
Type t2 = in(2)._type;
// Exactly equals?
if( t1.isHigh() || t2.isHigh() )
- return BOOL.dual();
+ return (TypeInteger)BOOL.dual();
if( in(1)==in(2) )
// LT fails, both EQ and LE succeed
return this instanceof LT ? FALSE : TRUE;
diff --git a/src/main/java/com/seaofnodes/simple/node/CFGNode.java b/src/main/java/com/seaofnodes/simple/node/CFGNode.java
index 4f346ee3..6805c1ef 100644
--- a/src/main/java/com/seaofnodes/simple/node/CFGNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/CFGNode.java
@@ -1,10 +1,10 @@
package com.seaofnodes.simple.node;
-import com.seaofnodes.simple.Ary;
import com.seaofnodes.simple.Parser;
-import com.seaofnodes.simple.Utils;
import com.seaofnodes.simple.codegen.CodeGen;
import com.seaofnodes.simple.type.*;
+import com.seaofnodes.simple.util.Ary;
+import com.seaofnodes.simple.util.Utils;
import java.util.BitSet;
import java.util.HashMap;
import java.util.HashSet;
@@ -60,7 +60,7 @@ public abstract class CFGNode extends Node {
// Used by the encoding / final BB layout
public CFGNode uctrlSkipEmpty() {
CFGNode x = this, y;
- while( x.nOuts() == 1 && (y=x.uctrl())!=null ) // Skip empty blocks
+ while( x.nOuts() == 1 && (y=x.uctrl())!=null && !(y instanceof CallNode) ) // Skip empty blocks
x = y;
return x;
}
diff --git a/src/main/java/com/seaofnodes/simple/node/CProjNode.java b/src/main/java/com/seaofnodes/simple/node/CProjNode.java
index 4d3d8d0d..1225de31 100644
--- a/src/main/java/com/seaofnodes/simple/node/CProjNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/CProjNode.java
@@ -3,10 +3,10 @@ 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.TypeMem;
import com.seaofnodes.simple.type.TypeTuple;
+import com.seaofnodes.simple.util.Utils;
public class CProjNode extends CFGNode {
diff --git a/src/main/java/com/seaofnodes/simple/node/CallEndMach.java b/src/main/java/com/seaofnodes/simple/node/CallEndMach.java
index 47cab07a..d26ea33b 100644
--- a/src/main/java/com/seaofnodes/simple/node/CallEndMach.java
+++ b/src/main/java/com/seaofnodes/simple/node/CallEndMach.java
@@ -1,7 +1,7 @@
package com.seaofnodes.simple.node;
-import com.seaofnodes.simple.SB;
import com.seaofnodes.simple.codegen.*;
+import com.seaofnodes.simple.util.SB;
public class CallEndMach extends CallEndNode implements MachNode {
public CallEndMach( CallEndNode n ) { super(n); }
diff --git a/src/main/java/com/seaofnodes/simple/node/CallEndNode.java b/src/main/java/com/seaofnodes/simple/node/CallEndNode.java
index 94f5ee49..e83e5cba 100644
--- a/src/main/java/com/seaofnodes/simple/node/CallEndNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/CallEndNode.java
@@ -75,10 +75,10 @@ public class CallEndNode extends CFGNode implements MultiNode {
assert fun.in(1) instanceof StartNode && fun.in(2)==call;
// Disallow self-recursive inlining (loop unrolling by another name)
CFGNode idom = call;
- while( idom!=null && !(idom instanceof FunNode) )
+ while( !(idom instanceof FunNode) && idom!=null )
idom = idom.idom();
// Inline?
- if( idom != null && idom != fun ) {
+ if( idom != fun && idom != null ) {
// Trivial inline: rewrite
_folding = true;
// Rewrite Fun so the normal RegionNode ideal collapses
@@ -87,6 +87,11 @@ public class CallEndNode extends CFGNode implements MultiNode {
fun.setDef(2,call.ctrl()); // Bypass the Call;
fun.ret().setDef(3,null); // Return is folding also
CodeGen.CODE.addAll(fun._outputs);
+ // Repeat defs 1 layer down, for users of Parm (Phis)
+ for( Node parm : fun._outputs )
+ if( parm instanceof ParmNode )
+ CodeGen.CODE.addAll(parm._outputs);
+
// Inlining immediately blows all cache idepth fields past the inline point.
// Bump the global version number invalidating them en-masse.
CodeGen.CODE.invalidateIDepthCaches();
@@ -199,5 +204,4 @@ public class CallEndNode extends CFGNode implements MultiNode {
@Override public Node pcopy(int idx) {
return _folding ? in(1).in(idx) : null;
}
-
}
diff --git a/src/main/java/com/seaofnodes/simple/node/CallNode.java b/src/main/java/com/seaofnodes/simple/node/CallNode.java
index 3bf873fa..f66d1f71 100644
--- a/src/main/java/com/seaofnodes/simple/node/CallNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/CallNode.java
@@ -2,12 +2,12 @@ package com.seaofnodes.simple.node;
import com.seaofnodes.print.ExprPrinter;
-import com.seaofnodes.simple.codegen.CodeGen;
import com.seaofnodes.simple.IterPeeps;
import com.seaofnodes.simple.Parser;
-import com.seaofnodes.simple.Utils;
+import com.seaofnodes.simple.codegen.CodeGen;
import com.seaofnodes.simple.type.Type;
import com.seaofnodes.simple.type.TypeFunPtr;
+import com.seaofnodes.simple.util.Utils;
/**
* Call
@@ -32,7 +32,7 @@ public class CallNode extends CFGNode {
return p.unchar(',').unchar(' ').close();
}
public String name() {
- if( fptr()._type instanceof TypeFunPtr tfp && tfp.isConstant() && tfp.notNull() ) {
+ if( fptr()._type instanceof TypeFunPtr tfp && tfp._isConstant() && tfp.notNull() ) {
FunNode fun = CodeGen.CODE.link(tfp);
if( fun !=null ) return fun._name;
if( fptr() instanceof ExternNode ex ) return ex._extern;
@@ -162,8 +162,10 @@ public class CallNode extends CFGNode {
// Check for args
for( int i=0; i<tfp.nargs(); i++ )
- if( !arg(i+2)._type.isa(tfp.arg(i)) || !arg(i+2)._type.accessISA(tfp.arg(i)) )
- return Parser.error( "Argument #"+i+" isa "+arg(i+2)._type+", but must be a "+tfp.arg(i), _loc);
+ if( !arg(i+2)._type.isa(tfp.arg(i)) || !arg(i+2)._type.accessISA(tfp.arg(i)) ) {
+ cend().addDep(arg(i+2)); // Upgrading arg type may allow inlining
+ return Parser.error( "Argument #" + i + " isa " + arg(i+2)._type + ", but must be a " + tfp.arg(i), _loc );
+ }
if( tfp.fidxs() < 0 )
throw Utils.TODO(); // Infinite unknown TFPs? Should be fairly precise CG
diff --git a/src/main/java/com/seaofnodes/simple/node/CastMach.java b/src/main/java/com/seaofnodes/simple/node/CastMach.java
index 92822433..e45898f5 100644
--- a/src/main/java/com/seaofnodes/simple/node/CastMach.java
+++ b/src/main/java/com/seaofnodes/simple/node/CastMach.java
@@ -1,9 +1,9 @@
package com.seaofnodes.simple.node;
-import com.seaofnodes.simple.SB;
import com.seaofnodes.simple.codegen.CodeGen;
import com.seaofnodes.simple.codegen.Encoding;
import com.seaofnodes.simple.codegen.RegMask;
+import com.seaofnodes.simple.util.SB;
/**
* Cast a pointer to read-only
diff --git a/src/main/java/com/seaofnodes/simple/node/CastNode.java b/src/main/java/com/seaofnodes/simple/node/CastNode.java
index 8ae7b327..4e93f3e6 100644
--- a/src/main/java/com/seaofnodes/simple/node/CastNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/CastNode.java
@@ -3,11 +3,10 @@ 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.TypeInteger;
import com.seaofnodes.simple.type.TypeMemPtr;
-
+import com.seaofnodes.simple.util.Utils;
// Upcast (join) the input to a t. Used after guard test to lift an input.
// Can also be used to make a type-assertion if ctrl is null.
@@ -37,14 +36,20 @@ public class CastNode extends Node {
@Override
public Type compute() {
// Cast array to int
- if( _t == TypeInteger.BOT && in(1)._type instanceof TypeMemPtr tmp && tmp._obj.isAry() )
+ Type t1 = in(1)._type;
+ if( _t == TypeInteger.BOT && t1 instanceof TypeMemPtr tmp && tmp._obj.isAry() )
return _t;
- return in(1)._type.join(_t);
+ // Freeze if the join is high but both inputs are low
+ Type tj = t1.join(_t);
+ if( tj.isHigh() && !t1.isHigh() )
+ return _type==null ? _t : _type;
+
+ return tj;
}
@Override
public Node idealize() {
- return in(1)._type.isa(_t) ? in(1) : null;
+ return in(1)._type.isa(_t) && in(1)._type.accessISA(_t) ? in(1) : null;
}
@Override
@@ -61,6 +66,6 @@ public class CastNode extends Node {
// Has a condition to test, so OK
if( in(0) != null ) return null;
// No condition to test, so this must optimize away
- throw Utils.TODO();
+ return Parser.error( "Type " + in(1)._type.str() + " is not of declared type " + _t.str(), null );
}
}
diff --git a/src/main/java/com/seaofnodes/simple/node/ConFldOffNode.java b/src/main/java/com/seaofnodes/simple/node/ConFldOffNode.java
new file mode 100644
index 00000000..04b88b87
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/ConFldOffNode.java
@@ -0,0 +1,41 @@
+package com.seaofnodes.simple.node;
+
+import com.seaofnodes.print.ExprPrinter;
+
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.type.Type;
+import com.seaofnodes.simple.type.TypeInteger;
+import com.seaofnodes.simple.type.TypeStruct;
+import com.seaofnodes.simple.util.SB;
+
+/**
+ * A field offset. This becomes a constant *after* other optimizations which
+ * determine field sizes - some fields might be dead, or hold only zeros or
+ * small values.
+ */
+
+public class ConFldOffNode extends ConstantNode {
+ public final String _name; // Struct name
+ public final String _fname; // Field name
+ public ConFldOffNode( String name, String fname ) { super(TypeInteger.BOT); _name = name; _fname = fname; }
+
+ @Override public String label() {
+ return _fname == " len" ? "sizeof("+_name+")" : "#"+_fname;
+ }
+
+ @Override protected ExprPrinter<Node> _print1(ExprPrinter<Node> p) {
+ return p.p(label());
+ }
+
+ // Convert field offset to an integer
+ public Node asOffset(TypeStruct ts) {
+ return new ConstantNode(TypeInteger.constant(ts.offset(_fname==" len" ? ts._fields.length : ts.find(_fname)))).peephole();
+ }
+
+
+ @Override public boolean eq(Node n) {
+ ConFldOffNode off = (ConFldOffNode)n; // Invariant
+ return _name==off._name && _fname==off._fname && super.eq(n);
+ }
+ @Override int hash() { return super.hash() * _name.hashCode() * _fname.hashCode(); }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/ConstantNode.java b/src/main/java/com/seaofnodes/simple/node/ConstantNode.java
index d3ac24a7..6384228e 100644
--- a/src/main/java/com/seaofnodes/simple/node/ConstantNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/ConstantNode.java
@@ -3,16 +3,12 @@ package com.seaofnodes.simple.node;
import com.seaofnodes.print.ExprPrinter;
import com.seaofnodes.simple.codegen.CodeGen;
-import com.seaofnodes.simple.Parser;
-import com.seaofnodes.simple.SB;
import com.seaofnodes.simple.type.Type;
import com.seaofnodes.simple.type.TypeFunPtr;
+import com.seaofnodes.simple.util.SB;
/**
- * A Constant node represents a constant value. At present, the only constants
- * that we allow are integer literals; therefore Constants contain an integer
- * value. As we add other types of constants, we will refactor how we represent
- * Constants.
+ * A Constant node represents a constant value.
* <p>
* Constants have no semantic inputs. However, we set Start as an input to
* Constants to enable a forward graph walk. This edge carries no semantic
@@ -27,8 +23,7 @@ public class ConstantNode extends Node {
super(new Node[]{CodeGen.CODE._start});
_con = _type = type;
}
- public ConstantNode( Node con, Type t ) { super(con); _con = t; }
- public ConstantNode( ConstantNode con ) { this(con,con._type); }
+ public ConstantNode( ConstantNode con ) { super(con); _con = con._type; }
public static Node make( Type type ) {
if( type==Type. CONTROL ) return new CtrlNode();
@@ -48,28 +43,17 @@ public class ConstantNode extends Node {
@Override
protected ExprPrinter<Node> _print1(ExprPrinter<Node> p) {
- if( _con instanceof TypeFunPtr tfp && tfp.isConstant() && tfp.notNull() ) {
+ 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==null ? "---" : _con.print(new SB()));
+ return p.p(_con==null ? "---" : _con.toString());
}
@Override public boolean isConst() { return true; }
-
- @Override
- public Type compute() { return _con; }
-
- @Override
- public Node idealize() { return null; }
-
- @Override
- public boolean eq(Node n) {
- ConstantNode con = (ConstantNode)n; // Contract
- return _con==con._con;
- }
-
- @Override
- int hash() { return _con.hashCode(); }
+ @Override public Type compute() { return _con; }
+ @Override public Node idealize() { return null; }
+ @Override public boolean eq(Node n) { return _con==((ConstantNode)n)._con; }
+ @Override int hash() { return _con.hashCode(); }
}
diff --git a/src/main/java/com/seaofnodes/simple/node/ExternNode.java b/src/main/java/com/seaofnodes/simple/node/ExternNode.java
index 093cc2cb..ccbcfc66 100644
--- a/src/main/java/com/seaofnodes/simple/node/ExternNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/ExternNode.java
@@ -1,10 +1,10 @@
package com.seaofnodes.simple.node;
-import com.seaofnodes.simple.codegen.CodeGen;
import com.seaofnodes.simple.Parser;
-import com.seaofnodes.simple.SB;
+import com.seaofnodes.simple.codegen.CodeGen;
import com.seaofnodes.simple.type.Type;
import com.seaofnodes.simple.type.TypeFunPtr;
+import com.seaofnodes.simple.util.SB;
import java.util.BitSet;
/**
diff --git a/src/main/java/com/seaofnodes/simple/node/FunNode.java b/src/main/java/com/seaofnodes/simple/node/FunNode.java
index 5f67f859..e7d6c3a6 100644
--- a/src/main/java/com/seaofnodes/simple/node/FunNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/FunNode.java
@@ -1,13 +1,14 @@
package com.seaofnodes.simple.node;
-
-import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.Parser;
import com.seaofnodes.simple.codegen.CodeGen;
import com.seaofnodes.simple.codegen.Encoding;
import com.seaofnodes.simple.codegen.RegMask;
import com.seaofnodes.simple.type.Type;
import com.seaofnodes.simple.type.TypeFunPtr;
import com.seaofnodes.simple.type.TypeTuple;
+import com.seaofnodes.simple.util.SB;
+import com.seaofnodes.simple.util.Utils;
import java.util.BitSet;
import static com.seaofnodes.simple.codegen.CodeGen.CODE;
@@ -134,10 +135,10 @@ public class FunNode extends RegionNode {
// unless folding then just a ID on input#1
@Override public int idepth() { return _folding ? super.idepth() : 1; }
// Bypass Region idom, always assume idom is Start
- @Override public CFGNode idom(Node dep) { return _folding && nIns()==3 ? cfg(2) : cfg(1); }
+ @Override public CFGNode idom(Node dep) { return _folding && nIns()==3 ? cfg(2) : (nIns()>1 ? cfg(1) : null); }
// Always in-progress until we run out of unknown callers
- public boolean unknownCallers() { return nIns()<2 || in(1) instanceof StartNode; }
+ public boolean unknownCallers() { return nIns()>=2 && in(1) instanceof StartNode; }
@Override public boolean inProgress() { return unknownCallers(); }
diff --git a/src/main/java/com/seaofnodes/simple/node/IfNode.java b/src/main/java/com/seaofnodes/simple/node/IfNode.java
index 81fbe170..da6a0cde 100644
--- a/src/main/java/com/seaofnodes/simple/node/IfNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/IfNode.java
@@ -2,9 +2,9 @@ package com.seaofnodes.simple.node;
import com.seaofnodes.print.ExprPrinter;
-import com.seaofnodes.simple.Utils;
import com.seaofnodes.simple.codegen.CodeGen;
import com.seaofnodes.simple.type.*;
+import com.seaofnodes.simple.util.Utils;
public class IfNode extends CFGNode implements MultiNode {
@@ -88,8 +88,8 @@ public class IfNode extends CFGNode implements MultiNode {
return switch( bop ) {
case "<" -> ">" ;
case "<=" -> ">=";
- case "==" -> "!=";
- case "!=" -> "==";
+ case "==" -> "==";
+ case "!=" -> "!=";
case ">" -> "<" ;
case ">=" -> "<=";
default -> throw Utils.TODO();
diff --git a/src/main/java/com/seaofnodes/simple/node/LoadNode.java b/src/main/java/com/seaofnodes/simple/node/LoadNode.java
index 3084b4f7..06226df0 100644
--- a/src/main/java/com/seaofnodes/simple/node/LoadNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/LoadNode.java
@@ -5,6 +5,7 @@ import com.seaofnodes.print.ExprPrinter;
import com.seaofnodes.simple.codegen.CodeGen;
import com.seaofnodes.simple.Parser;
import com.seaofnodes.simple.type.*;
+import com.seaofnodes.simple.util.Utils;
import java.util.BitSet;
/**
@@ -52,16 +53,29 @@ public class LoadNode extends MemOpNode {
@Override
public Type compute() {
if( ptr()._type==Type.TOP ) return Type.TOP; // Dead pointer, not a type error.
- Type t = MemMergeNode.contents(mem(),_alias,this);
- // Update declared forward ref to the actual.
- if( _declaredType.isFRef() && t instanceof TypeMemPtr tmp && !tmp.isFRef() )
- _declaredType = tmp.withAccess(((TypeMemPtr)_declaredType)._ro);
- Type rez = err()==null ? _declaredType.join(t) : _declaredType;
- return ptr()._type.isFinal() || _declaredType.isFinal() ? rez.makeRO() : rez;
+ if( !(mem()._type instanceof TypeMem mem) )
+ return _declaredType; // No memory yet? Declared type
+ assert !_declaredType.isFRef();
+ // No lifting if ptr might null-check
+ if( err()!=null || !(ptr()._type instanceof TypeMemPtr tmp) )
+ return _declaredType; // No pointer yet? Declared type
+ Type t = tmp._obj.field(_name)._t;
+ if( t instanceof TypeConAry ary ) {
+ t = ary.elem(); // TODO: if offset is known, can peek the constant
+ }
+ // Lift from declared type and memory input
+ t = t.join(_declaredType).join(MemMergeNode.contents(mem(),_alias,this));
+ if( ptr()._type.isFinal() || _declaredType.isFinal() )
+ t = t.makeRO(); // Deep final applied
+ return t;
}
@Override
public Node idealize() {
+ if( mem() instanceof CastNode cast ) {
+ setDef(1,cast.in(1));
+ return this;
+ }
if( mem() instanceof MemMergeNode merge ) {
setDef(1,CodeGen.CODE.add(merge.alias(_alias)));
return this;
@@ -118,7 +132,7 @@ public class LoadNode extends MemOpNode {
// 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();
+ Type zero = ts._fields[ts.findAlias(_alias)]._t.makeZero();
assert zero.isa(_type); // Catch an uninitialized non-null field
return castRO(new ConstantNode(zero).peephole());
}
@@ -133,6 +147,7 @@ public class LoadNode extends MemOpNode {
if( mem==stop ) return mem;
switch( mem ) {
case MemMergeNode merge: mem=merge.alias(_alias); break;
+ case CastNode cast: mem=cast.in(1); break;
case StoreNode st:
addDep(st.ptr());
if( _alias==st._alias && ptr==st.ptr() && off()==st.off() ) return st;
diff --git a/src/main/java/com/seaofnodes/simple/node/LoopNode.java b/src/main/java/com/seaofnodes/simple/node/LoopNode.java
index 265132dc..1d493db7 100644
--- a/src/main/java/com/seaofnodes/simple/node/LoopNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/LoopNode.java
@@ -1,11 +1,11 @@
package com.seaofnodes.simple.node;
import com.seaofnodes.simple.Parser;
-import com.seaofnodes.simple.Utils;
import com.seaofnodes.simple.codegen.CodeGen;
import com.seaofnodes.simple.type.Type;
import com.seaofnodes.simple.type.TypeInteger;
import com.seaofnodes.simple.type.TypeMem;
+import com.seaofnodes.simple.util.Utils;
import java.util.BitSet;
import java.util.HashSet;
@@ -46,7 +46,7 @@ public class LoopNode extends RegionNode {
x = x.idom();
}
// Found a no-exit loop. Insert an exit
- NeverNode iff = new NeverNode(back()); // Ideal never-branch
+ NeverNode iff = (NeverNode)new NeverNode(back()).peephole(); // Ideal never-branch
CProjNode t = new CProjNode(iff,0,"True" ).init();
CProjNode f = new CProjNode(iff,1,"False").init();
setDef(2,t); // True continues loop, False (never) exits loop
@@ -77,17 +77,19 @@ public class LoopNode extends RegionNode {
expr = new PhiNode(r,expr).init();
}
// Append new Never exit
- ctrl.addDef(f );
- mem .addDef(memout);
- expr.addDef(top);
+ ctrl.keep().addDef(f );
+ mem .keep().addDef(memout);
+ expr.keep().addDef(top);
} else {
- ctrl = f;
- mem = memout;
- expr = top;
+ ctrl = f .keep();
+ mem = memout.keep();
+ expr = top .keep();
}
- ret.setDef(0,ctrl);
- ret.setDef(1,mem );
- ret.setDef(2,expr);
+ ret.setDef(0,ctrl.unkeep());
+ ret.setDef(1,mem .unkeep());
+ ret.setDef(2,expr.unkeep());
+ ret._type = null; // This may LIFT the Return type, from [XCtrl,mem,expr] to [Ctrl,mem,expr]
+ ret.setType(ret.compute());
return fun;
}
diff --git a/src/main/java/com/seaofnodes/simple/node/MachConcreteNode.java b/src/main/java/com/seaofnodes/simple/node/MachConcreteNode.java
index 4f5d9151..9c0755b9 100644
--- a/src/main/java/com/seaofnodes/simple/node/MachConcreteNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/MachConcreteNode.java
@@ -3,7 +3,7 @@ package com.seaofnodes.simple.node;
import com.seaofnodes.print.ExprPrinter;
import com.seaofnodes.simple.type.Type;
-import com.seaofnodes.simple.Utils;
+import com.seaofnodes.simple.util.Utils;
import java.io.ByteArrayOutputStream;
// Generic machine-specific class, has a few Node implementations that have to
diff --git a/src/main/java/com/seaofnodes/simple/node/MachNode.java b/src/main/java/com/seaofnodes/simple/node/MachNode.java
index f63b3651..c0b3af66 100644
--- a/src/main/java/com/seaofnodes/simple/node/MachNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/MachNode.java
@@ -2,6 +2,8 @@ package com.seaofnodes.simple.node;
import com.seaofnodes.simple.*;
import com.seaofnodes.simple.codegen.*;
+import com.seaofnodes.simple.util.SB;
+import com.seaofnodes.simple.util.Utils;
public interface MachNode {
diff --git a/src/main/java/com/seaofnodes/simple/node/MemMergeNode.java b/src/main/java/com/seaofnodes/simple/node/MemMergeNode.java
index 421e4864..8d924b96 100644
--- a/src/main/java/com/seaofnodes/simple/node/MemMergeNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/MemMergeNode.java
@@ -36,13 +36,16 @@ public class MemMergeNode extends Node {
// 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) {
- dep.addDep(mem);
+ // A loop memory Phi can also be a user of the querying Store.
+ // Its type changing still needs to revisit this query.
+ dep.addDepForwards(mem);
+ if( mem instanceof CastNode cast ) return contents(cast.in(1),alias,dep);
if( mem instanceof MemMergeNode merge )
return contents(merge.alias(alias),alias,dep);
if( mem instanceof ProjNode proj && proj.in(0) instanceof NewNode nnn ) {
assert proj._idx==1 && nnn.field(alias)!=null;
dep.addDep(nnn);
- return contents(nnn.mem(),alias,dep).meet(nnn.field(alias)._type.makeZero());
+ return contents(nnn.mem(),alias,dep).meet(nnn.field(alias)._t.makeZero());
}
// Function parameters and call results may contain arbitrary heap values.
// Their bulk memory types do not imply empty or zero-filled storage.
@@ -67,6 +70,7 @@ public class MemMergeNode extends Node {
@Override public Node idealize() {
boolean progress=false, allDefault=true;
for( int i=2; i<nIns(); i++ ) {
+ if( in(i) instanceof CastNode cast ) { setDef(i,cast.in(1)); progress=true; }
if( in(i)!=null && in(i)==in(1) ) { setDef(i,null); progress=true; }
if( in(i) instanceof MemMergeNode mem ) {
setDef(i,CodeGen.CODE.add(mem.alias(i)));
diff --git a/src/main/java/com/seaofnodes/simple/node/MemOpNode.java b/src/main/java/com/seaofnodes/simple/node/MemOpNode.java
index db667e6d..e7b2c792 100644
--- a/src/main/java/com/seaofnodes/simple/node/MemOpNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/MemOpNode.java
@@ -3,9 +3,9 @@ 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 com.seaofnodes.simple.util.Utils;
import java.lang.StringBuilder;
import java.util.BitSet;
import java.util.ArrayList;
diff --git a/src/main/java/com/seaofnodes/simple/node/MemPhiNode.java b/src/main/java/com/seaofnodes/simple/node/MemPhiNode.java
index 1349cadb..bada20ac 100644
--- a/src/main/java/com/seaofnodes/simple/node/MemPhiNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/MemPhiNode.java
@@ -22,7 +22,7 @@ public class MemPhiNode extends PhiNode {
assert BulkMemPhiNode.checkMem(this,_alias);
if( !(region() instanceof RegionNode r) )
return region()._type==Type.XCONTROL ? TypeMem.TOP : _type;
- if( r.inProgress() ) return _declaredType;
+ if( r.inProgress() ) return _minType;
Type t = Type.TOP;
for( int i=1; i<nIns(); i++ )
if( addDep(r.in(i))._type!=Type.XCONTROL )
diff --git a/src/main/java/com/seaofnodes/simple/node/MinusNode.java b/src/main/java/com/seaofnodes/simple/node/MinusNode.java
index cd67b8fc..921e6066 100644
--- a/src/main/java/com/seaofnodes/simple/node/MinusNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/MinusNode.java
@@ -3,8 +3,7 @@ package com.seaofnodes.simple.node;
import com.seaofnodes.simple.type.Type;
import com.seaofnodes.simple.type.TypeInteger;
-import com.seaofnodes.simple.Utils;
-
+import com.seaofnodes.simple.util.Utils;
public class MinusNode extends Node {
public MinusNode(Node in) { super(null, in); }
diff --git a/src/main/java/com/seaofnodes/simple/node/MultiNode.java b/src/main/java/com/seaofnodes/simple/node/MultiNode.java
index 17d00881..b4d6d78e 100644
--- a/src/main/java/com/seaofnodes/simple/node/MultiNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/MultiNode.java
@@ -1,6 +1,6 @@
package com.seaofnodes.simple.node;
-import com.seaofnodes.simple.Ary;
+import com.seaofnodes.simple.util.Ary;
public interface MultiNode {
diff --git a/src/main/java/com/seaofnodes/simple/node/NewNode.java b/src/main/java/com/seaofnodes/simple/node/NewNode.java
index 4179821c..3c2a8de3 100644
--- a/src/main/java/com/seaofnodes/simple/node/NewNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/NewNode.java
@@ -2,10 +2,10 @@ package com.seaofnodes.simple.node;
import com.seaofnodes.print.ExprPrinter;
-import com.seaofnodes.simple.SB;
-import com.seaofnodes.simple.Utils;
import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.type.*;
+import com.seaofnodes.simple.util.SB;
+
/** Allocate a zeroed object. Inputs {ctrl, $mem, size};
* results {ptr, $mem}.
@@ -30,14 +30,14 @@ public class NewNode extends Node implements MultiNode {
public Field field(int alias) {
for( Field f : _ptr._obj._fields )
- if( f._alias==alias ) return f;
+ if( !f._one && f._alias==alias ) return f;
return null;
}
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 public String label() { return "new_"+(_ptr._obj.isAry() ? "ary_"+_ptr._obj._fields[1]._t.str() : _ptr._obj.str()); }
@Override protected ExprPrinter<Node> _print1(ExprPrinter<Node> p) {
return p.p("new ").p(_ptr._obj.str());
}
@@ -49,7 +49,14 @@ public class NewNode extends Node implements MultiNode {
}
@Override
- public Node idealize() { return null; }
+ public Node idealize() {
+ Node progress=null;
+ for( int i=1; i<nIns(); i++ ) {
+ if( in(i) instanceof CastNode cast )
+ progress = setDef(i,cast.in(1));
+ }
+ return progress == null ? null : this;
+ }
@Override
public boolean eq(Node n) { return this == n; }
diff --git a/src/main/java/com/seaofnodes/simple/node/Node.java b/src/main/java/com/seaofnodes/simple/node/Node.java
index fff1ab14..3eacbb16 100644
--- a/src/main/java/com/seaofnodes/simple/node/Node.java
+++ b/src/main/java/com/seaofnodes/simple/node/Node.java
@@ -2,7 +2,9 @@ package com.seaofnodes.simple.node;
import com.seaofnodes.print.BaseNode;
-import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.Parser;
+import com.seaofnodes.simple.util.Ary;
+import com.seaofnodes.simple.util.Utils;
import com.seaofnodes.simple.codegen.CodeGen;
import com.seaofnodes.simple.print.IRPrinter;
import com.seaofnodes.simple.type.Type;
@@ -256,6 +258,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);
+ CODE.add(n);
CODE.addAll(n._outputs);
}
kill();
@@ -360,7 +363,7 @@ public abstract class Node extends BaseNode<Node> implements Cloneable {
// 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() )
+ if( !isConst() && _type.isHighOrConst() )
return ConstantNode.make(_type).peephole();
// Global Value Numbering
diff --git a/src/main/java/com/seaofnodes/simple/node/NotNode.java b/src/main/java/com/seaofnodes/simple/node/NotNode.java
index 57fa9301..775b069b 100644
--- a/src/main/java/com/seaofnodes/simple/node/NotNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/NotNode.java
@@ -1,8 +1,8 @@
package com.seaofnodes.simple.node;
-import com.seaofnodes.simple.Utils;
import com.seaofnodes.simple.type.*;
+import com.seaofnodes.simple.util.Utils;
public class NotNode extends Node {
public NotNode(Node in) { super(null, in); }
@@ -14,7 +14,7 @@ public class NotNode extends Node {
@Override
public TypeInteger compute() {
Type t0 = in(1)._type;
- if( t0.isHigh() ) return TypeInteger.BOOL.dual();
+ if( t0.isHigh() ) return (TypeInteger)TypeInteger.BOOL.dual();
if( t0 == Type.NIL || t0 == TypeInteger.ZERO ) return TypeInteger.TRUE;
if( t0 instanceof TypeNil tn && tn.notNull() ) return TypeInteger.FALSE;
if( t0 instanceof TypeInteger i && (i._min > 0 || i._max < 0) ) return TypeInteger.FALSE;
diff --git a/src/main/java/com/seaofnodes/simple/node/ParmNode.java b/src/main/java/com/seaofnodes/simple/node/ParmNode.java
index 3e8e3711..8b614624 100644
--- a/src/main/java/com/seaofnodes/simple/node/ParmNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/ParmNode.java
@@ -2,7 +2,6 @@ package com.seaofnodes.simple.node;
import com.seaofnodes.print.ExprPrinter;
-import com.seaofnodes.simple.Utils;
import com.seaofnodes.simple.type.Type;
public class ParmNode extends PhiNode {
@@ -14,7 +13,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; }
+ public ParmNode(ParmNode parm) { super(parm, parm._label, parm._minType ); _idx = parm._idx; }
@Override public String label() { return MemOpNode.mlabel(_label); }
@@ -30,14 +29,8 @@ public class ParmNode extends PhiNode {
}
- @Override
- public Node idealize() {
- if( inProgress() ) return null;
- return super.idealize();
- }
-
// Always in-progress until we run out of unknown callers
- @Override public boolean inProgress() { return fun().inProgress(); }
+ @Override public boolean inProgress() { return in(0) instanceof FunNode fun && fun.inProgress(); }
@Override public boolean eq( Node n ) {
return ((ParmNode)n)._idx==_idx && super.eq(n);
diff --git a/src/main/java/com/seaofnodes/simple/node/PhiNode.java b/src/main/java/com/seaofnodes/simple/node/PhiNode.java
index 8aa123f7..af5d900b 100644
--- a/src/main/java/com/seaofnodes/simple/node/PhiNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/PhiNode.java
@@ -3,24 +3,33 @@ package com.seaofnodes.simple.node;
import com.seaofnodes.print.ExprPrinter;
import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.codegen.CodeGen;
import com.seaofnodes.simple.type.*;
+import com.seaofnodes.simple.util.Utils;
public class PhiNode extends Node {
public final String _label;
- // The Phi type we compute must stay within the domain of the Phi.
- // Example Int stays Int, Ptr stays Ptr, Control stays Control, Mem stays Mem.
- 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; _type = _declaredType = declaredType; }
- public PhiNode(PhiNode phi) { super(phi); _label = phi._label; _declaredType = phi._declaredType; }
+ // The Phi type we compute must stay within the domain of the Phi. Example
+ // Int stays Int, Ptr stays Ptr, Control stays Control, Mem stays Mem.
+ Type _minType;
+ public PhiNode(String label, Type minType, Node... inputs) {
+ super(inputs);
+ _label = label;
+ assert minType!=null;
+ _minType = minType;
+ }
+ // Used by ParmNode
+ public PhiNode(PhiNode phi, String label, Type minType) { super(phi); _label = label; _type = _minType = minType; }
+ // Used by instruction Selection
+ public PhiNode(PhiNode phi) { this(phi,phi._label,phi._minType ); }
+ // Used by the infinite-loop exit breaker
public PhiNode(RegionNode r, Node sample) {
super(new Node[]{r});
_label = "";
- _declaredType = sample._type;
+ _minType = sample._type;
while( nIns() < r.nIns() )
addDef(sample);
}
@@ -41,21 +50,26 @@ public class PhiNode extends Node {
}
public CFGNode region() { return (CFGNode)in(0); }
- @Override public boolean isMem() { return _declaredType instanceof TypeMem; }
+ @Override public boolean isMem() { return _minType instanceof TypeMem; }
@Override
public Type compute() {
if( !(region() instanceof RegionNode r) )
return region()._type==Type.XCONTROL ? (_type instanceof TypeMem ? TypeMem.TOP : Type.TOP) : _type;
// During parsing Phis have to be computed type pessimistically.
- if( r.inProgress() ) return _declaredType;
+ if( r.inProgress() ) return _minType;
// Set type to local top of the starting type
- Type t = _declaredType.glb(false).dual();//Type.TOP;
+ //Type t = _minType.dual();
+ Type t = Type.TOP;
for (int i = 1; i < nIns(); i++)
// If the region's control input is live, add this as a dependency
// to the control because we can be peeped should it become dead.
- if( addDep(r.in(i))._type != Type.XCONTROL )
+ if( addDep(r.in(i))._type != Type.XCONTROL ) {
+ if( in(i)._type==Type.BOTTOM )
+ return Type.BOTTOM;
t = t.meet(in(i)._type);
+ }
+ t = t.join( _minType );
return t;
}
@@ -63,13 +77,27 @@ public class PhiNode extends Node {
public Node idealize() {
if( !(region() instanceof RegionNode r ) )
return in(1); // Input has collapse to e.g. starting control.
+ // Can upgrade minType even while in-progress
+ if( _minType instanceof TypeMemPtr tmp && _minType.isFRef() ) {
+ TypeMemPtr tmp2 = (TypeMemPtr)CodeGen.CODE.P.TYPES.get(tmp._obj._name);
+ if( tmp2!=null && tmp2._obj != tmp._obj ) {
+ _minType = tmp.makeFrom(tmp2._obj);
+ return this;
+ }
+ }
if( r.inProgress() || r.nIns()<=1 )
return null; // Input is in-progress
// If we have only a single unique input, become it.
Node live = singleUniqueInput();
- if (live != null)
- return live;
+ if( live != null ) {
+ if( live._type.isa(_type) )
+ return live;
+ // Let the bulk input split this alias before collapsing a precise Phi.
+ if( live instanceof BulkMemPhiNode ) return null;
+ // Keep the Phi upcast
+ return new CastNode(_type,null,live);
+ }
// No bother if region is going to fold dead paths soon
for( int i=1; i<nIns(); i++ )
@@ -165,6 +193,12 @@ public class PhiNode extends Node {
ins[i] = in(i).in(j);
t = t.meet(ins[i]._type);
}
+ // Not accepts both pointers and integers, but a Phi cannot mix them.
+ if( t==Type.BOTTOM ) {
+ for( int i=1; i<ins.length; i++ ) addDep(ins[i]);
+ cp.kill();
+ return null;
+ }
PhiNode phi = j==1 && op instanceof MemOpNode mem
? new MemPhiNode(_label,mem._alias,ins)
: PhiNode.make(_label,t.glb(false),ins);
@@ -213,9 +247,19 @@ public class PhiNode extends Node {
return in(nIns()-1) == null;
}
- // Never equal if inProgress
+ // Never equal if inProgress.
+ // Also, joins
@Override public boolean eq( Node n ) {
- return !inProgress();
+ if( inProgress() ) return false;
+ Type min = ((PhiNode)n)._minType;
+ if( _minType==min ) return true;
+ Type mt = min.meet(_minType);
+ if( min!=mt && _minType!=mt ) return false;
+ //// Theory says these 2 Phis CAN be merged/GVNd, but I need to pick the
+ //// most general minType.
+ //_minType = ((PhiNode)n)._minType = mt;
+ //return true;
+ return false;
}
@Override
diff --git a/src/main/java/com/seaofnodes/simple/node/ReadOnlyMach.java b/src/main/java/com/seaofnodes/simple/node/ReadOnlyMach.java
index 86d17f9b..a35f5a1d 100644
--- a/src/main/java/com/seaofnodes/simple/node/ReadOnlyMach.java
+++ b/src/main/java/com/seaofnodes/simple/node/ReadOnlyMach.java
@@ -1,9 +1,9 @@
package com.seaofnodes.simple.node;
-import com.seaofnodes.simple.SB;
import com.seaofnodes.simple.codegen.CodeGen;
import com.seaofnodes.simple.codegen.Encoding;
import com.seaofnodes.simple.codegen.RegMask;
+import com.seaofnodes.simple.util.SB;
/**
* Cast a pointer to read-only
diff --git a/src/main/java/com/seaofnodes/simple/node/RegionNode.java b/src/main/java/com/seaofnodes/simple/node/RegionNode.java
index 79b7f53a..72f0df7e 100644
--- a/src/main/java/com/seaofnodes/simple/node/RegionNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/RegionNode.java
@@ -46,7 +46,7 @@ public class RegionNode extends CFGNode {
return in(1); // Collapse if no Phis; 1-input Phis will collapse on their own
// If a CFG diamond with no merging, delete: "if( pred ) {} else {};"
- if( !hasPhi() && // No Phi users, just a control user
+ if( !hasPhi() && nIns()>3 && // No Phi users, just a control user
in(1) instanceof CProjNode p1 &&
in(2) instanceof CProjNode p2 &&
addDepForwards(p1).in(0)==addDepForwards(p2).in(0) &&
@@ -58,8 +58,8 @@ public class RegionNode extends CFGNode {
return delDef(2);
}
- // Flatten stack regions (no loops involved)
- if( getClass() == RegionNode.class ) {
+ // Flatten stacked regions (no loops involved)
+ if( getClass() == RegionNode.class && nIns()>2 ) {
for( int i=1; i<nIns(); i++ )
if( cfg(i) instanceof RegionNode region && region.getClass() == RegionNode.class && region.nIns()>2 && !hasMidUser(region) ) {
assert !region.inProgress();
@@ -131,7 +131,7 @@ public class RegionNode extends CFGNode {
if( use instanceof PhiNode ) {
for( Node data : use._outputs ) {
if( !(data instanceof PhiNode phi2) || phi2.region()!=this )
- { addDep(use); addDep(data); return true; }
+ { addDep(use); if( data!=null ) addDep(data); return true; }
}
} else
{ addDep(use); return true; } // Control user
diff --git a/src/main/java/com/seaofnodes/simple/node/ReturnNode.java b/src/main/java/com/seaofnodes/simple/node/ReturnNode.java
index e0ab863a..6d268994 100644
--- a/src/main/java/com/seaofnodes/simple/node/ReturnNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/ReturnNode.java
@@ -3,12 +3,12 @@ package com.seaofnodes.simple.node;
import com.seaofnodes.print.ExprPrinter;
import com.seaofnodes.simple.Parser;
-import com.seaofnodes.simple.SB;
-import com.seaofnodes.simple.Utils;
import com.seaofnodes.simple.codegen.CodeGen;
-import com.seaofnodes.simple.codegen.RegMask;
import com.seaofnodes.simple.codegen.Encoding;
+import com.seaofnodes.simple.codegen.RegMask;
import com.seaofnodes.simple.type.*;
+import com.seaofnodes.simple.util.SB;
+import com.seaofnodes.simple.util.Utils;
/** Keeps all preceding memory effects and the return value alive: {ctrl, $mem, value, rpc}. */
public class ReturnNode extends CFGNode {
@@ -55,11 +55,10 @@ public class ReturnNode extends CFGNode {
// Upgrade signature based on return type
Type ret = expr()._type;
TypeFunPtr fcn = _fun.sig();
- assert ret.isa(fcn.ret());
- if( ret != fcn.ret() )
+ if( ret != fcn.ret() && ret.isa(fcn.ret()) )
_fun.setSig(fcn.makeFrom(ret));
- // If dead (cant be reached; infinite loop), kill the exit values
+ // If dead (cannot be reached; infinite loop), kill the exit values
if( ctrl()._type==Type.XCONTROL &&
!(mem() instanceof ConstantNode && expr() instanceof ConstantNode) ) {
Node top = new ConstantNode(Type.TOP).peephole();
@@ -92,8 +91,8 @@ public class ReturnNode extends CFGNode {
// Merge path into the One True Return
RegionNode r = (RegionNode)ctrl();
// Assert that the Phis are in particular outputs; not reordered or shuffled
- PhiNode mem = (PhiNode)r.out(0); assert mem._declaredType == TypeMem.BOT;
- PhiNode rez = (PhiNode)r.out(1); assert rez._declaredType == Type.BOTTOM;
+ PhiNode mem = (PhiNode)r.out(0); assert mem._minType == TypeMem.BOT;
+ PhiNode rez = (PhiNode)r.out(1); assert rez._minType == Type.BOTTOM;
// Pop "inProgress" null off
r ._inputs.pop();
mem._inputs.pop();
diff --git a/src/main/java/com/seaofnodes/simple/node/ScopeNode.java b/src/main/java/com/seaofnodes/simple/node/ScopeNode.java
index 44f0bc05..307fc0f9 100644
--- a/src/main/java/com/seaofnodes/simple/node/ScopeNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/ScopeNode.java
@@ -2,9 +2,14 @@ package com.seaofnodes.simple.node;
import com.seaofnodes.print.ExprPrinter;
-import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.Parser;
+import com.seaofnodes.simple.Var;
import com.seaofnodes.simple.codegen.CodeGen;
import com.seaofnodes.simple.type.*;
+import com.seaofnodes.simple.util.Ary;
+import com.seaofnodes.simple.util.AryInt;
+import com.seaofnodes.simple.util.Utils;
+
import java.util.*;
/**
@@ -29,15 +34,22 @@ public class ScopeNode extends Node {
// All active/live variables in all nested scopes, all run together
public final Ary<Var> _vars;
- // Size of each nested lexical scope
- public final Ary<Integer> _lexSize;
-
// Lexical scope is typed:
- // - Block scope - basic block scoping
- // - Constructor - struct type is defined, and here we give values to all fields
- // - Function - out-of-scope lookups restricted to final constants
- public enum Kind { Block, Constructor, Function };
+ public static class Kind {
+ // Basic block scoping
+ public static class Block extends Kind { }
+ // Function scope
+ public static class Func extends Kind { }
+ // Struct definition, mapping types to fields
+ public static class Define extends Kind { public Define(TypeMemPtr tmp) { _tmp=tmp; } public final TypeMemPtr _tmp; }
+ // Struct allocation, mapping values to fields
+ public static class Alloc extends Kind { public Alloc (TypeMemPtr tmp) { _tmp=tmp; } public final TypeMemPtr _tmp; }
+
+ public int _lexSize; // Number of Vars in this scope
+ }
public final Ary<Kind> _kinds;
+ // Lexical scope nesting depth
+ public int depth() { return _kinds._len; }
// Extra guards; tested predicates and casted results
private final Ary<Node> _guards;
@@ -46,7 +58,6 @@ public class ScopeNode extends Node {
public ScopeNode() {
_type = Type.BOTTOM;
_vars = new Ary<>(Var .class);
- _lexSize= new Ary<>(Integer.class);
_kinds = new Ary<>(Kind .class);
_guards = new Ary<>(Node .class);
}
@@ -58,9 +69,9 @@ public class ScopeNode extends Node {
p.p("Scope[ ");
int j=1;
for( int i=0; i<nIns(); i++ ) {
- if( j < _lexSize._len && i == _lexSize.at(j) ) { p.p("| "); j++; }
+ if( j < depth() && i == _kinds.at(j)._lexSize ) { p.p("| "); j++; }
Var v = _vars.at(i);
- p.p(v._type().print(new SB()));
+ p.p(v._type());
p.p(" ");
if( v._final ) p.p("!");
p.p(v._name);
@@ -95,17 +106,14 @@ public class ScopeNode extends Node {
public Node mem(Node n) { update(MEM0,n); return n; }
public void push( Kind kind ) {
- assert _lexSize._len==_kinds._len;
- _lexSize.push(_vars.size());
- _kinds .push(kind);
+ kind._lexSize = _vars.size();
+ _kinds.push(kind);
}
// Pop a lexical scope
public void pop() {
- assert _lexSize._len==_kinds._len;
promote(); // Promote forward references to the next outer scope
- int n = _lexSize.pop();
- _kinds.pop();
+ int n = _kinds.pop()._lexSize;
popUntil(n); // Pop off inputs going out of scope
_vars.setLen(n); // Pop off variables going out of scope
}
@@ -114,17 +122,17 @@ public class ScopeNode extends Node {
// Look for forward references in the last lexical scope and promote to the
// next outer lexical scope. At the last scope declare them an error.
public void promote() {
- int n = _lexSize.last();
+ Kind kind = _kinds.last();
+ int n = kind._lexSize;
for( int i=n; i<nIns(); i++ ) {
Var v = var(i);
if( !v.isFRef() ) continue;
- if( _lexSize._len==1 )
+ if( depth()==1 )
throw Parser.error("Undefined name '" + v._name + "'",v._loc);
- _vars.swap(n,i);
+ _vars .swap(n,i);
_inputs.swap(n,i);
v._idx = n;
- n++;
- _lexSize.set(_lexSize._len-1,n);
+ kind._lexSize = ++n;
}
}
@@ -133,19 +141,46 @@ public class ScopeNode extends Node {
// block. Nested blocks retain that permission; surrounding bindings do not.
public boolean canInit(Var v) {
for( int i=_kinds._len-1; i>0; i-- )
- if( _kinds.at(i)==Kind.Constructor )
- return v._idx >= _lexSize.at(i-1) && v._idx < _lexSize.at(i);
+ if( _kinds.at(i) instanceof Kind.Alloc )
+ return v._idx >= _kinds.at(i-1)._lexSize && v._idx < _kinds.at(i)._lexSize;
return false;
}
- public boolean inConstructor() { return _kinds.last() == Kind.Constructor; }
+ public boolean inConstructor() { return _kinds.last() instanceof Kind.Define; }
+ public boolean inAllocation () { return _kinds.last() instanceof Kind.Alloc ; }
+ public boolean inFunction () { return _kinds.last() instanceof Kind.Func ; }
- // Is v outside any current function scope?
- public boolean outOfFunction( Var v ) {
- for( int i=_lexSize._len-1; i>=0 && v._idx<_lexSize.at(i); i-- )
- if( _kinds.at(i)==Kind.Function )
- return true;
- return false;
+ public Kind kind( Var v ) {
+ for( int i=depth()-1; i>=0; i-- )
+ if( v._idx >= _kinds.at(i)._lexSize )
+ return _kinds.at(i);
+ throw Utils.TODO();
+ }
+
+ // Return the kind for the defining scope, or a function scope if found
+ // first. Block scopes means the variable can be r/w directly in the
+ // scope, Function scopes require final constants (no capture), and
+ // Constructor scopes mean an instance variable reference.
+
+ // Walk up-lexical scope looking for defining lexical Kind.
+ // - Skip any amount of nested block scopes
+ // - Walking out of a function requires v be a final constant (for now) -
+ // and we can stop walking.
+
+ // - Walking out of a method and (skipping Blocks) into the matching struct
+ // is OK, and this is a instance var load.
+
+ // - Walking out of a method and into the wrong struct is an error;
+ // including nested structs containing (no nested classes yet)
+
+ // Returns error, forward-ref, ok containing struct, ok containing function/block.
+
+ public String outOfFunction( Var v ) {
+ //if( v==null ) return null; // Prolly forward reference
+ //int i; for( i=_lexSize._len-1; i>=0 && v._idx<_lexSize.at(i); i-- )
+ // if( _kinds.at(i)=="{->}" ) return "{->}";
+ //return _kinds.at(i);
+ throw Utils.TODO();
}
@@ -163,9 +198,9 @@ public class ScopeNode extends Node {
* Create a new variable name in the current scope
*/
public boolean define( String name, Type declaredType, boolean xfinal, Node init, Parser.Lexer loc ) {
- assert _lexSize.isEmpty() || name.charAt(0)!='$' ; // Later scopes do not define memory
- if( _lexSize._len > 0 )
- for( int i=_vars.size()-1; i>=_lexSize.last(); i-- ) {
+ assert _kinds.isEmpty() || name.charAt(0)!='$' ; // Later scopes do not define memory
+ if( depth() > 0 )
+ for( int i=_vars.size()-1; i>=_kinds.last()._lexSize; i-- ) {
Var n = var(i);
if( n._name.equals(name) ) {
if( !n.isFRef() ) return false; // Double define
@@ -222,7 +257,7 @@ public class ScopeNode extends Node {
: loop.setDef(v._idx,PhiNode.make(v._name, v.type(), loop.ctrl(), loop.in(loop.update(v,null)._idx),null).peephole());
setDef(v._idx,old);
}
- assert !v._final || st==null || canInit(v);
+ //assert !v._final || st==null;
if( st!=null ) setDef(v._idx,st); // Set new value
return v;
}
@@ -248,7 +283,6 @@ public class ScopeNode extends Node {
// 2) Make the new ScopeNode a user of all the nodes bound
// 3) Ensure that the order of defs is the same to allow easy merging
dup._vars .addAll(_vars );
- dup._lexSize.addAll(_lexSize);
dup._kinds .addAll(_kinds );
dup._guards .addAll(_guards );
// The dup'd guards all need dup'd keepers, to keep proper accounting
@@ -366,13 +400,28 @@ public class ScopeNode extends Node {
private void _addGuards( Node ctrl, Node pred, boolean invert ) {
if( pred==null || pred.isDead() )
return;
- // A single negation is handled below. For !!p (including p != null),
- // also refine the underlying p without changing the Boolean result.
- if( pred instanceof NotNode not && not.in(1) instanceof NotNode ) {
+ if( !availableAt(pred,(CFGNode)ctrl,new BitSet()) )
+ return;
+ // A single negation is handled below. For !!p (including p != null)
+ // or a negated short-circuit Phi, also discover the underlying facts.
+ if( pred instanceof NotNode not &&
+ (not.in(1) instanceof NotNode || not.in(1) instanceof PhiNode) ) {
pred.keep(); // Recursive peepholes may rediscover this Not.
_addGuards(ctrl,not.in(1),!invert);
pred.unkeep();
}
+ // A false || or true && proves both operands. Only guard values
+ // available at this control, not ones confined to the RHS branch.
+ if( pred instanceof PhiNode phi && phi.nIns()==3 && phi.region() instanceof RegionNode r && r.nIns()==3 )
+ for( int i=1; i<3; i++ )
+ if( r.in(i) instanceof CProjNode prj && prj.ctrl() instanceof IfNode iff ) {
+ Node skip = shortCircuitSkippedPred(phi.in(i),iff.pred(),prj,invert);
+ if( skip != null ) {
+ _addGuards(ctrl,skip,invert);
+ _addGuards(ctrl,phi.in(3-i),invert);
+ break;
+ }
+ }
// Invert the If conditional
if( invert )
pred = pred instanceof NotNode not ? not.in(1) : CodeGen.CODE.add(new NotNode(pred).peephole());
@@ -390,6 +439,35 @@ public class ScopeNode extends Node {
}
}
+ // A Phi's value is available at its Region. Other data nodes require all
+ // their inputs, including pinned control, to dominate the proposed guard.
+ private static boolean availableAt(Node n, CFGNode ctrl, BitSet visit) {
+ if( n==null || n instanceof ConstantNode || visit.get(n._nid) ) return true;
+ visit.set(n._nid);
+ if( n instanceof PhiNode phi ) n = phi.region();
+ if( n instanceof CFGNode cfg ) {
+ for( CFGNode dom=ctrl; dom!=null; dom=dom.idom() )
+ if( dom==cfg ) return true;
+ return false;
+ }
+ for( Node def : n._inputs )
+ if( !availableAt(def,ctrl,visit) ) return false;
+ return true;
+ }
+
+ private static Node shortCircuitSkippedPred( Node val, Node pred, CProjNode prj, boolean invert ) {
+ if( val == null ) return null;
+ if( val == pred )
+ return (prj._idx==0)==invert ? val : null;
+ if( val instanceof NotNode not && not.in(1)==pred )
+ return (prj._idx==1)==invert ? val : null;
+ if( val._type == TypeInteger.TRUE && invert )
+ return prj._idx==0 ? pred : CodeGen.CODE.add(new NotNode(pred).peephole());
+ if( val._type == TypeInteger.FALSE && !invert )
+ return prj._idx==1 ? pred : CodeGen.CODE.add(new NotNode(pred).peephole());
+ return null;
+ }
+
private void _addGuard(Type guard, Node ctrl, Node pred) {
Type tcast = guard.join(pred._type);
if( tcast != pred._type && !tcast.isHigh() ) {
diff --git a/src/main/java/com/seaofnodes/simple/node/SplitNode.java b/src/main/java/com/seaofnodes/simple/node/SplitNode.java
index ff18b292..211489fb 100644
--- a/src/main/java/com/seaofnodes/simple/node/SplitNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/SplitNode.java
@@ -2,9 +2,9 @@ package com.seaofnodes.simple.node;
import com.seaofnodes.print.ExprPrinter;
-import com.seaofnodes.simple.SB;
import com.seaofnodes.simple.codegen.CodeGen;
import com.seaofnodes.simple.type.Type;
+import com.seaofnodes.simple.util.SB;
public abstract class SplitNode extends MachConcreteNode {
public final String _kind; // Kind of split
diff --git a/src/main/java/com/seaofnodes/simple/node/StartNode.java b/src/main/java/com/seaofnodes/simple/node/StartNode.java
index cbaefa6a..e13d35c0 100644
--- a/src/main/java/com/seaofnodes/simple/node/StartNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/StartNode.java
@@ -6,14 +6,14 @@ import com.seaofnodes.simple.codegen.CodeGen;
import com.seaofnodes.simple.Parser;
import com.seaofnodes.simple.type.*;
import java.util.HashSet;
-import static com.seaofnodes.simple.Utils.TODO;
+import static com.seaofnodes.simple.util.Utils.TODO;
/** Start supplies control, whole memory, and the argument, in that order. */
public class StartNode extends LoopNode implements MultiNode {
final Type _arg;
- public StartNode(Type arg) { super((Parser.Lexer)null,null); _arg = arg; _type = compute(); }
+ public StartNode(Type arg) { super(null,null); _arg = arg; _type = compute(); }
public StartNode(StartNode start) { super(start); _arg = start==null ? null : start._arg; }
@Override public String label() { return "Start"; }
@@ -22,13 +22,12 @@ public class StartNode extends LoopNode implements MultiNode {
return p.p(label());
}
- @Override public boolean blockHead() { return true; }
- @Override public CFGNode cfg0() { return null; }
+ @Override public CFGNode cfg0() { return null; }
// Get the one control following; error to call with more than one e.g. an
// IfNode or other multi-way branch. For Start, its "main"
@Override public CFGNode uctrl() {
- // Find "main", its the start.
+ // Find "main", it's the start.
CFGNode C = null;
for( Node use : _outputs )
if( use instanceof FunNode fun && fun.sig().isa(CodeGen.CODE._main) )
diff --git a/src/main/java/com/seaofnodes/simple/node/StopNode.java b/src/main/java/com/seaofnodes/simple/node/StopNode.java
index 298c24a9..9f7a925b 100644
--- a/src/main/java/com/seaofnodes/simple/node/StopNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/StopNode.java
@@ -2,8 +2,8 @@ package com.seaofnodes.simple.node;
import com.seaofnodes.print.ExprPrinter;
-import com.seaofnodes.simple.Utils;
import com.seaofnodes.simple.type.Type;
+import com.seaofnodes.simple.util.Utils;
public class StopNode extends CFGNode {
diff --git a/src/main/java/com/seaofnodes/simple/node/StoreNode.java b/src/main/java/com/seaofnodes/simple/node/StoreNode.java
index ecda014b..603a0607 100644
--- a/src/main/java/com/seaofnodes/simple/node/StoreNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/StoreNode.java
@@ -44,8 +44,7 @@ public class StoreNode extends MemOpNode {
@Override
public Type compute() {
Type val = val()._type;
- if( _declaredType.isFRef() && val instanceof TypeMemPtr tmp && !tmp.isFRef() )
- _declaredType = tmp;
+ assert !_declaredType.isFRef();
Type t = val.join(_declaredType).meet(MemMergeNode.contents(mem(),_alias,this));
return TypeMem.make(_alias,t);
}
@@ -58,6 +57,11 @@ public class StoreNode extends MemOpNode {
}
+ if( mem() instanceof CastNode cast ) {
+ setDef(1,cast.in(1));
+ return this;
+ }
+
// Simple store-after-store on same address. Should pick up the
// required init-store being stomped by a first user store.
if( mem() instanceof StoreNode st &&
diff --git a/src/main/java/com/seaofnodes/simple/node/StructNode.java b/src/main/java/com/seaofnodes/simple/node/StructNode.java
index 3782d64a..78b20e9f 100644
--- a/src/main/java/com/seaofnodes/simple/node/StructNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/StructNode.java
@@ -2,15 +2,16 @@ package com.seaofnodes.simple.node;
import com.seaofnodes.print.ExprPrinter;
-import com.seaofnodes.simple.SB;
import com.seaofnodes.simple.type.*;
+import com.seaofnodes.simple.util.SB;
/**
* Build a compound object
*/
public class StructNode extends Node {
- public TypeStruct _ts;
+ public final TypeStruct _ts;
+ public StructNode(TypeStruct ts) { _ts=ts; assert !ts._open; }
@Override public String label() { return _ts==null ? "STRUCT?" : _ts.str(); }
@@ -20,7 +21,7 @@ public class StructNode extends Node {
p.p(_ts._name).p(" {");
for( int i=0; i<nIns(); i++ ) {
p.p(_ts._fields[i]._fname).p(":");
- p.p((in(i)==null ? Type.BOTTOM : in(i)._type).print(new SB()));
+ p.p(in(i)==null ? Type.BOTTOM : in(i)._type);
p.p("; ");
}
return p.unchar("; ").p("}");
@@ -32,7 +33,7 @@ public class StructNode extends Node {
Field[] fs = new Field[_ts._fields.length];
for( int i=0; i<fs.length; i++ )
fs[i] = _ts._fields[i].makeFrom(in(i)==null ? Type.TOP : in(i)._type);
- return TypeStruct.make(_ts._name,fs);
+ return TypeStruct.make(_ts._name,false,fs);
}
@Override
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/AddARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/AddARM.java
index 76e7b9b5..76e47ca2 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/AddARM.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/AddARM.java
@@ -1,9 +1,9 @@
package com.seaofnodes.simple.node.cpus.arm;
import com.seaofnodes.isa.Arm64;
-import com.seaofnodes.simple.*;
import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.util.SB;
public class AddARM extends MachConcreteNode implements MachNode {
AddARM( Node add) { super(add); }
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/AddFARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/AddFARM.java
index 9c2f64c0..67a10b48 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/AddFARM.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/AddFARM.java
@@ -1,9 +1,9 @@
package com.seaofnodes.simple.node.cpus.arm;
import com.seaofnodes.isa.Arm64;
-import com.seaofnodes.simple.SB;
import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.util.SB;
public class AddFARM extends MachConcreteNode implements MachNode {
AddFARM(Node addf) { super(addf); }
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/AddIARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/AddIARM.java
index 52cf36ea..4c635d07 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/AddIARM.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/AddIARM.java
@@ -1,7 +1,7 @@
package com.seaofnodes.simple.node.cpus.arm;
import com.seaofnodes.isa.Arm64;
-import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.util.SB;
import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.*;
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/AndARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/AndARM.java
index 40812d07..1a0365ad 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/AndARM.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/AndARM.java
@@ -1,11 +1,11 @@
package com.seaofnodes.simple.node.cpus.arm;
import com.seaofnodes.isa.Arm64;
-import com.seaofnodes.simple.SB;
import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.MachConcreteNode;
import com.seaofnodes.simple.node.MachNode;
import com.seaofnodes.simple.node.Node;
+import com.seaofnodes.simple.util.SB;
public class AndARM extends MachConcreteNode implements MachNode {
AndARM(Node and) { super(and); }
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/AndIARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/AndIARM.java
index 82d553aa..6292e98e 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/AndIARM.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/AndIARM.java
@@ -1,9 +1,9 @@
package com.seaofnodes.simple.node.cpus.arm;
import com.seaofnodes.isa.Arm64;
-import com.seaofnodes.simple.*;
import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.util.SB;
public class AndIARM extends MachConcreteNode implements MachNode {
final int _imm;
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/AsrARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/AsrARM.java
index cce868f9..77f14d4c 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/AsrARM.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/AsrARM.java
@@ -1,9 +1,9 @@
package com.seaofnodes.simple.node.cpus.arm;
import com.seaofnodes.isa.Arm64;
-import com.seaofnodes.simple.*;
import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.util.SB;
// Arithmetic Shift Right (register)
public class AsrARM extends MachConcreteNode implements MachNode {
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/AsrIARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/AsrIARM.java
index 2d50e967..7cb42eec 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/AsrIARM.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/AsrIARM.java
@@ -1,13 +1,13 @@
package com.seaofnodes.simple.node.cpus.arm;
import com.seaofnodes.isa.Arm64;
-import com.seaofnodes.simple.SB;
import com.seaofnodes.simple.codegen.CodeGen;
import com.seaofnodes.simple.codegen.Encoding;
import com.seaofnodes.simple.codegen.RegMask;
import com.seaofnodes.simple.node.MachConcreteNode;
import com.seaofnodes.simple.node.MachNode;
import com.seaofnodes.simple.node.Node;
+import com.seaofnodes.simple.util.SB;
public class AsrIARM extends MachConcreteNode implements MachNode {
final int _imm;
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/BranchARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/BranchARM.java
index 5a660c90..127f17ff 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/BranchARM.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/BranchARM.java
@@ -1,9 +1,10 @@
package com.seaofnodes.simple.node.cpus.arm;
import com.seaofnodes.isa.Arm64;
-import com.seaofnodes.simple.*;
import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.util.SB;
+import com.seaofnodes.simple.util.Utils;
// Jump on flags, uses flags
public class BranchARM extends IfNode implements MachNode, RIPRelSize {
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/CallARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/CallARM.java
index dd2d9e2c..e1717cb3 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/CallARM.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/CallARM.java
@@ -1,10 +1,10 @@
package com.seaofnodes.simple.node.cpus.arm;
import com.seaofnodes.isa.Arm64;
-import com.seaofnodes.simple.*;
import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.*;
import com.seaofnodes.simple.type.TypeFunPtr;
+import com.seaofnodes.simple.util.SB;
public class CallARM extends CallNode implements MachNode, RIPRelSize {
final TypeFunPtr _tfp;
@@ -43,9 +43,9 @@ public class CallARM extends CallNode implements MachNode, RIPRelSize {
}
@Override public void asm(CodeGen code, SB sb) {
- sb.p(_name);
+ sb.p(_name).p(" ");
for( int i=0; i<nargs(); i++ )
- sb.p(code.reg(arg(i+2))).p(" ");
+ sb.p(code.reg(arg(i+2))).p(", ");
sb.unchar(2);
}
}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/CallRRARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/CallRRARM.java
index fa0fbd81..1949ae46 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/CallRRARM.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/CallRRARM.java
@@ -1,9 +1,9 @@
package com.seaofnodes.simple.node.cpus.arm;
import com.seaofnodes.isa.Arm64;
-import com.seaofnodes.simple.*;
import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.util.SB;
public class CallRRARM extends CallNode implements MachNode {
CallRRARM(CallNode call) { super(call); }
@@ -26,7 +26,7 @@ public class CallRRARM extends CallNode implements MachNode {
@Override public void asm(CodeGen code, SB sb) {
sb.p(code.reg(fptr())).p(" ");
for( int i=0; i<nargs(); i++ )
- sb.p(code.reg(arg(i))).p(" ");
+ sb.p(code.reg(arg(i))).p(", ");
sb.unchar(2);
}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/CmpARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/CmpARM.java
index ec5e8ff0..1bd9c372 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/CmpARM.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/CmpARM.java
@@ -1,9 +1,9 @@
package com.seaofnodes.simple.node.cpus.arm;
import com.seaofnodes.isa.Arm64;
-import com.seaofnodes.simple.SB;
import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.util.SB;
public class CmpARM extends MachConcreteNode implements MachNode {
CmpARM( Node cmp ) { super(cmp); }
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/CmpFARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/CmpFARM.java
index e82a2ee3..67ffc27b 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/CmpFARM.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/CmpFARM.java
@@ -1,8 +1,8 @@
package com.seaofnodes.simple.node.cpus.arm;
-import com.seaofnodes.simple.*;
import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.util.SB;
// compare instruction on float regs input(D-registers)
public class CmpFARM extends MachConcreteNode implements MachNode{
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/CmpIARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/CmpIARM.java
index dbf9c2e3..cd4dd9cf 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/CmpIARM.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/CmpIARM.java
@@ -1,9 +1,9 @@
package com.seaofnodes.simple.node.cpus.arm;
import com.seaofnodes.isa.Arm64;
-import com.seaofnodes.simple.*;
import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.util.SB;
// Compare with immediate.
// CMP (immediate) - SUBS
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/DivARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/DivARM.java
index 91bd347f..e94b6491 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/DivARM.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/DivARM.java
@@ -1,9 +1,9 @@
package com.seaofnodes.simple.node.cpus.arm;
import com.seaofnodes.isa.Arm64;
-import com.seaofnodes.simple.*;
import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.util.SB;
public class DivARM extends MachConcreteNode implements MachNode {
DivARM( Node div ) { super(div); }
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/DivFARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/DivFARM.java
index 6b86e722..68ad6047 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/DivFARM.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/DivFARM.java
@@ -1,9 +1,9 @@
package com.seaofnodes.simple.node.cpus.arm;
import com.seaofnodes.isa.Arm64;
-import com.seaofnodes.simple.*;
import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.util.SB;
public class DivFARM extends MachConcreteNode implements MachNode {
DivFARM( Node divf) { super(divf); }
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/ExtendARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/ExtendARM.java
index 6fec7a77..814a82c2 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/ExtendARM.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/ExtendARM.java
@@ -4,7 +4,7 @@ import com.seaofnodes.isa.Arm64;
import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.*;
import com.seaofnodes.simple.type.TypeInteger;
-import com.seaofnodes.simple.SB;
+import com.seaofnodes.simple.util.SB;
// Convert a narrow C return register to Simple's full-width integer value.
public class ExtendARM extends MachConcreteNode {
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/F8ToIARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/F8ToIARM.java
index 098c1353..31dcfc36 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/F8ToIARM.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/F8ToIARM.java
@@ -1,9 +1,9 @@
package com.seaofnodes.simple.node.cpus.arm;
import com.seaofnodes.isa.Arm64;
-import com.seaofnodes.simple.*;
import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.util.SB;
public class F8ToIARM extends MachConcreteNode implements MachNode {
F8ToIARM(Node f8toi) { super(f8toi); }
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/FloatARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/FloatARM.java
index 6bc2fe08..dc21e075 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/FloatARM.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/FloatARM.java
@@ -1,12 +1,12 @@
package com.seaofnodes.simple.node.cpus.arm;
import com.seaofnodes.isa.Arm64;
-import com.seaofnodes.simple.SB;
-import com.seaofnodes.simple.Utils;
import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.ConstantNode;
import com.seaofnodes.simple.node.MachNode;
import com.seaofnodes.simple.type.TypeFloat;
+import com.seaofnodes.simple.util.SB;
+import com.seaofnodes.simple.util.Utils;
//FMOV (scalar, immediate)
//Floating-point move immediate.
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/FunARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/FunARM.java
index c49875f4..17cafcde 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/FunARM.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/FunARM.java
@@ -1,11 +1,11 @@
package com.seaofnodes.simple.node.cpus.arm;
import com.seaofnodes.isa.Arm64;
-import com.seaofnodes.simple.SB;
-import com.seaofnodes.simple.Utils;
import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.FunNode;
import com.seaofnodes.simple.node.MachNode;
+import com.seaofnodes.simple.util.SB;
+import com.seaofnodes.simple.util.Utils;
public class FunARM extends FunNode implements MachNode {
FunARM(FunNode fun) { super(fun); }
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/I2F8ARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/I2F8ARM.java
index a7fa7a6a..b1c30afc 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/I2F8ARM.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/I2F8ARM.java
@@ -1,9 +1,9 @@
package com.seaofnodes.simple.node.cpus.arm;
import com.seaofnodes.isa.Arm64;
-import com.seaofnodes.simple.*;
import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.util.SB;
public class I2F8ARM extends MachConcreteNode implements MachNode {
I2F8ARM(Node i2f8) { super(i2f8); }
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/IntARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/IntARM.java
index 64d00d57..7a9e74c6 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/IntARM.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/IntARM.java
@@ -1,12 +1,12 @@
package com.seaofnodes.simple.node.cpus.arm;
import com.seaofnodes.isa.Arm64;
-import com.seaofnodes.simple.*;
import com.seaofnodes.simple.codegen.*;
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 com.seaofnodes.simple.util.SB;
// Integer constants
public class IntARM extends ConstantNode implements MachNode {
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/LoadARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/LoadARM.java
index 6fd4274a..8fc22a67 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/LoadARM.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/LoadARM.java
@@ -1,10 +1,10 @@
package com.seaofnodes.simple.node.cpus.arm;
import com.seaofnodes.isa.Arm64;
-import com.seaofnodes.simple.SB;
import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.LoadNode;
import com.seaofnodes.simple.node.Node;
+import com.seaofnodes.simple.util.SB;
// Load memory addressing on ARM
// Support imm, reg(direct), or reg+off(indirect) addressing
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/LslARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/LslARM.java
index 7624554e..954cfac2 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/LslARM.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/LslARM.java
@@ -1,9 +1,9 @@
package com.seaofnodes.simple.node.cpus.arm;
import com.seaofnodes.isa.Arm64;
-import com.seaofnodes.simple.*;
import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.util.SB;
// Logical Shift Left (register)
public class LslARM extends MachConcreteNode implements MachNode {
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/LslIARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/LslIARM.java
index bf394172..b32153ab 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/LslIARM.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/LslIARM.java
@@ -1,13 +1,13 @@
package com.seaofnodes.simple.node.cpus.arm;
import com.seaofnodes.isa.Arm64;
-import com.seaofnodes.simple.SB;
import com.seaofnodes.simple.codegen.CodeGen;
import com.seaofnodes.simple.codegen.Encoding;
import com.seaofnodes.simple.codegen.RegMask;
import com.seaofnodes.simple.node.MachConcreteNode;
import com.seaofnodes.simple.node.MachNode;
import com.seaofnodes.simple.node.Node;
+import com.seaofnodes.simple.util.SB;
public class LslIARM extends MachConcreteNode implements MachNode {
final int _imm;
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/LsrARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/LsrARM.java
index 2d5fa52c..91be34e3 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/LsrARM.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/LsrARM.java
@@ -1,9 +1,9 @@
package com.seaofnodes.simple.node.cpus.arm;
import com.seaofnodes.isa.Arm64;
-import com.seaofnodes.simple.*;
import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.util.SB;
// Logical Shift Right (register)
public class LsrARM extends MachConcreteNode implements MachNode {
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/LsrIARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/LsrIARM.java
index 6a12e4a5..243ce968 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/LsrIARM.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/LsrIARM.java
@@ -1,10 +1,9 @@
package com.seaofnodes.simple.node.cpus.arm;
import com.seaofnodes.isa.Arm64;
-
-import com.seaofnodes.simple.*;
import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.util.SB;
// logical right shift immediate
public class LsrIARM extends MachConcreteNode implements MachNode {
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/MemOpARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/MemOpARM.java
index 5e175b1e..dfbe5e06 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/MemOpARM.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/MemOpARM.java
@@ -3,14 +3,13 @@ package com.seaofnodes.simple.node.cpus.arm;
import com.seaofnodes.isa.Arm64;
import com.seaofnodes.print.ExprPrinter;
-import com.seaofnodes.simple.*;
import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.*;
import com.seaofnodes.simple.type.*;
-
+import com.seaofnodes.simple.util.SB;
+import com.seaofnodes.simple.util.Utils;
import java.lang.StringBuilder;
-
public abstract class MemOpARM extends MemOpNode implements MachNode {
final int _off; // Limit 9 bits sized, or (13 bits<<logsize) unsigned
final int _imm; // Limit ? bits
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/MulARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/MulARM.java
index a0955565..f598cefb 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/MulARM.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/MulARM.java
@@ -1,9 +1,9 @@
package com.seaofnodes.simple.node.cpus.arm;
import com.seaofnodes.isa.Arm64;
-import com.seaofnodes.simple.*;
import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.util.SB;
// mulh signed multiply instruction(no-imm form)
public class MulARM extends MachConcreteNode implements MachNode {
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/MulFARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/MulFARM.java
index 33d32bd4..a4f81fd4 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/MulFARM.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/MulFARM.java
@@ -1,9 +1,9 @@
package com.seaofnodes.simple.node.cpus.arm;
import com.seaofnodes.isa.Arm64;
-import com.seaofnodes.simple.*;
import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.util.SB;
public class MulFARM extends MachConcreteNode implements MachNode{
MulFARM(Node mulf) {super(mulf);}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/NegARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/NegARM.java
index b7c3c803..881b41f9 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/NegARM.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/NegARM.java
@@ -1,10 +1,10 @@
package com.seaofnodes.simple.node.cpus.arm;
-import com.seaofnodes.simple.*;
import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.*;
import com.seaofnodes.simple.node.MachConcreteNode;
import com.seaofnodes.simple.type.TypeInteger;
+import com.seaofnodes.simple.util.SB;
public class NegARM extends MachConcreteNode implements MachNode {
NegARM(Node sub) {
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/NewARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/NewARM.java
index 0dac086b..7110df62 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/NewARM.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/NewARM.java
@@ -1,9 +1,10 @@
package com.seaofnodes.simple.node.cpus.arm;
import com.seaofnodes.isa.Arm64;
-import com.seaofnodes.simple.*;
import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.util.SB;
+import com.seaofnodes.simple.util.Utils;
public class NewARM extends NewNode implements MachNode, RIPRelSize {
// A pre-zeroed chunk of memory.
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/NotARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/NotARM.java
index ec017032..f8e450f6 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/NotARM.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/NotARM.java
@@ -1,11 +1,10 @@
package com.seaofnodes.simple.node.cpus.arm;
import com.seaofnodes.isa.Arm64;
-import com.seaofnodes.simple.*;
import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.*;
-
import com.seaofnodes.simple.node.MachConcreteNode;
+import com.seaofnodes.simple.util.SB;
public class NotARM extends MachConcreteNode implements MachNode{
NotARM(NotNode not) {super(not);}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/OrARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/OrARM.java
index ba3197c1..5b7264ec 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/OrARM.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/OrARM.java
@@ -1,9 +1,9 @@
package com.seaofnodes.simple.node.cpus.arm;
import com.seaofnodes.isa.Arm64;
-import com.seaofnodes.simple.SB;
import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.util.SB;
// Not using the ORRS variant.
public class OrARM extends MachConcreteNode implements MachNode {
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/OrIARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/OrIARM.java
index 9c7cb8cc..86d07bee 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/OrIARM.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/OrIARM.java
@@ -1,9 +1,9 @@
package com.seaofnodes.simple.node.cpus.arm;
import com.seaofnodes.isa.Arm64;
-import com.seaofnodes.simple.*;
import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.util.SB;
public class OrIARM extends MachConcreteNode implements MachNode {
final int _imm;
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/SetARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/SetARM.java
index 6809bb1c..1cda4787 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/SetARM.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/SetARM.java
@@ -1,9 +1,9 @@
package com.seaofnodes.simple.node.cpus.arm;
import com.seaofnodes.isa.Arm64;
-import com.seaofnodes.simple.*;
import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.util.SB;
// conditional set?
// e.g CSET, stores flag info into GPRS after compare
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/SplitARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/SplitARM.java
index c0c60ea2..f93f0f24 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/SplitARM.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/SplitARM.java
@@ -1,13 +1,12 @@
package com.seaofnodes.simple.node.cpus.arm;
import com.seaofnodes.isa.Arm64;
-import com.seaofnodes.simple.SB;
-import com.seaofnodes.simple.Utils;
import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.FunNode;
import com.seaofnodes.simple.node.Node;
import com.seaofnodes.simple.node.SplitNode;
-import com.seaofnodes.simple.node.cpus.riscv.riscv;
+import com.seaofnodes.simple.util.SB;
+import com.seaofnodes.simple.util.Utils;
public class SplitARM extends SplitNode {
SplitARM(String kind, byte round) { super(kind,round,new Node[2]);}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/StoreARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/StoreARM.java
index cf858c36..2cc54f76 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/StoreARM.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/StoreARM.java
@@ -1,10 +1,10 @@
package com.seaofnodes.simple.node.cpus.arm;
import com.seaofnodes.isa.Arm64;
-import com.seaofnodes.simple.SB;
import com.seaofnodes.simple.codegen.*;
-import com.seaofnodes.simple.node.StoreNode;
import com.seaofnodes.simple.node.Node;
+import com.seaofnodes.simple.node.StoreNode;
+import com.seaofnodes.simple.util.SB;
// Store memory addressing on ARM
// Support imm, reg(direct), or reg+off(indirect) addressing
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/SubARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/SubARM.java
index 6024df68..fd5cd4de 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/SubARM.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/SubARM.java
@@ -1,9 +1,9 @@
package com.seaofnodes.simple.node.cpus.arm;
import com.seaofnodes.isa.Arm64;
-import com.seaofnodes.simple.SB;
import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.util.SB;
public class SubARM extends MachConcreteNode implements MachNode {
SubARM(Node sub) { super(sub); }
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/SubFARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/SubFARM.java
index dd147544..1a751434 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/SubFARM.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/SubFARM.java
@@ -1,9 +1,9 @@
package com.seaofnodes.simple.node.cpus.arm;
import com.seaofnodes.isa.Arm64;
-import com.seaofnodes.simple.SB;
import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.util.SB;
public class SubFARM extends MachConcreteNode implements MachNode {
SubFARM(Node subf) {super(subf);}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/SubIARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/SubIARM.java
index e45bfa63..b2c0b2ed 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/SubIARM.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/SubIARM.java
@@ -2,11 +2,10 @@ package com.seaofnodes.simple.node.cpus.arm;
import com.seaofnodes.isa.Arm64;
import com.seaofnodes.simple.codegen.*;
-import com.seaofnodes.simple.node.MachConcreteNode;
-
-import com.seaofnodes.simple.*;
import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.node.MachConcreteNode;
import com.seaofnodes.simple.type.TypeInteger;
+import com.seaofnodes.simple.util.SB;
public class SubIARM extends MachConcreteNode implements MachNode {
final int _imm;
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/TFPARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/TFPARM.java
index 720a14c1..98a56089 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/TFPARM.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/TFPARM.java
@@ -1,12 +1,13 @@
package com.seaofnodes.simple.node.cpus.arm;
import com.seaofnodes.isa.Arm64;
-import com.seaofnodes.simple.*;
import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.ConstantNode;
import com.seaofnodes.simple.node.MachNode;
import com.seaofnodes.simple.node.Node;
import com.seaofnodes.simple.type.TypeFunPtr;
+import com.seaofnodes.simple.util.SB;
+import com.seaofnodes.simple.util.Utils;
public class TFPARM extends ConstantNode implements MachNode, RIPRelSize {
TFPARM( ConstantNode con ) { super(con); }
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/TMPARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/TMPARM.java
index adbaa8c2..d1cafa96 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/TMPARM.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/TMPARM.java
@@ -1,12 +1,13 @@
package com.seaofnodes.simple.node.cpus.arm;
import com.seaofnodes.isa.Arm64;
-import com.seaofnodes.simple.*;
import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.ConstantNode;
import com.seaofnodes.simple.node.MachNode;
import com.seaofnodes.simple.node.Node;
import com.seaofnodes.simple.type.TypeMemPtr;
+import com.seaofnodes.simple.util.SB;
+import com.seaofnodes.simple.util.Utils;
public class TMPARM extends ConstantNode implements MachNode, RIPRelSize {
TMPARM( ConstantNode con ) { super(con); }
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/UJmpARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/UJmpARM.java
index acb32ddb..aecd02bb 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/UJmpARM.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/UJmpARM.java
@@ -2,10 +2,11 @@ package com.seaofnodes.simple.node.cpus.arm;
import com.seaofnodes.isa.Arm64;
-import com.seaofnodes.simple.*;
import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.*;
import com.seaofnodes.simple.type.Type;
+import com.seaofnodes.simple.util.SB;
+import com.seaofnodes.simple.util.Utils;
// unconditional jump
public class UJmpARM extends CFGNode implements MachNode, RIPRelSize {
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/XorARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/XorARM.java
index 5b635303..d56397a3 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/XorARM.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/XorARM.java
@@ -1,9 +1,9 @@
package com.seaofnodes.simple.node.cpus.arm;
import com.seaofnodes.isa.Arm64;
-import com.seaofnodes.simple.SB;
import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.util.SB;
public class XorARM extends MachConcreteNode implements MachNode{
XorARM(Node xor) {super(xor);}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/XorIARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/XorIARM.java
index 017815f3..12143ccd 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/XorIARM.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/XorIARM.java
@@ -1,12 +1,11 @@
package com.seaofnodes.simple.node.cpus.arm;
import com.seaofnodes.isa.Arm64;
-import com.seaofnodes.simple.node.MachConcreteNode;
-import com.seaofnodes.simple.node.MachNode;
-
-import com.seaofnodes.simple.*;
import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.node.MachConcreteNode;
+import com.seaofnodes.simple.node.MachNode;
+import com.seaofnodes.simple.util.SB;
public class XorIARM extends MachConcreteNode implements MachNode {
final int _imm;
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/arm.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/arm.java
index ddd9797b..9faf1d40 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/arm.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/arm.java
@@ -2,10 +2,10 @@ package com.seaofnodes.simple.node.cpus.arm;
import com.seaofnodes.isa.Arm64;
import static com.seaofnodes.isa.Arm64.*;
-import com.seaofnodes.simple.Utils;
import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.*;
import com.seaofnodes.simple.type.*;
+import com.seaofnodes.simple.util.Utils;
public class arm extends Machine {
public arm( CodeGen code ) {
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AUIPC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AUIPC.java
index fc96cbe9..14581111 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AUIPC.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AUIPC.java
@@ -1,11 +1,12 @@
package com.seaofnodes.simple.node.cpus.riscv;
import com.seaofnodes.isa.RiscV;
-import com.seaofnodes.simple.*;
import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.ConstantNode;
import com.seaofnodes.simple.node.MachNode;
import com.seaofnodes.simple.type.Type;
+import com.seaofnodes.simple.util.SB;
+import com.seaofnodes.simple.util.Utils;
// Add upper 20bits to PC. Immediate comes from the relocation info.
public class AUIPC extends ConstantNode implements MachNode, RIPRelSize {
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
index b0921fa0..d87e49b9 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AddFRISC.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AddFRISC.java
@@ -1,9 +1,9 @@
package com.seaofnodes.simple.node.cpus.riscv;
import com.seaofnodes.isa.RiscV;
-import com.seaofnodes.simple.SB;
import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.util.SB;
// fadd.d
public class AddFRISC extends MachConcreteNode implements MachNode {
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
index c3aa1276..8012b61e 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AddIRISC.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AddIRISC.java
@@ -1,8 +1,8 @@
package com.seaofnodes.simple.node.cpus.riscv;
import com.seaofnodes.isa.RiscV;
-import com.seaofnodes.simple.Utils;
import com.seaofnodes.simple.node.Node;
+import com.seaofnodes.simple.util.Utils;
public class AddIRISC extends ImmRISC {
// Used to inst-selection as a direct match against an ideal Add/Sub
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
index df392a0e..7010916f 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AddRISC.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AddRISC.java
@@ -1,8 +1,8 @@
package com.seaofnodes.simple.node.cpus.riscv;
-import com.seaofnodes.simple.SB;
import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.util.SB;
public class AddRISC extends MachConcreteNode implements MachNode {
public AddRISC( Node add ) { super(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
index 7639ea94..9fd65e7d 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AndIRISC.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AndIRISC.java
@@ -1,8 +1,8 @@
package com.seaofnodes.simple.node.cpus.riscv;
import com.seaofnodes.isa.RiscV;
-import com.seaofnodes.simple.Utils;
import com.seaofnodes.simple.node.Node;
+import com.seaofnodes.simple.util.Utils;
public class AndIRISC extends ImmRISC {
public AndIRISC( Node and, int imm) { super(and,imm); }
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
index 93ef95c0..1845985e 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AndRISC.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AndRISC.java
@@ -1,8 +1,8 @@
package com.seaofnodes.simple.node.cpus.riscv;
-import com.seaofnodes.simple.SB;
import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.util.SB;
public class AndRISC extends MachConcreteNode implements MachNode {
public AndRISC(Node and) { super(and); }
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/BranchRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/BranchRISC.java
index b5ad5f59..a9d03da5 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/BranchRISC.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/BranchRISC.java
@@ -3,9 +3,10 @@ package com.seaofnodes.simple.node.cpus.riscv;
import com.seaofnodes.isa.RiscV;
import com.seaofnodes.print.ExprPrinter;
-import com.seaofnodes.simple.*;
import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.util.SB;
+import com.seaofnodes.simple.util.Utils;
// Conditional branch such as: BEQ
public class BranchRISC extends IfNode implements MachNode, RIPRelSize {
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
index ff50a67f..f0ad8e88 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CallRISC.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CallRISC.java
@@ -1,10 +1,11 @@
package com.seaofnodes.simple.node.cpus.riscv;
import com.seaofnodes.isa.RiscV;
-import com.seaofnodes.simple.*;
import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.*;
import com.seaofnodes.simple.type.TypeFunPtr;
+import com.seaofnodes.simple.util.SB;
+import com.seaofnodes.simple.util.Utils;
public class CallRISC extends CallNode implements MachNode, RIPRelSize {
final TypeFunPtr _tfp;
@@ -57,7 +58,6 @@ public class CallRISC extends CallNode implements MachNode, RIPRelSize {
sb.p(_name).p(" ");
for( int i=0; i<nargs(); i++ )
sb.p(code.reg(arg(i+2))).p(", ");
- sb.unchar(2).p(" ").p(code.reg(fptr()));
-
+ sb.unchar(2);
}
}
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
index 55e36ebf..5eee596d 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CallRRISC.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CallRRISC.java
@@ -1,9 +1,9 @@
package com.seaofnodes.simple.node.cpus.riscv;
import com.seaofnodes.isa.RiscV;
-import com.seaofnodes.simple.*;
import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.util.SB;
public class CallRRISC extends CallNode implements MachNode {
CallRRISC( CallNode call ) { super(call); }
@@ -26,7 +26,7 @@ public class CallRRISC extends CallNode implements MachNode {
@Override public void asm(CodeGen code, SB sb) {
sb.p(code.reg(fptr())).p(" ");
for( int i=0; i<nargs(); i++ )
- sb.p(code.reg(arg(i))).p(" ");
+ sb.p(code.reg(arg(i))).p(", ");
sb.unchar(2);
}
}
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
index 12a798b5..8eb4fb3b 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/DivFRISC.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/DivFRISC.java
@@ -1,9 +1,9 @@
package com.seaofnodes.simple.node.cpus.riscv;
import com.seaofnodes.isa.RiscV;
-import com.seaofnodes.simple.SB;
import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.util.SB;
// fdiv.d
public class DivFRISC extends MachConcreteNode implements MachNode{
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
index 8367ee68..515ffa47 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/DivRISC.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/DivRISC.java
@@ -1,6 +1,6 @@
package com.seaofnodes.simple.node.cpus.riscv;
-import com.seaofnodes.simple.SB;
+import com.seaofnodes.simple.util.SB;
import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.*;
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/ExtendRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/ExtendRISC.java
index c2e93159..5bd3ec1c 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/ExtendRISC.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/ExtendRISC.java
@@ -4,7 +4,7 @@ import com.seaofnodes.isa.RiscV;
import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.*;
import com.seaofnodes.simple.type.TypeInteger;
-import com.seaofnodes.simple.SB;
+import com.seaofnodes.simple.util.SB;
// Convert a narrow C return register to Simple's full-width integer value.
public class ExtendRISC extends MachConcreteNode {
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
index b2d4efea..a56ad216 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/F8ToIRISC.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/F8ToIRISC.java
@@ -1,9 +1,9 @@
package com.seaofnodes.simple.node.cpus.riscv;
import com.seaofnodes.isa.RiscV;
-import com.seaofnodes.simple.*;
import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.util.SB;
public class F8ToIRISC extends MachConcreteNode implements MachNode {
F8ToIRISC(Node f8toi) {super(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
index 3dbb40d1..51ddc9db 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/FltRISC.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/FltRISC.java
@@ -1,12 +1,12 @@
package com.seaofnodes.simple.node.cpus.riscv;
import com.seaofnodes.isa.RiscV;
-import com.seaofnodes.simple.SB;
-import com.seaofnodes.simple.Utils;
import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.ConstantNode;
import com.seaofnodes.simple.node.MachNode;
import com.seaofnodes.simple.type.TypeFloat;
+import com.seaofnodes.simple.util.SB;
+import com.seaofnodes.simple.util.Utils;
public class FltRISC extends ConstantNode implements MachNode, RIPRelSize {
FltRISC(ConstantNode con) { super(con); }
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
index 4ebdec15..d3d64b06 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/FunRISC.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/FunRISC.java
@@ -1,11 +1,11 @@
package com.seaofnodes.simple.node.cpus.riscv;
import com.seaofnodes.isa.RiscV;
-import com.seaofnodes.simple.SB;
-import com.seaofnodes.simple.Utils;
import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.FunNode;
import com.seaofnodes.simple.node.MachNode;
+import com.seaofnodes.simple.util.SB;
+import com.seaofnodes.simple.util.Utils;
public class FunRISC extends FunNode implements MachNode {
FunRISC(FunNode fun) {super(fun);}
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
index c9b20939..1b53ab0f 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/I2F8RISC.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/I2F8RISC.java
@@ -1,9 +1,9 @@
package com.seaofnodes.simple.node.cpus.riscv;
import com.seaofnodes.isa.RiscV;
-import com.seaofnodes.simple.*;
import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.util.SB;
// fcvt.d.w
// Converts a 32-bit signed integer, in integer register rs1 into a double-precision floating-point number in floating-point register rd.
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/ImmRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/ImmRISC.java
index ceea98e3..ee247c3e 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/ImmRISC.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/ImmRISC.java
@@ -3,10 +3,10 @@ package com.seaofnodes.simple.node.cpus.riscv;
import com.seaofnodes.isa.RiscV;
import com.seaofnodes.print.ExprPrinter;
-import com.seaofnodes.simple.*;
import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.*;
import com.seaofnodes.simple.type.TypeInteger;
+import com.seaofnodes.simple.util.SB;
abstract public class ImmRISC extends MachConcreteNode implements MachNode {
public final int _imm12;
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/Int8RISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/Int8RISC.java
index fabb3b9d..23ac8481 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/Int8RISC.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/Int8RISC.java
@@ -1,11 +1,10 @@
package com.seaofnodes.simple.node.cpus.riscv;
import com.seaofnodes.isa.RiscV;
-import com.seaofnodes.simple.node.ConstantNode;
-
-import com.seaofnodes.simple.*;
import com.seaofnodes.simple.codegen.*;
+import com.seaofnodes.simple.node.ConstantNode;
import com.seaofnodes.simple.node.MachNode;
+import com.seaofnodes.simple.util.SB;
// Special instruction for loading 8 byte constants from the constant pool.
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
index e4dd39dd..9088abbd 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/IntRISC.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/IntRISC.java
@@ -1,12 +1,12 @@
package com.seaofnodes.simple.node.cpus.riscv;
import com.seaofnodes.isa.RiscV;
-import com.seaofnodes.simple.*;
import com.seaofnodes.simple.codegen.*;
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 com.seaofnodes.simple.util.SB;
// 12-bit integer constant. Larger constants are made up in the instruction
// selection by adding with a LUI.
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/LUI.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/LUI.java
index deaa3b59..aedd8b47 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/LUI.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/LUI.java
@@ -1,11 +1,11 @@
package com.seaofnodes.simple.node.cpus.riscv;
import com.seaofnodes.isa.RiscV;
-import com.seaofnodes.simple.*;
import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.ConstantNode;
import com.seaofnodes.simple.node.MachNode;
import com.seaofnodes.simple.type.TypeInteger;
+import com.seaofnodes.simple.util.SB;
// Load upper 20bits.
public class LUI extends ConstantNode implements MachNode {
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
index 5e1845b8..2e3dfd8d 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/LoadRISC.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/LoadRISC.java
@@ -1,9 +1,9 @@
package com.seaofnodes.simple.node.cpus.riscv;
import com.seaofnodes.isa.RiscV;
-import com.seaofnodes.simple.*;
import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.util.SB;
// Load memory addressing on RISC
// Support imm, reg(direct), or reg+off(indirect) addressing
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
index c2025a3d..22dba099 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/MemOpRISC.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/MemOpRISC.java
@@ -2,8 +2,8 @@ package com.seaofnodes.simple.node.cpus.riscv;
import com.seaofnodes.print.ExprPrinter;
-import com.seaofnodes.simple.SB;
-import com.seaofnodes.simple.Utils;
+import com.seaofnodes.simple.util.SB;
+import com.seaofnodes.simple.util.Utils;
import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.*;
import com.seaofnodes.simple.type.*;
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
index 40609f96..dacec2b5 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/MulFRISC.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/MulFRISC.java
@@ -1,9 +1,9 @@
package com.seaofnodes.simple.node.cpus.riscv;
import com.seaofnodes.isa.RiscV;
-import com.seaofnodes.simple.SB;
import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.util.SB;
// fmul.d
public class MulFRISC extends MachConcreteNode implements MachNode {
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
index a7961627..9cb9ace7 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/MulRISC.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/MulRISC.java
@@ -1,8 +1,8 @@
package com.seaofnodes.simple.node.cpus.riscv;
-import com.seaofnodes.simple.SB;
import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.util.SB;
// mulh signed multiply instruction(no-imm form)
public class MulRISC extends MachConcreteNode implements MachNode{
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/NegRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/NegRISC.java
index 58340b32..3070c56c 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/NegRISC.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/NegRISC.java
@@ -1,9 +1,9 @@
package com.seaofnodes.simple.node.cpus.riscv;
import com.seaofnodes.isa.RiscV;
-import com.seaofnodes.simple.SB;
import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.util.SB;
public class NegRISC extends MachConcreteNode implements MachNode {
public NegRISC( Node neg ) { super(neg); }
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
index 8e3daaba..965a3c7c 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/NewRISC.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/NewRISC.java
@@ -1,10 +1,11 @@
package com.seaofnodes.simple.node.cpus.riscv;
import com.seaofnodes.isa.RiscV;
-import com.seaofnodes.simple.*;
import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.*;
import com.seaofnodes.simple.print.ASMPrinter;
+import com.seaofnodes.simple.util.SB;
+import com.seaofnodes.simple.util.Utils;
public class NewRISC extends NewNode implements MachNode, RIPRelSize {
// A pre-zeroed chunk of memory.
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/NotRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/NotRISC.java
index 6aefc54b..6f499db1 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/NotRISC.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/NotRISC.java
@@ -1,11 +1,11 @@
package com.seaofnodes.simple.node.cpus.riscv;
import com.seaofnodes.isa.RiscV;
-import com.seaofnodes.simple.SB;
import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.MachConcreteNode;
import com.seaofnodes.simple.node.MachNode;
import com.seaofnodes.simple.node.NotNode;
+import com.seaofnodes.simple.util.SB;
public class NotRISC extends MachConcreteNode implements MachNode {
public NotRISC(NotNode not) { super(not); }
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
index 53abcf0b..937595a3 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/OrIRISC.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/OrIRISC.java
@@ -1,8 +1,8 @@
package com.seaofnodes.simple.node.cpus.riscv;
import com.seaofnodes.isa.RiscV;
-import com.seaofnodes.simple.Utils;
import com.seaofnodes.simple.node.Node;
+import com.seaofnodes.simple.util.Utils;
public class OrIRISC extends ImmRISC {
public OrIRISC( Node and, int imm) { super(and,imm); }
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
index 103d4c7d..a310a1f4 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/OrRISC.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/OrRISC.java
@@ -1,8 +1,8 @@
package com.seaofnodes.simple.node.cpus.riscv;
-import com.seaofnodes.simple.SB;
import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.util.SB;
public class OrRISC extends MachConcreteNode implements MachNode {
public OrRISC(Node or) { super(or); }
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
index d28311dc..33143c6b 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/RetRISC.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/RetRISC.java
@@ -1,13 +1,13 @@
package com.seaofnodes.simple.node.cpus.riscv;
import com.seaofnodes.isa.RiscV;
-import com.seaofnodes.simple.*;
import com.seaofnodes.simple.codegen.CodeGen;
import com.seaofnodes.simple.codegen.Encoding;
import com.seaofnodes.simple.codegen.RegMask;
import com.seaofnodes.simple.node.FunNode;
-import com.seaofnodes.simple.node.ReturnNode;
import com.seaofnodes.simple.node.MachNode;
+import com.seaofnodes.simple.node.ReturnNode;
+import com.seaofnodes.simple.util.Utils;
public class RetRISC extends ReturnNode implements MachNode {
public RetRISC(ReturnNode ret, FunNode fun) { super(ret, fun); fun.setRet(this); }
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SetFRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SetFRISC.java
index fabb9f92..d037dd5c 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SetFRISC.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SetFRISC.java
@@ -1,11 +1,11 @@
package com.seaofnodes.simple.node.cpus.riscv;
import com.seaofnodes.isa.RiscV;
-import com.seaofnodes.simple.SB;
import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.BoolNode;
import com.seaofnodes.simple.node.MachConcreteNode;
import com.seaofnodes.simple.node.MachNode;
+import com.seaofnodes.simple.util.SB;
// corresponds to feq.d = equal
// fle.d = less than or equal
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SetIRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SetIRISC.java
index 8b6512e1..9bc86565 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SetIRISC.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SetIRISC.java
@@ -1,6 +1,6 @@
package com.seaofnodes.simple.node.cpus.riscv;
-import com.seaofnodes.simple.Utils;
+import com.seaofnodes.simple.util.Utils;
import com.seaofnodes.simple.node.Node;
// corresponds to slti,sltiu
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
index 8d4a2f71..848cc59f 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SetRISC.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SetRISC.java
@@ -1,8 +1,8 @@
package com.seaofnodes.simple.node.cpus.riscv;
-import com.seaofnodes.simple.SB;
import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.util.SB;
// corresponds to slt sltu
public class SetRISC extends MachConcreteNode implements MachNode {
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
index 52b238ec..1425caa2 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SllIRISC.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SllIRISC.java
@@ -1,8 +1,8 @@
package com.seaofnodes.simple.node.cpus.riscv;
import com.seaofnodes.isa.RiscV;
-import com.seaofnodes.simple.Utils;
import com.seaofnodes.simple.node.Node;
+import com.seaofnodes.simple.util.Utils;
public class SllIRISC extends ImmRISC {
public SllIRISC( Node and, int imm) { super(and,imm); }
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
index 87e6693e..5a4d4cc6 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SllRISC.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SllRISC.java
@@ -1,8 +1,8 @@
package com.seaofnodes.simple.node.cpus.riscv;
-import com.seaofnodes.simple.SB;
import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.util.SB;
// Shift left logical
public class SllRISC extends MachConcreteNode implements MachNode {
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SplitRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SplitRISC.java
index ca1bf614..8a16f6b7 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SplitRISC.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SplitRISC.java
@@ -1,12 +1,12 @@
package com.seaofnodes.simple.node.cpus.riscv;
import com.seaofnodes.isa.RiscV;
-import com.seaofnodes.simple.SB;
-import com.seaofnodes.simple.Utils;
import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.FunNode;
import com.seaofnodes.simple.node.Node;
import com.seaofnodes.simple.node.SplitNode;
+import com.seaofnodes.simple.util.SB;
+import com.seaofnodes.simple.util.Utils;
public class SplitRISC extends SplitNode {
SplitRISC( String kind, byte round ) { super(kind,round, new Node[2]); }
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
index 48171483..b316b303 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SraIRISC.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SraIRISC.java
@@ -1,8 +1,8 @@
package com.seaofnodes.simple.node.cpus.riscv;
import com.seaofnodes.isa.RiscV;
-import com.seaofnodes.simple.Utils;
import com.seaofnodes.simple.node.Node;
+import com.seaofnodes.simple.util.Utils;
public class SraIRISC extends ImmRISC {
public SraIRISC( Node and, int imm) {
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
index 1e930e72..e2e34415 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SraRISC.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SraRISC.java
@@ -1,8 +1,8 @@
package com.seaofnodes.simple.node.cpus.riscv;
-import com.seaofnodes.simple.SB;
import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.util.SB;
// Right Shift Arithmetic
public class SraRISC extends MachConcreteNode implements MachNode {
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
index 985abe2c..d0ea6b18 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SrlIRISC.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SrlIRISC.java
@@ -1,8 +1,8 @@
package com.seaofnodes.simple.node.cpus.riscv;
import com.seaofnodes.isa.RiscV;
-import com.seaofnodes.simple.Utils;
import com.seaofnodes.simple.node.Node;
+import com.seaofnodes.simple.util.Utils;
public class SrlIRISC extends ImmRISC {
public SrlIRISC( Node and, int imm, boolean pop) { super(and,imm,pop); }
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
index 47712585..1eccc1c8 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SrlRISC.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SrlRISC.java
@@ -1,8 +1,8 @@
package com.seaofnodes.simple.node.cpus.riscv;
-import com.seaofnodes.simple.SB;
import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.util.SB;
// Right Shift Logical
public class SrlRISC extends MachConcreteNode implements MachNode {
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
index 03e06f83..48cb7c78 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/StoreRISC.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/StoreRISC.java
@@ -1,11 +1,11 @@
package com.seaofnodes.simple.node.cpus.riscv;
import com.seaofnodes.isa.RiscV;
-import com.seaofnodes.simple.SB;
-import com.seaofnodes.simple.Utils;
import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.codegen.RegMask;
import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.util.SB;
+import com.seaofnodes.simple.util.Utils;
// Store memory addressing on ARM
// Support imm, reg(direct), or reg+off(indirect) addressing
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
index caf751bb..044820cf 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SubFRISC.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SubFRISC.java
@@ -1,9 +1,9 @@
package com.seaofnodes.simple.node.cpus.riscv;
import com.seaofnodes.isa.RiscV;
-import com.seaofnodes.simple.SB;
import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.util.SB;
// fsub.d
public class SubFRISC extends MachConcreteNode implements MachNode {
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
index 7ab4f904..2adfce23 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SubRISC.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SubRISC.java
@@ -1,8 +1,8 @@
package com.seaofnodes.simple.node.cpus.riscv;
-import com.seaofnodes.simple.*;
import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.util.SB;
public class SubRISC extends MachConcreteNode implements MachNode {
public SubRISC( Node sub ) { super(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
index d7da5ba1..e57f6e09 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/TFPRISC.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/TFPRISC.java
@@ -1,12 +1,13 @@
package com.seaofnodes.simple.node.cpus.riscv;
import com.seaofnodes.isa.RiscV;
-import com.seaofnodes.simple.*;
import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.ConstantNode;
import com.seaofnodes.simple.node.MachNode;
import com.seaofnodes.simple.node.Node;
import com.seaofnodes.simple.type.TypeFunPtr;
+import com.seaofnodes.simple.util.SB;
+import com.seaofnodes.simple.util.Utils;
public class TFPRISC extends ConstantNode implements MachNode, RIPRelSize {
TFPRISC(ConstantNode con) { super(con); }
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/TMPRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/TMPRISC.java
index 46b8aace..7bc2db7e 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/TMPRISC.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/TMPRISC.java
@@ -1,12 +1,12 @@
package com.seaofnodes.simple.node.cpus.riscv;
import com.seaofnodes.isa.RiscV;
-import com.seaofnodes.simple.*;
import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.ConstantNode;
import com.seaofnodes.simple.node.MachNode;
import com.seaofnodes.simple.node.Node;
import com.seaofnodes.simple.type.TypeMemPtr;
+import com.seaofnodes.simple.util.SB;
public class TMPRISC extends ConstantNode implements MachNode, RIPRelSize {
TMPRISC(ConstantNode con) { super(con); }
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/UJmpRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/UJmpRISC.java
index cc47b7ed..448d9ec3 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/UJmpRISC.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/UJmpRISC.java
@@ -3,10 +3,11 @@ package com.seaofnodes.simple.node.cpus.riscv;
import com.seaofnodes.isa.RiscV;
import com.seaofnodes.print.ExprPrinter;
-import com.seaofnodes.simple.*;
import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.*;
import com.seaofnodes.simple.type.Type;
+import com.seaofnodes.simple.util.SB;
+import com.seaofnodes.simple.util.Utils;
// unconditional jump
public class UJmpRISC extends CFGNode implements MachNode, RIPRelSize {
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
index ebdf9fa8..f4323cae 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/XorIRISC.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/XorIRISC.java
@@ -1,8 +1,8 @@
package com.seaofnodes.simple.node.cpus.riscv;
import com.seaofnodes.isa.RiscV;
-import com.seaofnodes.simple.Utils;
import com.seaofnodes.simple.node.Node;
+import com.seaofnodes.simple.util.Utils;
public class XorIRISC extends ImmRISC {
public XorIRISC( Node and, int imm) { super(and,imm); }
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
index 725d2b78..413a7c3b 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/XorRISC.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/XorRISC.java
@@ -1,8 +1,8 @@
package com.seaofnodes.simple.node.cpus.riscv;
-import com.seaofnodes.simple.SB;
import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.util.SB;
public class XorRISC extends MachConcreteNode implements MachNode {
public XorRISC(Node or) { super(or); }
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
index f2d1e0b9..7c994624 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/riscv.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/riscv.java
@@ -2,10 +2,10 @@ package com.seaofnodes.simple.node.cpus.riscv;
import com.seaofnodes.isa.RiscV;
import static com.seaofnodes.isa.RiscV.*;
-import com.seaofnodes.simple.Utils;
import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.*;
import com.seaofnodes.simple.type.*;
+import com.seaofnodes.simple.util.Utils;
public class riscv extends Machine {
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
index 76a2d3f7..974f690f 100644
--- 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
@@ -1,10 +1,10 @@
package com.seaofnodes.simple.node.cpus.x86_64_v2;
import com.seaofnodes.isa.X86;
-import com.seaofnodes.simple.SB;
-import com.seaofnodes.simple.Utils;
import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.util.SB;
+import com.seaofnodes.simple.util.Utils;
public class AddFMemX86 extends MemOpX86 {
AddFMemX86( AddFNode add, LoadNode ld , Node base, Node idx, int off, int scale, Node val ) {
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
index b3266cb0..c7d0cf9c 100644
--- 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
@@ -1,9 +1,9 @@
package com.seaofnodes.simple.node.cpus.x86_64_v2;
import com.seaofnodes.isa.X86;
-import com.seaofnodes.simple.*;
import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.util.SB;
public class AddFX86 extends MachConcreteNode implements MachNode {
AddFX86( Node addf ) { super(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
index c55f975c..0e652a93 100644
--- 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
@@ -1,10 +1,10 @@
package com.seaofnodes.simple.node.cpus.x86_64_v2;
import com.seaofnodes.isa.X86;
-import com.seaofnodes.simple.SB;
-import com.seaofnodes.simple.Utils;
import com.seaofnodes.simple.codegen.Encoding;
import com.seaofnodes.simple.node.Node;
+import com.seaofnodes.simple.util.SB;
+import com.seaofnodes.simple.util.Utils;
public class AddIX86 extends ImmX86 {
AddIX86( Node add, int imm ) { super(add,imm); }
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
index d2111d07..ed65b0a8 100644
--- 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
@@ -1,9 +1,9 @@
package com.seaofnodes.simple.node.cpus.x86_64_v2;
import com.seaofnodes.isa.X86;
-import com.seaofnodes.simple.SB;
import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.util.SB;
public class AddMemX86 extends MemOpX86 {
AddMemX86( AddNode add, LoadNode ld , Node base, Node idx, int off, int scale, int imm, Node val ) {
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
index 15b83bfc..f97163eb 100644
--- 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
@@ -1,10 +1,10 @@
package com.seaofnodes.simple.node.cpus.x86_64_v2;
import com.seaofnodes.isa.X86;
-import com.seaofnodes.simple.SB;
import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.CallNode;
import com.seaofnodes.simple.node.MachNode;
+import com.seaofnodes.simple.util.SB;
public class CallRX86 extends CallNode implements MachNode {
CallRX86( CallNode call ) { super(call); }
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
index 3d450040..d1a43b83 100644
--- 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
@@ -1,10 +1,10 @@
package com.seaofnodes.simple.node.cpus.x86_64_v2;
import com.seaofnodes.isa.X86;
-import com.seaofnodes.simple.SB;
import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.*;
import com.seaofnodes.simple.type.TypeFunPtr;
+import com.seaofnodes.simple.util.SB;
public class CallX86 extends CallNode implements MachNode, RIPRelSize {
final TypeFunPtr _tfp;
@@ -43,7 +43,7 @@ public class CallX86 extends CallNode implements MachNode, RIPRelSize {
@Override public void asm(CodeGen code, SB sb) {
sb.p(_name).p(" ");
for( int i=0; i<nargs(); i++ )
- sb.p(code.reg(in(i+2))).p(" ");
+ sb.p(code.reg(in(i+2))).p(", ");
sb.unchar(2);
}
}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CmpFX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CmpFX86.java
index 5a0c7a37..5ef7a476 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CmpFX86.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CmpFX86.java
@@ -1,9 +1,9 @@
package com.seaofnodes.simple.node.cpus.x86_64_v2;
import com.seaofnodes.isa.X86;
-import com.seaofnodes.simple.*;
import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.util.SB;
public class CmpFX86 extends MachConcreteNode implements MachNode {
CmpFX86( Node cmp ) { super(cmp); }
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
index 2811a640..151f8ca8 100644
--- 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
@@ -1,9 +1,9 @@
package com.seaofnodes.simple.node.cpus.x86_64_v2;
import com.seaofnodes.isa.X86;
-import com.seaofnodes.simple.SB;
import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.util.SB;
// Compare immediate. Sets flags.
public class CmpIX86 extends MachConcreteNode implements MachNode {
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
index 942400f4..7b7d98d5 100644
--- 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
@@ -1,11 +1,11 @@
package com.seaofnodes.simple.node.cpus.x86_64_v2;
import com.seaofnodes.isa.X86;
-import com.seaofnodes.simple.SB;
import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.*;
import com.seaofnodes.simple.type.Type;
import com.seaofnodes.simple.type.TypeFloat;
+import com.seaofnodes.simple.util.SB;
public class CmpMemX86 extends MemOpX86 {
final boolean _swap; // Op switched LHS, RHS
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
index 27246db5..7faea17b 100644
--- 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
@@ -1,9 +1,9 @@
package com.seaofnodes.simple.node.cpus.x86_64_v2;
import com.seaofnodes.isa.X86;
-import com.seaofnodes.simple.*;
import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.util.SB;
public class CmpX86 extends MachConcreteNode implements MachNode {
CmpX86( Node add ) { super(add); }
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
index 962ec44e..7bbb5102 100644
--- 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
@@ -1,9 +1,9 @@
package com.seaofnodes.simple.node.cpus.x86_64_v2;
import com.seaofnodes.isa.X86;
-import com.seaofnodes.simple.*;
import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.util.SB;
public class DivFX86 extends MachConcreteNode implements MachNode {
DivFX86( Node divf) { super(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
index 5d061ae1..9f375a80 100644
--- 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
@@ -1,9 +1,9 @@
package com.seaofnodes.simple.node.cpus.x86_64_v2;
import com.seaofnodes.isa.X86;
-import com.seaofnodes.simple.*;
import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.util.SB;
public class DivX86 extends MachConcreteNode implements MachNode {
DivX86( Node div ) { super(div); }
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ExtendX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ExtendX86.java
index bd40bae7..1a6ae123 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ExtendX86.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ExtendX86.java
@@ -4,7 +4,7 @@ import com.seaofnodes.isa.X86;
import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.*;
import com.seaofnodes.simple.type.TypeInteger;
-import com.seaofnodes.simple.SB;
+import com.seaofnodes.simple.util.SB;
// Convert a narrow C return register to Simple's full-width integer value.
public class ExtendX86 extends MachConcreteNode {
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
index 44bcbe7b..a79a1017 100644
--- 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
@@ -1,9 +1,9 @@
package com.seaofnodes.simple.node.cpus.x86_64_v2;
import com.seaofnodes.isa.X86;
-import com.seaofnodes.simple.*;
import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.util.SB;
public class F8ToIX86 extends MachConcreteNode implements MachNode {
F8ToIX86(Node f8toi ) { super(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
index b17ea305..da9bdce7 100644
--- 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
@@ -1,9 +1,9 @@
package com.seaofnodes.simple.node.cpus.x86_64_v2;
import com.seaofnodes.isa.X86;
-import com.seaofnodes.simple.SB;
import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.util.SB;
public class FltX86 extends ConstantNode implements MachNode, RIPRelSize{
private byte _encSize;
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
index 24c2a78b..c7a20e4d 100644
--- 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
@@ -1,11 +1,11 @@
package com.seaofnodes.simple.node.cpus.x86_64_v2;
import com.seaofnodes.isa.X86;
-import com.seaofnodes.simple.SB;
import com.seaofnodes.simple.codegen.CodeGen;
import com.seaofnodes.simple.codegen.Encoding;
import com.seaofnodes.simple.node.FunNode;
import com.seaofnodes.simple.node.MachNode;
+import com.seaofnodes.simple.util.SB;
public class FunX86 extends FunNode implements MachNode {
FunX86( FunNode fun ) { super(fun); }
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
index 4a17b92b..ee357901 100644
--- 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
@@ -1,9 +1,9 @@
package com.seaofnodes.simple.node.cpus.x86_64_v2;
import com.seaofnodes.isa.X86;
-import com.seaofnodes.simple.*;
import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.util.SB;
public class I2f8X86 extends MachConcreteNode implements MachNode {
I2f8X86(Node i2f8 ) { super(i2f8); }
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ImmX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ImmX86.java
index 08c9b4c9..5a4ff4b2 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ImmX86.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ImmX86.java
@@ -1,10 +1,10 @@
package com.seaofnodes.simple.node.cpus.x86_64_v2;
import com.seaofnodes.isa.X86;
-import com.seaofnodes.simple.*;
import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.*;
import com.seaofnodes.simple.type.TypeInteger;
+import com.seaofnodes.simple.util.SB;
public abstract class ImmX86 extends MachConcreteNode implements MachNode {
final int _imm;
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
index 29513d98..656b1543 100644
--- 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
@@ -1,11 +1,11 @@
package com.seaofnodes.simple.node.cpus.x86_64_v2;
import com.seaofnodes.isa.X86;
-import com.seaofnodes.simple.*;
import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.*;
import com.seaofnodes.simple.type.Type;
import com.seaofnodes.simple.type.TypeInteger;
+import com.seaofnodes.simple.util.SB;
// Integer constants
public class IntX86 extends ConstantNode implements MachNode {
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
index 92156c58..10cee059 100644
--- 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
@@ -1,9 +1,10 @@
package com.seaofnodes.simple.node.cpus.x86_64_v2;
import com.seaofnodes.isa.X86;
-import com.seaofnodes.simple.*;
import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.util.SB;
+import com.seaofnodes.simple.util.Utils;
// Jump on flags, uses flags
public class JmpX86 extends IfNode implements MachNode, RIPRelSize {
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
index e20fd1ec..4b00e4dd 100644
--- 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
@@ -1,9 +1,9 @@
package com.seaofnodes.simple.node.cpus.x86_64_v2;
import com.seaofnodes.isa.X86;
-import com.seaofnodes.simple.*;
import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.util.SB;
public class LeaX86 extends MachConcreteNode implements MachNode {
final int _scale;
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
index c5329e1d..d4f1413e 100644
--- 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
@@ -1,15 +1,12 @@
package com.seaofnodes.simple.node.cpus.x86_64_v2;
import com.seaofnodes.isa.X86;
-import com.seaofnodes.simple.SB;
-import com.seaofnodes.simple.Utils;
import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.LoadNode;
import com.seaofnodes.simple.node.Node;
-import com.seaofnodes.simple.type.Type;
-import com.seaofnodes.simple.type.TypeFloat;
-import com.seaofnodes.simple.type.TypeInteger;
-import com.seaofnodes.simple.type.TypeMemPtr;
+import com.seaofnodes.simple.type.*;
+import com.seaofnodes.simple.util.SB;
+import com.seaofnodes.simple.util.Utils;
public class LoadX86 extends MemOpX86 {
LoadX86( LoadNode ld, Node base, Node idx, int off, int scale ) {
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
index 22a1a7ae..2e18a058 100644
--- 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
@@ -1,9 +1,9 @@
package com.seaofnodes.simple.node.cpus.x86_64_v2;
import com.seaofnodes.isa.X86;
-import com.seaofnodes.simple.SB;
import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.util.SB;
public class MemAddX86 extends MemOpX86 {
MemAddX86( StoreNode st, Node base, Node idx, int off, int scale, int imm, Node val ) {
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
index fa3cdd13..309e62d9 100644
--- 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
@@ -2,11 +2,11 @@ package com.seaofnodes.simple.node.cpus.x86_64_v2;
import com.seaofnodes.print.ExprPrinter;
-import com.seaofnodes.simple.*;
import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.*;
import com.seaofnodes.simple.type.*;
-
+import com.seaofnodes.simple.util.SB;
+import com.seaofnodes.simple.util.Utils;
import java.lang.StringBuilder;
// Generic X86 memory operand base.
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
index 3371a8ba..74616db7 100644
--- 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
@@ -1,9 +1,9 @@
package com.seaofnodes.simple.node.cpus.x86_64_v2;
import com.seaofnodes.isa.X86;
-import com.seaofnodes.simple.*;
import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.util.SB;
public class MulFX86 extends MachConcreteNode implements MachNode {
MulFX86( Node mulf) { super(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
index 4a7e24d0..5dcb253d 100644
--- 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
@@ -1,9 +1,9 @@
package com.seaofnodes.simple.node.cpus.x86_64_v2;
import com.seaofnodes.isa.X86;
-import com.seaofnodes.simple.SB;
-import com.seaofnodes.simple.node.*;
import com.seaofnodes.simple.codegen.*;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.util.SB;
public class MulIX86 extends MachConcreteNode implements MachNode {
final int _imm;
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
index cdc67ea8..6f3fcdeb 100644
--- 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
@@ -1,9 +1,9 @@
package com.seaofnodes.simple.node.cpus.x86_64_v2;
import com.seaofnodes.isa.X86;
-import com.seaofnodes.simple.*;
import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.util.SB;
public class MulX86 extends MachConcreteNode implements MachNode {
MulX86( Node mul ) { super(mul); }
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/NegX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/NegX86.java
index 3d3798cd..86610f6d 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/NegX86.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/NegX86.java
@@ -1,9 +1,9 @@
package com.seaofnodes.simple.node.cpus.x86_64_v2;
import com.seaofnodes.isa.X86;
-import com.seaofnodes.simple.SB;
import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.util.SB;
public class NegX86 extends MachConcreteNode implements MachNode {
NegX86(MinusNode not) { super(not); }
@@ -11,6 +11,7 @@ public class NegX86 extends MachConcreteNode implements MachNode {
@Override public RegMask regmap(int i) { return x86_64_v2.RMASK; }
@Override public RegMask outregmap() { return x86_64_v2.RMASK; }
@Override public RegMask killmap() { return x86_64_v2.FLAGS_MASK; }
+ @Override public int twoAddress() { return 1; }
@Override public void encoding( Encoding enc ) {
X86.unary(enc,0xF7,3,enc.reg(this));
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
index 877ec224..1cfc1c42 100644
--- 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
@@ -1,10 +1,10 @@
package com.seaofnodes.simple.node.cpus.x86_64_v2;
import com.seaofnodes.isa.X86;
-import com.seaofnodes.simple.*;
import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.*;
import com.seaofnodes.simple.type.TypeFunPtr;
+import com.seaofnodes.simple.util.SB;
public class NewX86 extends NewNode implements MachNode {
// A pre-zeroed chunk of memory.
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/NotX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/NotX86.java
index 46227dd4..1e4e7c26 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/NotX86.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/NotX86.java
@@ -1,9 +1,9 @@
package com.seaofnodes.simple.node.cpus.x86_64_v2;
import com.seaofnodes.isa.X86;
-import com.seaofnodes.simple.SB;
import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.util.SB;
public class NotX86 extends MachConcreteNode implements MachNode {
NotX86(NotNode not) { super(not); }
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/RegX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/RegX86.java
index 50c8e974..06755143 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/RegX86.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/RegX86.java
@@ -1,9 +1,9 @@
package com.seaofnodes.simple.node.cpus.x86_64_v2;
import com.seaofnodes.isa.X86;
-import com.seaofnodes.simple.*;
import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.util.SB;
public abstract class RegX86 extends MachConcreteNode {
RegX86( Node add ) { super(add); }
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
index 43ad6bbd..cf6275dc 100644
--- 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
@@ -1,14 +1,13 @@
package com.seaofnodes.simple.node.cpus.x86_64_v2;
import com.seaofnodes.isa.X86;
-import com.seaofnodes.simple.SB;
import com.seaofnodes.simple.codegen.CodeGen;
import com.seaofnodes.simple.codegen.Encoding;
import com.seaofnodes.simple.codegen.RegMask;
+import com.seaofnodes.simple.node.*;
import com.seaofnodes.simple.node.MachNode;
import com.seaofnodes.simple.node.Node;
-
-import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.util.SB;
// Arithmetic Right Shift
public class SarIX86 extends MachConcreteNode implements MachNode {
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
index 50693034..92493712 100644
--- 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
@@ -1,9 +1,9 @@
package com.seaofnodes.simple.node.cpus.x86_64_v2;
import com.seaofnodes.isa.X86;
-import com.seaofnodes.simple.Utils;
import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.Node;
+import com.seaofnodes.simple.util.Utils;
public class SarX86 extends RegX86 {
SarX86( Node add ) { super(add); }
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
index 591bf8da..533d5652 100644
--- 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
@@ -1,9 +1,10 @@
package com.seaofnodes.simple.node.cpus.x86_64_v2;
import com.seaofnodes.isa.X86;
-import com.seaofnodes.simple.*;
import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.util.SB;
+import com.seaofnodes.simple.util.Utils;
// Corresponds to the x86 instruction "sete && setne".
// Use result of comparison without jump.
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
index f31832e6..1e62243e 100644
--- 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
@@ -1,11 +1,10 @@
package com.seaofnodes.simple.node.cpus.x86_64_v2;
import com.seaofnodes.isa.X86;
-import com.seaofnodes.simple.node.Node;
-
-import com.seaofnodes.simple.*;
import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.node.Node;
+import com.seaofnodes.simple.util.SB;
public class ShlIX86 extends MachConcreteNode implements MachNode {
final int _imm;
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
index 08c4db25..768d97e7 100644
--- 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
@@ -1,9 +1,9 @@
package com.seaofnodes.simple.node.cpus.x86_64_v2;
import com.seaofnodes.isa.X86;
-import com.seaofnodes.simple.Utils;
import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.Node;
+import com.seaofnodes.simple.util.Utils;
public class ShlX86 extends RegX86 {
ShlX86( Node add ) { super(add); }
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
index 218542f3..8a0d1b58 100644
--- 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
@@ -1,11 +1,10 @@
package com.seaofnodes.simple.node.cpus.x86_64_v2;
import com.seaofnodes.isa.X86;
-import com.seaofnodes.simple.node.Node;
-
-import com.seaofnodes.simple.*;
import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.node.Node;
+import com.seaofnodes.simple.util.SB;
public class ShrIX86 extends MachConcreteNode implements MachNode {
final int _imm;
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
index c72f21e2..1ba20fee 100644
--- 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
@@ -1,9 +1,9 @@
package com.seaofnodes.simple.node.cpus.x86_64_v2;
import com.seaofnodes.isa.X86;
-import com.seaofnodes.simple.Utils;
import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.Node;
+import com.seaofnodes.simple.util.Utils;
public class ShrX86 extends RegX86 {
ShrX86( Node add ) { super(add); }
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SplitX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SplitX86.java
index 5cf668d1..e8281975 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SplitX86.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SplitX86.java
@@ -1,8 +1,6 @@
package com.seaofnodes.simple.node.cpus.x86_64_v2;
import com.seaofnodes.isa.X86;
-import com.seaofnodes.simple.SB;
-import com.seaofnodes.simple.Utils;
import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.FunNode;
import com.seaofnodes.simple.node.Node;
@@ -10,6 +8,8 @@ import com.seaofnodes.simple.node.SplitNode;
import com.seaofnodes.simple.type.Type;
import com.seaofnodes.simple.type.TypeFloat;
import com.seaofnodes.simple.type.TypeInteger;
+import com.seaofnodes.simple.util.SB;
+import com.seaofnodes.simple.util.Utils;
public class SplitX86 extends SplitNode {
SplitX86( String kind, byte round ) { super(kind,round, new Node[2]); }
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
index 4796c0fe..c83dfd44 100644
--- 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
@@ -3,13 +3,13 @@ package com.seaofnodes.simple.node.cpus.x86_64_v2;
import com.seaofnodes.isa.X86;
import com.seaofnodes.print.ExprPrinter;
-import com.seaofnodes.simple.SB;
import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.ConstantNode;
import com.seaofnodes.simple.node.Node;
import com.seaofnodes.simple.node.StoreNode;
import com.seaofnodes.simple.type.Type;
import com.seaofnodes.simple.type.TypeFloat;
+import com.seaofnodes.simple.util.SB;
public class StoreX86 extends MemOpX86 {
StoreX86( StoreNode st, Node base, Node idx, int off, int scale, int imm, Node 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
index 6a008551..29f0033a 100644
--- 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
@@ -1,9 +1,9 @@
package com.seaofnodes.simple.node.cpus.x86_64_v2;
import com.seaofnodes.isa.X86;
-import com.seaofnodes.simple.*;
import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.util.SB;
public class SubFX86 extends MachConcreteNode implements MachNode {
SubFX86( Node subf) { super(subf); }
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
index 8723af91..fff2fb04 100644
--- 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
@@ -1,12 +1,12 @@
package com.seaofnodes.simple.node.cpus.x86_64_v2;
import com.seaofnodes.isa.X86;
-import com.seaofnodes.simple.*;
import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.*;
import com.seaofnodes.simple.node.cpus.riscv.riscv;
import com.seaofnodes.simple.type.Type;
import com.seaofnodes.simple.type.TypeFunPtr;
+import com.seaofnodes.simple.util.SB;
// Function constants
public class TFPX86 extends ConstantNode implements MachNode, RIPRelSize {
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/TMPX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/TMPX86.java
index 027b2e01..c78ac6de 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/TMPX86.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/TMPX86.java
@@ -1,10 +1,10 @@
package com.seaofnodes.simple.node.cpus.x86_64_v2;
import com.seaofnodes.isa.X86;
-import com.seaofnodes.simple.SB;
import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.*;
import com.seaofnodes.simple.type.TypeMemPtr;
+import com.seaofnodes.simple.util.SB;
public class TMPX86 extends ConstantNode implements MachNode, RIPRelSize{
TMPX86(ConstantNode con ) { super(con); }
@@ -18,7 +18,11 @@ public class TMPX86 extends ConstantNode implements MachNode, RIPRelSize{
short dst = enc.reg(this);
X86.rexF(dst, 0, 0, true, enc);
enc.add1(0x8D).add1(X86.modrm(X86.MOD.INDIRECT, dst, 0b101)).add4(0);
- enc.largeConstant(this,((TypeMemPtr)_con)._obj,7-4,2/*ELF encoding PC32*/);
+ int opLen = 1/*rexF*/+1/*opcode 0x8D*/+1/*modrm*/+4/*pc rel offset*/;
+ // Base of constant array
+ TypeMemPtr tmp = (TypeMemPtr)_con;
+ int baseOff = tmp._obj.aryBase();
+ enc.largeConstant(this,tmp._obj,opLen-baseOff,2/*ELF encoding PC32*/);
}
// Delta is from opcode start
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/UJmpX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/UJmpX86.java
index d991e40a..591a51c3 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/UJmpX86.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/UJmpX86.java
@@ -2,10 +2,11 @@ package com.seaofnodes.simple.node.cpus.x86_64_v2;
import com.seaofnodes.isa.X86;
-import com.seaofnodes.simple.*;
import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.*;
import com.seaofnodes.simple.type.Type;
+import com.seaofnodes.simple.util.SB;
+import com.seaofnodes.simple.util.Utils;
// unconditional jump
public class UJmpX86 extends CFGNode implements MachNode, RIPRelSize {
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
index 7ddd17d4..e283bc8f 100644
--- 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
@@ -1,9 +1,9 @@
package com.seaofnodes.simple.node.cpus.x86_64_v2;
-import com.seaofnodes.simple.Utils;
import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.*;
import com.seaofnodes.simple.type.*;
+import com.seaofnodes.simple.util.Utils;
public class x86_64_v2 extends Machine {
public x86_64_v2( CodeGen code ) {}
@@ -397,7 +397,8 @@ public class x86_64_v2 extends Machine {
return new CmpMemX86(bool, address(ld), ld.ptr(), idx, off, scale, imm(rhs), val, false);
// Operands swap in the encoding directly, no need for Set/Jmp to swap `bop`
- if( rhs instanceof LoadNode ld && ld.nOuts() == 1 && lhs._type.isa(ld._declaredType))
+ if( rhs instanceof LoadNode ld && ld.nOuts() == 1 && lhs._type.isa(ld._declaredType) &&
+ (val!=null || bool.op()=="==" || bool.op()=="!=") )
return new CmpMemX86(bool, address(ld), ld.ptr(), idx, off, scale, imm(lhs), val, true );
// Vs immediate
diff --git a/src/main/java/com/seaofnodes/simple/print/ASMPrinter.java b/src/main/java/com/seaofnodes/simple/print/ASMPrinter.java
index 1cc83813..48296ce7 100644
--- a/src/main/java/com/seaofnodes/simple/print/ASMPrinter.java
+++ b/src/main/java/com/seaofnodes/simple/print/ASMPrinter.java
@@ -4,7 +4,7 @@ import com.seaofnodes.print.AssemblyAdapter;
import com.seaofnodes.print.ASMPrinter.Data;
import com.seaofnodes.print.ASMPrinter.Pool;
import com.seaofnodes.simple.codegen.CodeGen;
-import com.seaofnodes.simple.SB;
+import com.seaofnodes.simple.util.SB;
import com.seaofnodes.simple.node.*;
import com.seaofnodes.simple.type.*;
import java.util.ArrayList;
diff --git a/src/main/java/com/seaofnodes/simple/print/IRPrinter.java b/src/main/java/com/seaofnodes/simple/print/IRPrinter.java
index e4d719f4..c912ab77 100644
--- a/src/main/java/com/seaofnodes/simple/print/IRPrinter.java
+++ b/src/main/java/com/seaofnodes/simple/print/IRPrinter.java
@@ -3,7 +3,7 @@ package com.seaofnodes.simple.print;
import com.seaofnodes.print.IRAdapter;
import com.seaofnodes.simple.node.*;
import java.util.ArrayList;
-import com.seaofnodes.simple.SB;
+import com.seaofnodes.simple.util.SB;
import com.seaofnodes.simple.codegen.CodeGen;
/** This chapter's read-only IR facts and the familiar debugger entry points. */
diff --git a/src/main/java/com/seaofnodes/simple/print/SimpleGraphAdapter.java b/src/main/java/com/seaofnodes/simple/print/SimpleGraphAdapter.java
index 3d10a80c..785049dc 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 22's view of the IR; browser, transport and layout are shared. */
+/** Chapter 23'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 22b6beba..8877b310 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 22's compilation context; capture and event bookkeeping are shared. */
+/** Chapter 23's compilation context; capture and event bookkeeping are shared. */
public class SimpleGraphObserver extends GraphCapture<Node> {
private CodeGen _code;
public SimpleGraphObserver() { super(new SimpleGraphAdapter()); }
diff --git a/src/main/java/com/seaofnodes/simple/sys/sys.smp b/src/main/java/com/seaofnodes/simple/sys/sys.smp
deleted file mode 100644
index 4ac925db..00000000
--- a/src/main/java/com/seaofnodes/simple/sys/sys.smp
+++ /dev/null
@@ -1,58 +0,0 @@
-// -*- mode: java; -*-
-
-// top-level default import
-struct sys {
- // libc bindings
- /** https://www.man7.org/linux/man-pages/man2/<libc call>.2.html */
- struct libc {
- i32 errno = "C";
- // fd buf len -> len
- { i32 i64 u32 -> u32 } write = "C";
- // fd buf len -> len
- { i32 i64 u32 -> u32 } read = "C";
- // status
- { i32 -> i32 } _exit = "C";
- // addr len prot flags fd off -> void*
- { i64 i64 i32 i32 i32 i32 -> i64 } mmap = "C";
- // mmap flags
- i32 PROT_EXEC = 4;
- i32 PROT_READ = 1;
- i32 PROT_WRITE= 2;
- i32 PROT_NONE = 0;
- i32 MAP_PRIVATE = 2;
- i32 MAP_ANON = 32;
- };
- struct ary {
- val copy = { u8[] dst, int doff, u8[~] src, int soff, int len ->
- for( int i=0; i<len; i++ )
- dst[i+doff] = src[i+soff];
- return dst;
- };
- };
- struct io {
- // Print string to stdout
- val p = { u8[~] str ->
- i64 ptr = str; // cast array base to i64
- return sys.libc.write(1,ptr,str#);
- };
-
- // Return stdin as a u8[]
- val stdin = { ->
- var buf = new u8[8*1024];
- i64 ptr = buf;
- int len = 0;
- while( true ) {
- int rez = sys.libc.read(0,ptr,buf#);
- if( rez < 0 ) sys.libc._exit(sys.libc.errno);
- len += rez;
- if( rez < buf# )
- break;
- sys.libc._exit(-2); // Need to implement
- }
- // Compress
- var dst = new u8[len];
- return sys.ary.copy(dst,0,buf,0,len);
- };
-
- };
-};
diff --git a/src/main/java/com/seaofnodes/simple/type/Field.java b/src/main/java/com/seaofnodes/simple/type/Field.java
index 94afcd26..1f22cb05 100644
--- a/src/main/java/com/seaofnodes/simple/type/Field.java
+++ b/src/main/java/com/seaofnodes/simple/type/Field.java
@@ -1,8 +1,8 @@
package com.seaofnodes.simple.type;
-import com.seaofnodes.simple.SB;
-import com.seaofnodes.simple.Utils;
-import java.util.ArrayList;
+import com.seaofnodes.simple.util.Ary;
+import com.seaofnodes.simple.util.SB;
+import java.util.*;
/**
* Represents a field in a struct. This is not a Type in the type system.
@@ -12,65 +12,125 @@ public class Field extends Type {
// The pair {fieldName,type} uniquely identifies a field.
// Field name
- public final String _fname;
+ public String _fname;
// Type of the field
- public final Type _type;
+ public Type _t;
// Unique memory alias, not sensibly part of a "type" but very convenient here.
- public final int _alias;
+ public int _alias;
// Field must be written to exactly once, no more, no less
- public final boolean _final;
+ public boolean _final;
+ // Field is final in the declaration; value is the same for all instances
+ // and will be moved to the class object
+ public boolean _one;
- private Field(String fname, Type type, int alias, boolean xfinal ) {
- super(TFLD);
+ private static final Ary<Field> FREE = new Ary<>(Field.class);
+ private Field(String fname, Type type, int alias, boolean xfinal, boolean one ) { super(TFLD); init(fname,type,alias,xfinal,one); }
+ private Field init(String fname, Type type, int alias, boolean xfinal, boolean one ) {
_fname = fname;
- _type = type;
+ _t = type;
_alias = alias;
_final = xfinal;
+ _one = one;
+ return this;
}
- // Make with existing alias
- public static Field make( String fname, Type type, int alias, boolean xfinal ) {
- return new Field(fname,type,alias,xfinal).intern();
+
+ // Return a filled-in Field; either from free list or alloc new.
+ private static Field malloc(String fname, Type type, int alias, boolean xfinal, boolean one ) {
+ return FREE.isEmpty() ? new Field(fname,type,alias,xfinal,one) : FREE.pop().init(fname,type,alias,xfinal,one);
+ }
+ // Malloc-from
+ public Field malloc( ) { return malloc(_fname,null,_alias,_final,_one); }
+ @Override Field free(Type t) {
+ Field f = (Field)t;
+ assert !f.isFree() && !f._terned;
+ f._t = null;
+ f._dual = null;
+ f._hash = 0;
+ FREE.push(f);
+ return this;
+ }
+ @Override boolean isFree() { return _t ==null; }
+
+ // Make and intern with listed fields
+ public static Field make( String fname, Type type, int alias, boolean xfinal, boolean one ) {
+ Field f = malloc(fname,type,alias,xfinal,one);
+ Field f2 = f.intern();
+ if( f2==f ) return f;
+ return VISIT.isEmpty() ? f2.free(f) : f2.delayFree(f);
}
public Field makeFrom( Type type ) {
- return type == _type ? this : new Field(_fname,type,_alias,_final).intern();
+ return type == _t ? this : make(_fname,type,_alias,_final,_one);
+ }
+ public Field makeFrom( boolean xfinal ) {
+ return xfinal == _final ? this : make(_fname,_t,_alias,xfinal,_one);
}
- @Override public Field makeRO() { return isFinal() ? this : make(_fname,_type.makeRO(),_alias,true); }
- @Override public boolean isFinal() { return _final && _type.isFinal(); }
- public static final Field TEST = make("test",Type.NIL,-2,false);
- public static final Field TEST2= make("test",Type.NIL,-2,true);
- public static void gather(ArrayList<Type> ts) { ts.add(TEST); ts.add(TEST2); }
+ // Cycle-making-breaking
+ void setType(Type t) { assert _t ==null; _t = t; }
+
+ public static final Field BOT = make(" ",Type.BOTTOM,-999,true,false);
+ public static final Field TEST = make("test",Type.NIL,-2,false,false);
+ public static final Field TEST2 = make("test",Type.NIL,-2,true, false);
+ public static final Field FLT = make("flt ",TypeFloat.F32,-2,false,false);
+ public static void gather(ArrayList<Type> ts) { ts.add(TEST); ts.add(TEST2); ts.add(FLT); }
@Override Field xmeet( Type that ) {
Field fld = (Field)that; // Invariant
- assert _fname.equals(fld._fname);
+ if( this==BOT || fld==BOT ) return BOT;
+ if( this==BOT.dual() ) return fld ;
+ if( fld ==BOT.dual() ) return this;
+ if( _fname!=fld._fname ) { assert !_fname.equals(fld._fname); return BOT; }
assert _alias==fld._alias;
- return make(_fname,_type.meet(fld._type),_alias,_final | fld._final);
+ assert _one ==fld._one ;
+ return make(_fname, _t.meet(fld._t ),_alias,_final | fld._final, _one);
}
@Override
- public Field dual() { return make(_fname,_type.dual(),_alias,!_final); }
+ Field xdual() { return malloc(_fname, _t.dual(),_alias,!_final,_one); }
- @Override public boolean isConstant() { return _type.isConstant(); }
+ @Override Field rdual() {
+ if( _dual!=null ) return dual();
+ assert !_terned;
+ Field d = malloc(_fname,null,_alias,!_final,_one);
+ (_dual = d)._dual = this; // Cross link duals
+ d._t = _t._terned ? _t.dual() : _t.rdual();
+ return d;
+ }
- @Override public Field glb(boolean mem) {
- Type glb = _type.glb(mem);
- return (glb==_type && _final) ? this : make(_fname,glb,_alias,true);
+ @Override boolean _isConstant() { return _t._isConstant(); }
+ @Override boolean _isFinal() { return _final && _t._isFinal(); }
+ @Override Field _makeRO() { return make(_fname, _t._makeRO(),_alias,true,_one); }
+ boolean isGLB2() { return _final && _t._isGLB(true); }
+ Field glb2() {
+ Type glb = _t._glb(true);
+ return (glb== _t && _final ) ? this : make(_fname,glb,_alias,true,_one);
}
+ @Override Field _close() { return makeFrom( _t._close()); }
// Override in subclasses
- int hash() { return _fname.hashCode() ^ _type.hashCode() ^ _alias ^ (_final ? 1024 : 0); }
+ int hash() { return _fname.hashCode() ^ _t.hashCode() ^ _alias ^ (_final ? 1024 : 0) ^ (_one ? 2048 : 0); }
- boolean eq(Type t) {
+ private boolean static_eq( Field f ) {
+ return _fname.equals(f._fname) && _alias==f._alias && _final==f._final && _one==f._one && _t !=null && f._t !=null;
+ }
+ @Override boolean eq( Type t ) {
+ Field f = (Field)t;
+ return static_eq(f) && _t ==f._t;
+ }
+ @Override boolean cycle_eq(Type t) {
Field f = (Field)t;
- return _fname.equals(f._fname) && _type==f._type && _alias==f._alias && _final==f._final;
+ return static_eq(f) && _t.cycle_eq(f._t );
}
+ @Override int nkids() { return 1; }
+ @Override Type at( int idx ) { return _t; }
+ @Override void set( int idx, Type t ) { _t = t; }
@Override
- public SB print( SB sb ) {
- return _type.print(sb.p(_final?"":"!").p(_fname).p(":").p(_alias).p(" : "));
+ public SB _print( SB sb, BitSet visit, boolean html ) {
+ sb.p(_final?"":"!").p(_one?"$":"").p(_fname).p(":").p(_alias).p(" : ");
+ return _t ==null ? sb.p("---") : _t.print(sb,visit,html);
}
- @Override public String str() { return (_final?"":"!")+_fname; }
+ @Override public String str() { return (_final?"":"!")+(_one?"$":"")+_fname; }
}
diff --git a/src/main/java/com/seaofnodes/simple/type/Type.java b/src/main/java/com/seaofnodes/simple/type/Type.java
index 4fad33cb..3f115c3f 100644
--- a/src/main/java/com/seaofnodes/simple/type/Type.java
+++ b/src/main/java/com/seaofnodes/simple/type/Type.java
@@ -1,9 +1,7 @@
package com.seaofnodes.simple.type;
-import com.seaofnodes.simple.SB;
-import com.seaofnodes.simple.Utils;
-import java.util.ArrayList;
-import java.util.HashMap;
+import com.seaofnodes.simple.util.*;
+import java.util.*;
/**
* These types are part of a Monotone Analysis Framework,
@@ -21,8 +19,33 @@ import java.util.HashMap;
* simple constant folding e.g. 1+2 == 3 stuff.
*/
-public class Type {
+public class Type /*implements Cloneable*/ {
+ // Interning tables
+
+ // Main intern table; a collection of all unique types.
static final HashMap<Type,Type> INTERN = new HashMap<>();
+ // Starting intern table; used to rapidly reset the main table between tests
+ private static final HashMap<Type,Type> INTERN0= new HashMap<>();
+ // Lifetime management during cyclic installs is complex; this is used to
+ // track created-but-already-interned objects
+ private static final Ary<Type> FREES = new Ary<>(Type.class);
+ // "visit" bit management, based solely on _uid
+ static final BitSet BITS = new BitSet();
+
+ // "visit" bit management. Key varies by kind of visit:
+
+ // - "close" an open cyclic type. Used by the Parser, it will hit on
+ // same-named structs, even if the structs have different states of
+ // completion/fields.
+ // - "uid/name" - Either the UID or name works (unlike "close" which must
+ // use the name). Used to make cyclic copies with changes, like make
+ // read-only where there should be only 1 copy of a name in a cycle.
+ // - "pair of uids" - For recursive MEET, where we need to key off two types
+ static final HashMap<Object,Type> VISIT = new HashMap<>();
+
+ // Every Type has a UID to allow visit bits on cyclic visits
+ private static int UID=1;
+ public char _uid; // Unique ID for every type
// ----------------------------------------------------------
// Simple types are implemented fully here. "Simple" means: the code and
@@ -38,33 +61,42 @@ public class Type {
static final byte TPTR = 7; // All nil-able scalar values
static final byte TINT = 8; // All Integers; see TypeInteger
static final byte TFLT = 9; // All Floats ; see TypeFloat
- static final byte TMEMPTR =10; // Memory pointer to a struct type
- static final byte TFUNPTR =11; // Function pointer; unique signature and code address (just a bit)
+ static final byte TCONARY =10; // Constant array
+ static final byte TRPC =11; // Return Program Control (Return PC or RPC)
static final byte TTUPLE =12; // Tuples; finite collections of unrelated Types, kept in parallel
- static final byte TMEM =13; // All memory (alias 0) or A slice of memory - with specific alias
- static final byte TSTRUCT =14; // Structs; tuples with named fields
- static final byte TFLD =15; // Named fields in structs
- static final byte TCONARY =16; // Constant array
- static final byte TRPC =17; // Return Program Control (Return PC or RPC)
+ static final byte TCYCLIC =13; // Has internal pointers, needs recursive treatment
+ static final byte TMEMPTR =13; // Memory pointer to a struct type
+ static final byte TFUNPTR =14; // Function pointer; unique signature and code address (just a bit)
+ static final byte TMEM =15; // All memory (alias 0) or A slice of memory - with specific alias
+ static final byte TSTRUCT =16; // Structs; tuples with named fields
+ static final byte TFLD =17; // Named fields in structs
+
+ // Basic RTTI, useful for a lot of fast tests.
public final byte _type;
+ public boolean _terned;
public boolean is_simple() { return _type < TSIMPLE; }
private static final String[] STRS = new String[]{"Bot","Top","Ctrl","~Ctrl","null","~nil"};
- protected Type(byte type) { _type = type; }
+ static final int[] CNTS = new int[TFLD+1];
+ protected Type(byte type) {
+ _type = type; // RTTI
+ _uid = (char)UID++; // A unique ID for every type
+ assert _uid!=0; // Overflow
+ CNTS[type]++;
+ }
public static final Type BOTTOM = new Type( TBOT ).intern(); // ALL
- public static final Type TOP = new Type( TTOP ).intern(); // ANY
+ public static final Type TOP = BOTTOM.dual();
public static final Type CONTROL = new Type( TCTRL ).intern(); // Ctrl
- public static final Type XCONTROL = new Type( TXCTRL ).intern(); // ~Ctrl
+ public static final Type XCONTROL = CONTROL.dual();
public static final Type NIL = new Type( TNIL ).intern(); // low null of all flavors
- public static final Type XNIL = new Type( TXNIL ).intern(); // high or choice null
+ public static final Type XNIL = NIL.dual();
public static Type[] gather() {
ArrayList<Type> ts = new ArrayList<>();
ts.add(BOTTOM);
ts.add(CONTROL);
ts.add(NIL);
- ts.add(XNIL);
TypeNil.gather(ts);
TypePtr.gather(ts);
TypeInteger.gather(ts);
@@ -77,6 +109,7 @@ public class Type {
TypeTuple.gather(ts);
TypeRPC.gather(ts);
TypeConAry.gather(ts);
+
int sz = ts.size();
for( int i = 0; i < sz; i++ )
ts.add(ts.get(i).dual());
@@ -87,10 +120,6 @@ public class Type {
public boolean isHigh () { return _type==TTOP || _type==TXCTRL || _type==TXNIL; }
public boolean isHighOrConst() { return isHigh() || isConstant(); }
- // Strict constant values, things on the lattice centerline.
- // Excludes both high and low values
- public boolean isConstant() { return _type==TNIL; }
-
// ----------------------------------------------------------
// Notes on Type interning: At the moment it is not easy to reset the
@@ -106,14 +135,68 @@ public class Type {
// Factory method which interns "this"
@SuppressWarnings("unchecked")
- public <T extends Type> T intern() {
- T nnn = (T)INTERN.get(this);
- if( nnn==null )
- INTERN.put(nnn=(T)this,this);
- return nnn;
+ <T extends Type> T intern() {
+ //assert check();
+ assert !_terned; // Do not ask for already-interned
+ T t2 = (T)INTERN.get(this);
+ if( t2!=null ) {
+ assert t2._dual != null; // Prior is complete with dual
+ assert this != t2; // Do not hashcons twice, should not get self back
+ return t2; // Return prior
+ }
+ // Mid recursive type construction
+ if( !VISIT.isEmpty() && _type >= TCYCLIC ) {
+ // No intern attempt at all
+ return (T)this;
+ }
+ // Not in type table
+ _dual = null; // No dual yet
+ INTERN.put(this,this); // Put in table without dual
+ _terned = true;
+ //Util.hash_quality_check_per(INTERN,"INTERN");
+ T d = xdual(); // Compute dual without requiring table lookup, and not setting name
+ _dual = d;
+ if( this==d ) return d; // Self-symmetric? Dual is self
+ assert !equals(d); // Self-symmetric is handled by caller
+ assert d._dual==null; // Else dual-dual not computed yet
+ assert INTERN.get(d)==null;
+ d._dual = (T)this;
+ INTERN.put(d,d); // Install dual also
+ d._terned = true;
+ //assert check();
+ return (T)this;
+ }
+
+ // Recursive visit elsewhere, interns with dual already
+ <T extends Type> T _intern() {
+ //assert check();
+ assert !_terned;
+ Type t = INTERN.get(this);
+ if( t!=null ) return (T)t.delayFree(this).delayFree(_dual);
+ assert !INTERN.containsKey( _dual );
+ INTERN.put(this,this);
+ INTERN.put(_dual,_dual);
+ _terned = _dual._terned = true;
+ //assert check();
+ return (T)this;
}
- private int _hash; // Hash cache; not-zero when set.
+ private static boolean check() {
+ for( Type t : INTERN.keySet() ) {
+ Type t2 = INTERN.get(t);
+ assert t==t2;
+ }
+ return true;
+ }
+
+ public static Type find(int uid) {
+ for( Type t : INTERN.keySet() )
+ if( t._uid==uid ) return t;
+ return null;
+ }
+
+
+ int _hash; // Hash cache; not-zero when set.
@Override
public final int hashCode() {
if( _hash!=0 ) return _hash;
@@ -132,8 +215,42 @@ public class Type {
return eq(t);
}
// Overridden in subclasses; subclass can assume "this!=t" and java classes are same
- boolean eq(Type t) { return true; }
+ boolean eq(Type t) { return this==t; }
+ boolean cycle_eq(Type t) { assert _type < TCYCLIC; return _type==t._type && eq(t); }
+ // A pair of uids
+ int pid( Type that ) {
+ return _uid < that._uid ? (_uid<<16 | that._uid) : (that._uid<<16 | _uid);
+ }
+ // At/Set child at 'idx' to t
+ Type at ( int idx ) { throw Utils.TODO(); }
+ void set( int idx, Type t ) { throw Utils.TODO(); }
+ int nkids() { assert _type < TTUPLE; return 0; } // Number of kids
+
+
+ // Clear and re-insert the basic Type INTERN table
+ public static void reset() {
+ VISIT.clear();
+ FREES.clear();
+ if( INTERN0.isEmpty() ) {
+ // First time run all <clinit> and collect basic types
+ Type t = TypeRPC .BOT; // Force class load, intern
+ Type u = TypeTuple.BOT; // Force class load, intern
+ Type w = TypePtr .PTR; // Force class load, intern
+ INTERN0.putAll(INTERN);
+ } else {
+ // Later times, reset back to first time
+ VISIT.put(0,CONTROL); // Defeat assert
+ for( Iterator<Type> it = INTERN.keySet().iterator(); it.hasNext(); ) {
+ Type t = it.next();
+ if( !INTERN0.containsKey(t) )
+ { t._terned=false; t.free(t); }
+ it.remove();
+ }
+ VISIT.clear();
+ INTERN.putAll(INTERN0);
+ }
+ }
// ----------------------------------------------------------
public final Type meet(Type t) {
@@ -172,18 +289,24 @@ public class Type {
return _type==TCTRL || t._type==TCTRL ? CONTROL : XCONTROL;
}
- public Type dual() {
- return switch( _type ) {
- case TBOT -> TOP;
- case TTOP -> BOTTOM;
- case TCTRL ->XCONTROL;
- case TXCTRL-> CONTROL;
- case TNIL ->XNIL;
- case TXNIL -> NIL;
+
+ // The dual is pre-computed on creation and available "for free" thereafter
+ Type _dual;
+ public final <T extends Type> T dual() { return (T)_dual; }
+
+ <T extends Type> T xdual() {
+ return (T)new Type(switch( _type ) {
+ case TBOT -> TTOP;
+ case TTOP -> TBOT;
+ case TCTRL -> TXCTRL;
+ case TXCTRL-> TCTRL;
+ case TNIL -> TXNIL;
+ case TXNIL -> TNIL;
default -> throw Utils.TODO(); // Should not reach here
- };
+ });
}
+
// ----------------------------------------------------------
// Our lattice is defined with a MEET and a DUAL.
// JOIN is dual of meet of both duals.
@@ -203,10 +326,6 @@ public class Type {
// Make a zero version of this type, 0 for integers and null for pointers.
public Type makeZero() { return Type.NIL; }
- /** Compute greatest lower bound in the lattice. If values are in memory,
- * ints and floats cannot widen. */
- public Type glb(boolean mem) { return Type.BOTTOM; }
-
// Writable array slots are invariant in their element access permissions.
// Otherwise an alias could insert a readonly pointer into a writable slot.
public boolean accessISA(Type dst) { return true; }
@@ -214,11 +333,154 @@ public class Type {
// Is forward-reference
public boolean isFRef() { return false; }
- // All reachable struct Fields are final
- public boolean isFinal() { return true; }
+ // ----------------------------------------------------------
- // Make all reachable struct Fields final
- public Type makeRO() { return this; }
+ // Cyclic types! Flag the start of a cyclic type by putting a sentinel in
+ // VISIT (and eventually clearing VISIT when done). Then do a normal
+ // recursive descent visit of all types; this will accumilate types without
+ // interning them. When done we have to visit the possibly cyclic type and
+ // intern the whole cycle, possibly hitting the entire cycle on a prior
+ // interned cycle. Any sub-part might also be interned, including whole
+ // disjoint cycles.
+
+ // E.g. a new type: "A<->B -> C1<->D1" gets created; the "C1<->D1" cycle
+ // already exists as "C0<->D0", but the "A<->B" cycle is new. The "A<->B"
+ // cycle will get interned - except the pointer to C1 needs to get replaced
+ // with C0. The "C1<->D1" cycle gets freed and "B" will point to the
+ // "C0<->D0" cycle.
+
+ Type recurOpen() { assert VISIT.isEmpty(); VISIT.put(0L,BOTTOM); return this; }
+ Type recurClose() {
+ VISIT.remove(0L); // Just ignore the sentinel
+ Ary<Type> ts = new Ary<>(Type.class);
+ ts.addAll(VISIT.values());
+ if( ts.find(this)== -1 ) ts.add(this);
+
+ // Pass #1: replace already interned fields
+ assert BITS.isEmpty();
+ for( int i=0; i<ts._len; i++ )
+ if( ts.at(i).tern()._terned )
+ ts.del(i--);
+ BITS.clear();
+
+ // Pass#2: compute recursive duals
+ for( Type t : ts )
+ t.rdual();
+
+ // Pass#3: Recursive install
+ for( Type t : ts )
+ if( !t._terned && INTERN.get(t)==null )
+ t.install();
+ // Upgrade the result
+ Type rez = INTERN.get(this);
+ assert rez!=null;
+
+ // Pass#4?: Free up any created-but-already interned
+ for( Type t : FREES )
+ t.rfree();
+ FREES.clear();
+ VISIT.clear();
+ return rez;
+ }
+
+ // Read-only recursive visits do not *make* cyclic types and so the cleanup
+ // is much easier.
+ static boolean recurClose(boolean rez) { VISIT.clear(); return rez; }
+
+ final Type tern() {
+ if( _terned ) return this;
+ if( !BITS.get(_uid) ) {
+ BITS.set(_uid);
+ int nkids = nkids();
+ for( int i=0; i<nkids; i++ )
+ set(i,at(i).tern());
+ }
+ Type told = INTERN.get(this);
+ return told==null ? this : told.delayFree(this);
+ }
+
+ // Recursive dual. Visit all types recursively. Each visited type is
+ // *new*, and hits in the intern table, or not. If not, we recursively
+ // call rdual to get its dual, and cross-link the duals. If hitting, we
+ // use the intern type and mark the prior type for freeing.
+ Type rdual() { assert !_terned; return xdual(); }
+
+ // Recursively free type and children
+ final void rfree() {
+ if( isFree() ) return;
+ int nkids = nkids();
+ for( int i=0; i<nkids; i++ )
+ if( !at(i)._terned )
+ at(i).rfree();
+ free(this);
+ }
+
+ Type free(Type free) { return this; }
+ boolean isFree() { return false; }
+
+ <T extends Type> T delayFree(Type free) {
+ assert !free._terned;
+ FREES.push(free);
+ return (T)this;
+ }
+
+ final Type install() {
+ if( this instanceof TypeStruct ) {
+ Type x = _intern(); // Stop the recursion
+ assert x==this;
+ }
+ int nkids = nkids();
+ for( int i=0; i<nkids; i++ ) {
+ Type kid = at(i);
+ if( !kid._terned ) {
+ Type ikid = kid.install();
+ if( ikid != kid ) {
+ set(i,ikid);
+ _dual.set(i,ikid.dual());
+ }
+ }
+ }
+ return this instanceof TypeStruct ? this : _intern();
+ }
+
+ // Strict constant values, things on the lattice centerline.
+ // Excludes both high and low values
+ public final boolean isConstant() { return recurClose(recurOpen()._isConstant()); }
+ boolean _isConstant() { return _type==TNIL; }
+
+ // Are all reachable struct Fields are final?
+ public final boolean isFinal() { return recurClose(recurOpen()._isFinal()); }
+ boolean _isFinal() { assert _type < TCYCLIC; return true; }
+
+ public final Type makeRO() {
+ if( isFinal() ) return this;
+ return recurOpen()._makeRO().recurClose();
+ }
+ Type _makeRO() { return this; }
+
+ // Compute greatest lower bound in the lattice. If values are in memory,
+ // ints and floats cannot widen.
+ public final boolean isGLB(boolean mem) { return recurClose(recurOpen()._isGLB(mem)); };
+ boolean _isGLB(boolean mem) {
+ return switch(_type) {
+ case TBOT -> false;
+ case TNIL -> false;
+ case TCTRL -> true;
+ case TXCTRL -> false;
+ case TXNIL -> false;
+ case TTOP -> false;
+ default -> throw Utils.TODO();
+ };
+ }
+ public final Type glb(boolean mem) {
+ if( isGLB(mem) ) return this;
+ Type glb = recurOpen()._glb(mem).recurClose();
+ assert this.isa(glb);
+ return glb;
+ }
+ Type _glb(boolean mem) { assert is_simple(); return Type.BOTTOM; }
+
+ Type _close() { return this; }
// ----------------------------------------------------------
@@ -235,11 +497,21 @@ public class Type {
// This is a more verbose dev-friendly print.
@Override
public final String toString() {
- return print(new SB()).toString();
+ return print(new SB(), new BitSet(), false).toString();
}
-
- public SB print(SB sb) { return sb.p(str()); }
- public SB gprint(SB sb) { return print(sb); }
+ public final String gprint() {
+ return print(new SB(), new BitSet(), true).toString();
+ }
+ public final SB print(SB sb) { return print(sb, new BitSet(), false); }
+ public final SB gprint(SB sb) { return print(sb, new BitSet(), true ); }
+
+ // Used during recursive printing, or combined with SB Node printing
+ public final SB print(SB sb, BitSet visit, boolean html ) {
+ if( visit.get(_uid) ) return sb.p(str());
+ visit.set(_uid);
+ return _print(sb,visit,html);
+ }
+ SB _print(SB sb, BitSet visit, boolean html ) { return sb.p(str()); }
// This is used by error messages, and is a shorted print.
public String str() { return STRS[_type]; }
diff --git a/src/main/java/com/seaofnodes/simple/type/TypeConAry.java b/src/main/java/com/seaofnodes/simple/type/TypeConAry.java
index 692450d0..84cccd12 100644
--- a/src/main/java/com/seaofnodes/simple/type/TypeConAry.java
+++ b/src/main/java/com/seaofnodes/simple/type/TypeConAry.java
@@ -1,8 +1,8 @@
package com.seaofnodes.simple.type;
-import com.seaofnodes.simple.Utils;
-import java.util.ArrayList;
+import com.seaofnodes.simple.util.Utils;
import java.io.ByteArrayOutputStream;
+import java.util.ArrayList;
/**
* Represents a constant array of primitives
@@ -12,9 +12,8 @@ public class TypeConAry<A> extends Type {
// One of byte,short,int,long,float,double array
public final A _ary;
- TypeConAry( boolean any, A ary ) { super(TCONARY); _any = any; _ary = ary; }
- private static final TypeConAry TOP = new TypeConAry(true ,null).intern();
- public static final TypeConAry BOT = new TypeConAry(false,null).intern();
+ TypeConAry( boolean any, A ary ) { super(TINT); _any = any; _ary = ary; }
+ public static final TypeConAry BOT = new TypeConAry(false,null).intern();
public static void gather(ArrayList<Type> ts) {
ts.add(BOT);
ts.add(TypeConAryB.ABC);
@@ -23,42 +22,56 @@ public class TypeConAry<A> extends Type {
}
@Override public String str() { return (_any?"~":"") + "[]"; }
- @Override public boolean isConstant() { return true; }
- @Override public TypeConAry dual() {
- if( this== BOT ) return TOP;
- if( this== TOP ) return BOT;
+ @Override TypeConAry xdual() {
+ if( _ary==null )
+ return new TypeConAry(!_any,null);
return this;
}
@Override Type xmeet(Type t) {
+ if( t instanceof TypeInteger ti ) return imeet(ti);
TypeConAry ary = (TypeConAry)t; // Invariant
- if( this==TOP ) return ary ;
- if( ary ==TOP ) return this;
+ if( this==BOT ) return BOT;
+ if( ary ==BOT ) return BOT;
+ if( this==BOT.dual() ) return ary ;
+ if( ary ==BOT.dual() ) return this;
assert _ary!=ary._ary; // Already interned and this!=t
- return BOT;
+ return elem().meet(ary.elem());
+ }
+ Type imeet( TypeInteger ti ) {
+ if( this==BOT.dual() ) return ti;
+ if( this==BOT ) return BOT;
+ Type elem = elem();
+ if( !(elem instanceof TypeInteger) ) return BOTTOM;
+ if( ti.isHigh() && elem.isa(ti.dual()) )
+ return this;
+ return elem.meet(ti);
}
+
+ @Override public boolean isHigh() { return this==TOP; }
+ @Override boolean _isConstant() { return true; }
+
// Meet-over-elements type
public Type elem() {
if( _ary==null )
- return _any ? TOP : BOTTOM;
+ return _any ? Type.TOP : BOTTOM;
int len = len();
long min = Long.MAX_VALUE;
long max = Long.MIN_VALUE;
for( int i=0; i<len; i++ ) {
- min = Math.min(min,at(i));
- max = Math.max(max,at(i));
+ min = Math.min(min,at8(i));
+ max = Math.max(max,at8(i));
}
return TypeInteger.make(min,max);
}
- public long at(int idx) { throw Utils.TODO(); }
+ public long at8(int idx) { throw Utils.TODO(); }
public int len() { throw Utils.TODO(); }
- @Override public int alignment() { return 0; }
+ @Override public int log_size() { throw Utils.TODO(); }
public void write( ByteArrayOutputStream baos ) { throw Utils.TODO(); }
@Override boolean eq(Type t) {
- TypeConAry ary = (TypeConAry)t; // Invariant
- return _any==ary._any && ary._ary==null;
+ return t instanceof TypeConAry ary && _any==ary._any && ary._ary==null;
}
@Override int hash() {
diff --git a/src/main/java/com/seaofnodes/simple/type/TypeConAryB.java b/src/main/java/com/seaofnodes/simple/type/TypeConAryB.java
index b56beb47..75f08bef 100644
--- a/src/main/java/com/seaofnodes/simple/type/TypeConAryB.java
+++ b/src/main/java/com/seaofnodes/simple/type/TypeConAryB.java
@@ -9,10 +9,11 @@ public class TypeConAryB extends TypeConAry<byte[]> {
public static TypeConAryB make( String s ) { return make(s.getBytes()); }
static final TypeConAryB ABC = make("abc");
static final TypeConAryB ABCD = make("abcd");
- @Override public long at(int idx) { return _ary[idx]; }
+ @Override public long at8(int idx) { return _ary[idx]; }
@Override public int len() { return _ary.length; }
+ @Override public int log_size() { return 0; }
@Override public String str() { return "[\""+new String(_ary)+"\"]"; }
- @Override boolean eq(Type t) { return Arrays.equals(_ary,((TypeConAryB)t)._ary); }
+ @Override boolean eq(Type t) { return t instanceof TypeConAryB ary && Arrays.equals(_ary,ary._ary); }
@Override int hash() { return Arrays.hashCode(_ary); }
@Override public void write( ByteArrayOutputStream baos ) { baos.write(_ary,0,_ary.length); }
}
diff --git a/src/main/java/com/seaofnodes/simple/type/TypeConAryI.java b/src/main/java/com/seaofnodes/simple/type/TypeConAryI.java
index 4859e839..577de82c 100644
--- a/src/main/java/com/seaofnodes/simple/type/TypeConAryI.java
+++ b/src/main/java/com/seaofnodes/simple/type/TypeConAryI.java
@@ -6,8 +6,10 @@ public class TypeConAryI extends TypeConAry<int[]> {
private TypeConAryI(int[] bs) { super(false,bs); }
public static TypeConAryI make( int[] bs ) { return new TypeConAryI(bs).intern(); }
static final TypeConAryI I123 = make(new int[]{1,2,3});
+ @Override public long at8(int idx) { return _ary[idx]; }
+ @Override public int len() { return _ary.length; }
+ @Override public int log_size() { return 2; }
@Override public String str() { return "[i32]"; }
- @Override public int alignment() { return 2; }
@Override boolean eq(Type t) { return Arrays.equals(_ary,((TypeConAryI)t)._ary); }
@Override int hash() { return Arrays.hashCode(_ary); }
}
diff --git a/src/main/java/com/seaofnodes/simple/type/TypeFloat.java b/src/main/java/com/seaofnodes/simple/type/TypeFloat.java
index a0940f8b..f3de581c 100644
--- a/src/main/java/com/seaofnodes/simple/type/TypeFloat.java
+++ b/src/main/java/com/seaofnodes/simple/type/TypeFloat.java
@@ -1,7 +1,8 @@
package com.seaofnodes.simple.type;
-import com.seaofnodes.simple.SB;
-import com.seaofnodes.simple.Utils;
+import com.seaofnodes.simple.util.SB;
+import com.seaofnodes.simple.util.Utils;
+import com.seaofnodes.simple.util.Ary;
import java.util.ArrayList;
/**
@@ -9,30 +10,38 @@ import java.util.ArrayList;
*/
public class TypeFloat extends Type {
- public final static TypeFloat ONE = constant(1.0);
- public final static TypeFloat FZERO = constant(0.0);
- public final static TypeFloat F32 = make(32, 0);
- public final static TypeFloat F64 = make(64, 0);
-
// - high -64, high -32, con 0, low +32, low +64
- public final byte _sz;
+ public byte _sz;
/**
* The constant value or 0
*/
- public final double _con;
+ public double _con;
- private TypeFloat(byte sz, double con) {
- super(TFLT);
- _sz = sz;
- _con = con;
- }
+ private static final Ary<TypeFloat> FREE = new Ary<>(TypeFloat.class);
+ private TypeFloat(byte sz, double con) { super(TFLT); init(sz,con); }
+ private TypeFloat init( byte sz, double con ) { _sz = sz; _con = con; return this; }
+ public static TypeFloat malloc(byte sz, double con) { return FREE.isEmpty() ? new TypeFloat(sz,con) : FREE.pop().init(sz,con); }
private static TypeFloat make(int sz, double con) {
- return new TypeFloat((byte)sz,con).intern();
+ TypeFloat f = malloc((byte)sz,con);
+ TypeFloat t2 = f.intern();
+ return t2==f ? f : t2.free(f);
+ }
+ @Override TypeFloat free(Type t) {
+ TypeFloat f = (TypeFloat)t;
+ f._hash = 0;
+ f._dual=null;
+ FREE.push(f);
+ return this;
}
public static TypeFloat constant(double con) { return make(0, con); }
+ public final static TypeFloat ONE = constant(1.0);
+ public final static TypeFloat FZERO = constant(0.0);
+ public final static TypeFloat F32 = make(32, 0);
+ public final static TypeFloat F64 = make(64, 0);
+
public static void gather(ArrayList<Type> ts) { ts.add(F64); ts.add(F32); ts.add(constant(3.141592653589793)); }
@Override public String str() {
@@ -48,7 +57,6 @@ public class TypeFloat extends Type {
private boolean isF32() { return ((float)_con)==_con; }
@Override public boolean isHigh () { return _sz< 0; }
- @Override public boolean isConstant() { return _sz==0; }
@Override public int log_size() { return _sz==32 || _sz==-32 ? 2 : 3; }
public double value() { return _con; }
@@ -76,13 +84,20 @@ public class TypeFloat extends Type {
}
@Override
- public Type dual() {
- return isConstant() ? this : make(-_sz,0); // Constants are a self-dual
+ TypeFloat xdual() {
+ return _isConstant() ? this : new TypeFloat((byte)-_sz,0); // Constants are a self-dual
}
- @Override public Type makeZero() { return FZERO; }
- @Override public Type glb(boolean mem) { return mem ? (isHigh() ? dual() : this) : F64; }
+ @Override boolean _isGLB(boolean mem) { return _glb(mem)==this; }
+ @Override Type _glb(boolean mem) {
+ if( !mem ) return F64;
+ if( _isConstant() ) return isF32() ? F32 : F64;
+ return isHigh() ? dual() : this;
+ }
+ @Override boolean _isConstant() { return _sz==0; }
+
+ @Override public Type makeZero() { return FZERO; }
@Override
int hash() { return (int)(Double.hashCode(_con) ^ _sz ^ (1<<17)); }
@Override
diff --git a/src/main/java/com/seaofnodes/simple/type/TypeFunPtr.java b/src/main/java/com/seaofnodes/simple/type/TypeFunPtr.java
index e6701360..cbf06e0c 100644
--- a/src/main/java/com/seaofnodes/simple/type/TypeFunPtr.java
+++ b/src/main/java/com/seaofnodes/simple/type/TypeFunPtr.java
@@ -1,9 +1,9 @@
package com.seaofnodes.simple.type;
-import com.seaofnodes.simple.SB;
-import com.seaofnodes.simple.Utils;
-import java.util.ArrayList;
-import java.util.HashMap;
+import com.seaofnodes.simple.util.SB;
+import com.seaofnodes.simple.util.Utils;
+import com.seaofnodes.simple.util.Ary;
+import java.util.*;
/**
* Represents a Pointer to a function.
@@ -19,89 +19,243 @@ import java.util.HashMap;
*/
public class TypeFunPtr extends TypeNil {
// A TypeFunPtr is Signature and a set of functions.
- public final TypeTuple _sig;
- public final Type _ret;
+ public Type[] _sig;
+ public Type _ret;
+ // TODO:
+ boolean _open; // Extra args are true:BOTTOM, false:TOP
// Cheesy easy implementation for a small set; 1 bit per unique function
- // 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
+ // within the same Type[]. Can be upgraded to a BitSet for larger classes
+ // of functions. Negative means "these 63 concrete bits plus infinite
+ // unknown more"
+ public long _fidxs; // 63 unique functions per signature
- private TypeFunPtr(byte nil, TypeTuple sig, Type ret, long fidxs) {
- super(TFUNPTR,nil);
+ private static final Ary<TypeFunPtr> FREE = new Ary<>(TypeFunPtr.class);
+ private TypeFunPtr(byte nil, boolean open, Type[] sig, Type ret, long fidxs) { super(TFUNPTR,nil); init(nil,open,sig,ret,fidxs); }
+ private TypeFunPtr init(byte nil, boolean open, Type[] sig, Type ret, long fidxs ) {
assert sig != null;
- _sig = sig;
- _ret = ret;
- _fidxs = fidxs;
+ _nil = nil;
+ _open = open;
+ _sig = sig;
+ _ret = ret;
+ _fidxs= fidxs;
+ return this;
}
+ static TypeFunPtr malloc( byte nil, boolean open, Type[] sig, Type ret, long fidxs ) {
+ return FREE.isEmpty() ? new TypeFunPtr(nil,open,sig,ret,fidxs) : FREE.pop().init(nil,open,sig,ret,fidxs);
+ }
+ @Override TypeFunPtr free(Type t) {
+ TypeFunPtr fun = (TypeFunPtr)t;
+ assert !fun.isFree() && !fun._terned;
+ fun._sig = null;
+ fun._ret = null;
+ fun._dual = null;
+ fun._hash = 0;
+ FREE.push(fun);
+ return this;
+ }
+ @Override boolean isFree() { return _sig==null; }
+
+ // All fields directly listed
+ public static TypeFunPtr make( byte nil, boolean open, Type[] sig, Type ret, long fidxs ) {
+ // Trim trailing args which match the implicit default.
+ Type def = open ? Type.BOTTOM : Type.TOP;
+ int nargs = sig.length;
+ while( nargs > 0 && sig[nargs-1] == def )
+ nargs--;
+ if( nargs != sig.length )
+ sig = nargs == 0 ? TEMPTY : Arrays.copyOf(sig,nargs);
+ TypeFunPtr fun = malloc(nil,open,sig,ret,fidxs);
+ TypeFunPtr f2 = fun.intern();
+ if( f2==fun ) return fun;
+ return VISIT.isEmpty() ? f2.free(fun) : f2.delayFree(fun);
+ }
+ public static TypeFunPtr make( boolean nil, boolean open, Type[] sig, Type ret ) { return make((byte)(nil ? 3 : 2),open,sig,ret,-1); }
- 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); }
- 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 = make((byte)3,TypeTuple.MAIN,TypeInteger.BOT,-1);
- public static TypeFunPtr CALLOC= make((byte)3,TypeTuple.CALLOC,TypeMemPtr.BOT,-1);
- public static void gather(ArrayList<Type> ts) { ts.add(TEST); ts.add(TEST0); ts.add(BOT); ts.add(MAIN); }
+ @Override TypeFunPtr makeFrom( byte nil ) { return nil ==_nil ? this : make( nil,_open,_sig,_ret, _fidxs); }
+ public TypeFunPtr makeFrom( Type ret ) { return ret ==_ret ? this : make( _nil,_open,_sig, ret, _fidxs); }
+ public TypeFunPtr makeFrom( int fidx ) { return make((byte)2, _open, _sig,_ret,1L<<fidx ); }
+
+
+ public static final Type[] TEMPTY = new Type[0];
+ static final Type[] TINT = new Type[]{TypeInteger.BOT};
+ static final Type[] TINTMEM = new Type[]{TypeInteger.BOT,TypeFloat.F32};
+ static final Type[] TINTINT = new Type[]{TypeInteger.BOT,TypeInteger.BOT};
+ public static TypeFunPtr BOT = make((byte)3,true ,TEMPTY,Type.BOTTOM,-1);
+ public static TypeFunPtr TEST = make((byte)2,false,TINTMEM,TypeInteger.BOT, 1);
+ public static TypeFunPtr TEST0 = make((byte)3,false,TINTMEM,TypeInteger.BOT, 3);
+ public static TypeFunPtr MAIN = make((byte)3,false,TINT ,Type.BOTTOM,-1); // Main can return anything
+ public static TypeFunPtr CALLOC= make((byte)3,false,TINTINT,TypeMemPtr .BOT,-1);
+ public static void gather(ArrayList<Type> ts) {
+ ts.add(TEST); ts.add(TEST0); ts.add(BOT); ts.add(MAIN); ts.add(CALLOC);
+ // Explicit trailing defaults must agree with the implicit argument tail.
+ for( boolean open : new boolean[]{false,true} ) {
+ Type def = open ? Type.BOTTOM : Type.TOP;
+ ts.add(make((byte)2,open,TINT,TypeInteger.I32,1));
+ ts.add(make((byte)2,open,new Type[]{TypeInteger.BOT,def,def},TypeInteger.I32,1));
+ ts.add(make((byte)2,open,new Type[]{def,def},TypeInteger.I32,1));
+ ts.add(make((byte)2,open,new Type[]{def,TypeInteger.BOT},TypeInteger.I32,1));
+ }
+ }
@Override
Type xmeet(Type t) {
TypeFunPtr that = (TypeFunPtr) t;
- return TypeFunPtr.make(xmeet0(that),(TypeTuple)_sig.meet(that._sig), _ret.meet(that._ret), _fidxs | that._fidxs);
+ // if equal, no matters.
+ // if short is BOT, chop ; recurPre on short.
+ // if short is TOP, copy long; recurPre on long.
+ TypeFunPtr min = _sig.length < that._sig.length ? this : that;
+ if( _sig.length != that._sig.length && (min._open ^ min==this) )
+ return that.xmeet(this);
+ // Recurse all common fields
+ Type[] args = new Type[_sig.length];
+ for( int i=0; i<min._sig.length; i++ )
+ args[i] = _sig[i].meet(that._sig[i]);
+ // Handle extra args
+ for( int i=min._sig.length; i<_sig.length; i++ )
+ args[i] = _sig[i];
+ return make(xmeet0(that),_open | that._open, args, _ret.meet(that._ret), _fidxs | that._fidxs);
+ }
+ static Type[] xmeet( Type[] ts0, Type[] ts1) {
+ if( ts0==ts1 ) return ts0;
+ assert ts0.length==ts1.length;
+ Type[] ts = new Type[ts0.length];
+ for( int i=0; i<ts0.length; i++ )
+ ts[i] = ts0[i].meet(ts1[i]);
+ return ts;
}
- @Override
- public TypeFunPtr dual() { return TypeFunPtr.make(dual0(), _sig.dual(), _ret.dual(), ~_fidxs); }
+ @Override TypeFunPtr xdual() { return malloc(dual0(), !_open, xdual(_sig), _ret.dual(), ~_fidxs); }
+ static Type[] xdual(Type[] ts) {
+ Type[] dual = new Type[ts.length];
+ for( int i=0; i<ts.length; i++ )
+ dual[i] = ts[i].dual();
+ return dual;
+ }
+
+ @Override TypeFunPtr rdual() {
+ if( _dual!=null ) return dual();
+ assert !_terned;
+ Type[] sigs = new Type[_sig.length];
+ TypeFunPtr d = malloc(dual0(),!_open,sigs,null,~_fidxs);
+ (_dual = d)._dual = this; // Cross link duals
+ // Return rdual
+ d._ret = _ret._terned ? _ret.dual() : _ret.rdual();
+ // Signature rdual
+ for( int i=0; i<_sig.length; i++ )
+ sigs[i] = _sig[i]._terned ? _sig[i].dual() : _sig[i].rdual();
+ return d;
+ }
// RHS is NIL; do not deep-dual when crossing the centerline
- @Override public Type meet0() { return _nil==3 ? this : make((byte)3,_sig,_ret,_fidxs); }
+ @Override public Type meet0() { return _nil==3 ? this : make((byte)3,_open,_sig,_ret,_fidxs); }
+
+ @Override public boolean isHigh() { return _nil <= 1 || (_nil==2 && _fidxs==0); }
- @Override public TypeFunPtr glb(boolean mem) { return make((byte)3,_sig,_ret,-1L); }
+ @Override public boolean _isConstant() { return (_nil==2 && Long.bitCount(_fidxs)==1) || (_nil==3 && _fidxs==0); }
- @Override public boolean isHigh () { return _nil <= 1 || (_nil==2 && _fidxs==0); }
- @Override public boolean isConstant() { return (_nil==2 && Long.bitCount(_fidxs)==1) || (_nil==3 && _fidxs==0); }
+ @Override boolean _isFinal() { return true; }
+ @Override boolean _isGLB(boolean mem) { return true; }
+ @Override TypeFunPtr _glb(boolean mem) { return this; }
+ @Override TypeFunPtr _close() {
+ Type[] sig = new Type[_sig.length];
+ TypeFunPtr fun = malloc(_nil,false,sig,null,_fidxs);
+ // Now start the recursion
+ fun._ret = _ret._close();
+ for( int i=0; i<sig.length; i++ )
+ sig[i] = _sig[i]._close();
+
+ return fun;
+ }
@Override public int log_size() { return 2; } // (1<<2)==4-byte pointers
- public Type arg(int i) { return _sig._types[i]; }
+ public Type arg(int i) { return _sig[i]; }
public long fidxs() { return _fidxs; }
public Type ret() { return _ret; }
- public int nargs() { return _sig._types.length; }
+ public int nargs() { return _sig.length; }
public int fidx() { assert Long.bitCount(_fidxs)==1; return Long.numberOfTrailingZeros(_fidxs); }
+
+ @Override int nkids() { return _sig.length+1; }
+ @Override Type at( int idx ) {
+ return idx == _sig.length ? _ret : _sig[idx];
+ }
+ @Override void set( int idx, Type t ) {
+ if( idx == _sig.length ) _ret = t;
+ else _sig[idx] = t;
+ }
+
@Override
- int hash() { return Utils.fold(_sig.hashCode() ^ _ret.hashCode() ^ _fidxs ^ super.hash()); }
+ int hash() {
+ long hash = _ret.hashCode() ^ _fidxs ^ super.hash();
+ for( Type arg : _sig )
+ hash = hash*19 ^ arg.hashCode();
+ return Utils.fold(hash);
+ }
@Override
boolean eq(Type t) {
- TypeFunPtr ptr = (TypeFunPtr)t; // Invariant
- return _sig == ptr._sig && _ret == ptr._ret && _fidxs == ptr._fidxs && super.eq(ptr);
+ // Recursive; so use cyclic equals
+ if( !VISIT.isEmpty() ) {
+ assert TypeStruct.CEQUALS.isEmpty();
+ boolean rez = cycle_eq(t);
+ TypeStruct.CEQUALS.clear();
+ return rez;
+ }
+ TypeFunPtr fun = (TypeFunPtr)t; // Invariant
+ if( !super.eq(fun) || _open != fun._open || _fidxs != fun._fidxs || _sig.length != fun._sig.length ) return false;
+ if( _sig == fun._sig && _ret == fun._ret ) return true;
+
+ if( !_ret.eq(fun._ret) ) return false;
+ for( int i = 0; i < _sig.length; i++ )
+ if( !_sig[i].eq(fun._sig[i]) )
+ return false;
+ return true;
+ }
+ @Override boolean cycle_eq(Type t) {
+ if( t._type != TFUNPTR ) return false;
+ TypeFunPtr fun = (TypeFunPtr)t; // Invariant
+ if( !super.eq(fun) || _open != fun._open || _fidxs != fun._fidxs || _sig.length != fun._sig.length ) return false;
+ if( _sig == fun._sig && _ret == fun._ret ) return true;
+
+ // Check to see if we've ever compared this pair of types before;
+ // if so, then assume the cycle is equal here.
+ int pid = pid(fun);
+ if( TypeStruct.CEQUALS.find(pid)!= -1 ) return true;
+ TypeStruct.CEQUALS.push(pid);
+ if( !_ret.cycle_eq(fun._ret) ) return false;
+ for( int i = 0; i < _sig.length; i++ )
+ if( !_sig[i].cycle_eq(fun._sig[i]) )
+ return false;
+ return true;
}
- @Override public String str() { return print(new SB()).toString();
+ @Override public String str() {
+ if( this==BOT ) return "FunBot";
+ if( this==BOT.dual() ) return "FunTop";
+ return "{"+printFIDX()+"}";
}
- @Override public SB print(SB sb) { return _print(sb,false,true); }
- public SB print(SB sb, boolean n) { return _print(sb,false,n); }
- @Override public SB gprint(SB sb) { return _print(sb,true ,true); }
- private static SB _print(SB sb, boolean g, Type t) { return g ? t.gprint(sb) : t.print(sb); }
- private SB _print(SB sb, boolean g, boolean n) {
+
+ SB _print(SB sb, BitSet visit, boolean html ) {
if( this==BOT ) return sb.p("FunBot");
- if( _nil==0 && _sig==TypeTuple.TOP && _ret==Type.TOP && _fidxs==0 ) return sb.p("FunTop");
+ if( this==BOT.dual() ) return sb.p("FunTop");
sb.p(x()).p("{ ");
- 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("~");
+ if( _sig!=null )
+ for( Type t : _sig )
+ sb.p(t.str()).p(" "); // Short form in signature
+ _ret.print(sb.p(html ? "&rarr; " : "-> "),visit,html).p(" #");
+ // Print fidxs
+ return sb.p(printFIDX()).p("}").p(q());
+ }
+ String printFIDX() {
+ String tilde = isHigh() ? "~" : "";
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());
+ return tilde+fidx;
}
// Usage: for( long fidxs=fidxs(); fidxs!=0; fidxs=nextFIDX(fidxs) { int fidxs = Long.numberOfTrailingZeros(fidxs); ... }
diff --git a/src/main/java/com/seaofnodes/simple/type/TypeInteger.java b/src/main/java/com/seaofnodes/simple/type/TypeInteger.java
index 59378ef3..1d398e3b 100644
--- a/src/main/java/com/seaofnodes/simple/type/TypeInteger.java
+++ b/src/main/java/com/seaofnodes/simple/type/TypeInteger.java
@@ -1,24 +1,54 @@
package com.seaofnodes.simple.type;
-import com.seaofnodes.simple.SB;
-import com.seaofnodes.simple.Utils;
+import com.seaofnodes.simple.util.Ary;
+import com.seaofnodes.simple.util.SB;
+import com.seaofnodes.simple.util.Utils;
import java.util.ArrayList;
/**
* Integer Type
*/
public class TypeInteger extends Type {
+ /**
+ * Describes an integer *range* - everything from min to max; both min and
+ * max are inclusive. If min==max, this is a constant.
+ * <br>
+ * If min <= max, this is a below center (towards bottom).
+ * If min > max, this is an above center (towards top).
+ */
+ public long _min, _max;
+
+ private static final Ary<TypeInteger> FREE = new Ary<>(TypeInteger.class);
+ private TypeInteger(long min, long max) { super(TINT); init(min,max); }
+ private TypeInteger init(long min, long max) { _min = min; _max = max; return this; }
+ public static TypeInteger malloc(long lo, long hi) { return FREE.isEmpty() ? new TypeInteger(lo,hi) : FREE.pop().init(lo,hi); }
+ public static TypeInteger make(long lo, long hi) {
+ TypeInteger i = malloc(lo,hi);
+ TypeInteger t2 = i.intern();
+ return t2==i ? i : t2.free(i);
+ }
+ @Override TypeInteger free(Type t) {
+ TypeInteger i = (TypeInteger)t;
+ i._min = i._max = 0;
+ i._hash = 0;
+ i._dual = null;
+ FREE.push(i);
+ return this;
+ }
+
+ public static TypeInteger constant(long con) { return make(con, con); }
public final static TypeInteger ZERO= make(0,0);
public final static TypeInteger FALSE=ZERO;
public final static TypeInteger TRUE= make(1,1);
+ public final static TypeInteger TWO = make(2,2); // Shows up in some Simple tests as the starting argument
public final static TypeInteger I1 = make(-1,0);
public final static TypeInteger I8 = make(-128,127);
public final static TypeInteger I16 = make(-32768,32767);
public final static TypeInteger I32 = make(-1L<<31,(1L<<31)-1);
public final static TypeInteger BOT = make(Long.MIN_VALUE,Long.MAX_VALUE);
- public final static TypeInteger TOP = BOT.dual();
+ public final static TypeInteger TOP = (TypeInteger)BOT.dual();
public final static TypeInteger U1 = make(0,1);
public final static TypeInteger BOOL= U1;
@@ -26,30 +56,10 @@ public class TypeInteger extends Type {
public final static TypeInteger U16 = make(0,65535);
public final static TypeInteger U32 = make(0,(1L<<32)-1);
- /**
- * Describes an integer *range* - everything from min to max; both min and
- * max are inclusive. If min==max, this is a constant.
- * <br>
- * If min <= max, this is a below center (towards bottom).
- * If min > max, this is an above center (towards top).
- */
- public final long _min, _max;
-
- private TypeInteger(long min, long max) {
- super(TINT);
- _min = min;
- _max = max;
- }
-
- // Strict non-zero contract
- public static TypeInteger make(long lo, long hi) { return new TypeInteger(lo,hi).intern(); }
-
- public static TypeInteger constant(long con) { return make(con, con); }
-
public static void gather(ArrayList<Type> ts) { ts.add(I32); ts.add(BOT); ts.add(U1); ts.add(I1); ts.add(U8); }
@Override public String str() {
- if( isConstant() ) return ""+_min;
+ if( _isConstant() ) return ""+_min;
long lo = _min, hi = _max;
String x = "";
if( hi < lo ) {
@@ -59,7 +69,7 @@ public class TypeInteger extends Type {
return x+_str(lo,hi);
}
private static String _str(long lo, long hi) {
- if( lo==Long.MIN_VALUE && hi==Long.MAX_VALUE ) return "int";
+ if( lo==Long.MIN_VALUE && hi==Long.MAX_VALUE ) return "i64";
if( lo== 0 && hi== 1 ) return "bool";
if( lo== -1 && hi== 0 ) return "i1";
if( lo== -128 && hi== 127 ) return "i8";
@@ -72,7 +82,6 @@ public class TypeInteger extends Type {
}
@Override public boolean isHigh () { return _min > _max; }
- @Override public boolean isConstant() { return _min == _max; }
@Override public int log_size() {
if( isHigh() ) return 0; // High types are dead, and should never hit code emission.
@@ -80,7 +89,7 @@ public class TypeInteger extends Type {
if( this==I16 || this==U16 ) return 1; // 1<<1 == 2 bytes
if( this==I32 || this==U32 ) return 2; // 1<<2 == 4 bytes
if( this==BOT ) return 3; // 1<<3 == 8 bytes
- if( isConstant() ) { // just const here
+ if( _isConstant() ) { // just const here
if (-0xFF <= _min && _min <= 0xFF) return 0;
else if (-0xFFFF <= _min && _min <= 0xFFFF) return 1;
else if (-0xFFFFFFFFL <= _min && _min <= 0xFFFFFFFFL) return 2;
@@ -89,7 +98,7 @@ public class TypeInteger extends Type {
throw Utils.TODO();
}
- public long value() { assert isConstant(); return _min; }
+ public long value() { assert _isConstant(); return _min; }
// AND-mask of forced zeros. e.g. unsigned types will return their mask;
// u8 will return 0xFF. But also a range of 16-18 (0x10-0x12) will return
@@ -110,12 +119,15 @@ public class TypeInteger extends Type {
@Override
public Type xmeet(Type other) {
+ if( other instanceof TypeConAry ary ) return ary.imeet(this);
// Invariant from caller: 'this' != 'other' and same class (TypeInteger)
TypeInteger i = (TypeInteger)other; // Contract
return make(Math.min(_min,i._min), Math.max(_max,i._max));
}
- @Override public TypeInteger dual() { return make(_max,_min); }
+ @Override TypeInteger xdual() {
+ return _min==_max ? this : malloc(_max,_min);
+ }
@Override public TypeInteger nonZero() {
if( isHigh() ) return this;
@@ -125,10 +137,17 @@ public class TypeInteger extends Type {
return this;
}
@Override public Type makeZero() { return ZERO; }
- @Override public Type glb(boolean mem) { return mem ? (isHigh() ? dual() : this) : BOT; }
+ @Override boolean _isConstant() { return _min == _max; }
+ @Override boolean _isGLB(boolean mem) { return _glb(mem)==this; }
+ @Override Type _glb(boolean mem) {
+ if( !mem ) return BOT;
+ // GLB is not well-defined in memory; depends on the size of the memory field
+ if( _isConstant() ) return BOT;
+ return isHigh() ? dual() : this;
+ }
+
@Override int hash() { return Utils.fold(_min) * Utils.fold(_max); }
@Override public boolean eq( Type t ) {
- TypeInteger i = (TypeInteger)t; // Contract
- return _min==i._min && _max==i._max;
+ return t instanceof TypeInteger i && _min==i._min && _max==i._max;
}
}
diff --git a/src/main/java/com/seaofnodes/simple/type/TypeMem.java b/src/main/java/com/seaofnodes/simple/type/TypeMem.java
index 74bcbd8f..d5ee8bd8 100644
--- a/src/main/java/com/seaofnodes/simple/type/TypeMem.java
+++ b/src/main/java/com/seaofnodes/simple/type/TypeMem.java
@@ -1,10 +1,10 @@
package com.seaofnodes.simple.type;
-import com.seaofnodes.simple.SB;
-import com.seaofnodes.simple.Utils;
-import java.lang.Long;
-import java.util.BitSet;
+import com.seaofnodes.simple.util.Ary;
+import com.seaofnodes.simple.util.SB;
+import com.seaofnodes.simple.util.Utils;
import java.util.ArrayList;
+import java.util.BitSet;
/**
@@ -13,24 +13,44 @@ import java.util.ArrayList;
public class TypeMem extends Type {
// Which slice of memory?
- // 0 means TOP, no slice.
- // 0 means BOT, all memory.
- // N means slice#N.
- public final int _alias;
- public final Type _t; // Memory contents, some scalar type
-
- private TypeMem(int alias, Type t) {
- super(TMEM);
- assert alias!=0 || (t==Type.TOP || t==Type.BOTTOM);
+ // 1 and TOP means no slice.
+ // 1 and BOT means all memory.
+ // N means alias slice#N.
+ public int _alias;
+ public Type _t; // Memory contents, some scalar type
+
+ private static final Ary<TypeMem> FREE = new Ary<>(TypeMem.class);
+ private TypeMem(int alias, Type t) { super(TMEM); init(alias,t); }
+ private TypeMem init(int alias, Type t) {
+ assert alias>1 || (t==Type.TOP || t==Type.BOTTOM);
_alias = alias;
_t = t;
+ return this;
+ }
+ @Override TypeMem free(Type t) {
+ TypeMem mem = (TypeMem)t;
+ mem._alias= -99;
+ mem._dual = null;
+ mem._hash = 0;
+ FREE.push(mem);
+ return this;
}
+ @Override boolean isFree() { return _alias == -99; }
- public static TypeMem make(int alias, Type t) { return new TypeMem(alias,t).intern(); }
- public static final TypeMem TOP = make(0, Type.TOP );
- public static final TypeMem BOT = make(0, Type.BOTTOM);
+ public static TypeMem malloc(int alias, Type t) {
+ return FREE.isEmpty() ? new TypeMem(alias,t) : FREE.pop().init(alias,t);
+ }
+ public static TypeMem make(int alias, Type t) {
+ TypeMem f = malloc(alias,t);
+ TypeMem f2 = f.intern();
+ if( f2==f ) return f;
+ return VISIT.isEmpty() ? f2.free(f) : f2.delayFree(f);
+ }
- public static void gather(ArrayList<Type> ts) { ts.add(make(1,Type.NIL)); ts.add(make(1,TypeInteger.ZERO)); ts.add(BOT); }
+ public static final TypeMem TOP = make(1, Type.TOP );
+ public static final TypeMem BOT = make(1, Type.BOTTOM);
+
+ public static void gather(ArrayList<Type> ts) { ts.add(make(2,Type.NIL)); ts.add(make(2,TypeInteger.ZERO)); ts.add(BOT); }
@Override
TypeMem xmeet(Type t) {
@@ -39,19 +59,28 @@ public class TypeMem extends Type {
if( that==TOP ) return this;
if( this==BOT ) return BOT;
if( that==BOT ) return BOT;
- int alias = _alias==that._alias ? _alias : 0;
+ int alias = _alias==that._alias ? _alias : 1;
Type mt = _t.meet(that._t);
return make(alias,mt);
}
@Override
- public Type dual() {
- return make(_alias,_t.dual());
+ Type xdual() { return _t._dual==_t ? this : malloc(_alias,_t.dual()); }
+
+ @Override TypeMem rdual() {
+ if( _dual!=null ) return dual();
+ assert !_terned;
+ TypeMem d = malloc(_alias,null);
+ (_dual = d)._dual = this; // Cross link duals
+ d._t = _t._terned ? _t.dual() : _t.rdual();
+ return d;
}
@Override public boolean isHigh() { return _t.isHigh(); }
@Override public int log_size() { throw Utils.TODO(); }
- @Override public Type glb(boolean mem) { return make(_alias,_t.glb(true)); }
+ @Override boolean _isFinal() { return _t._isFinal(); }
+ @Override boolean _isGLB(boolean mem) { return _t._isGLB(true); }
+ @Override public Type _glb(boolean mem) { return make(_alias,_t._glb(true)); }
@Override int hash() { return 9876543 + _alias + _t.hashCode(); }
@@ -60,19 +89,21 @@ public class TypeMem extends Type {
return _alias == that._alias && _t == that._t;
}
- @Override public SB print(SB sb) {
- if( this==BOT ) return sb.p("MemBot");
- if( this==TOP ) return sb.p("MemTop");
- sb.p("#");
- if( _alias==0 ) return sb.p(_t._type==TTOP ? "TOP" : "BOT");
- return _t.print(sb.p(_alias).p(":"));
+ @Override boolean cycle_eq(Type t) {
+ TypeMem that = (TypeMem) t; // Invariant
+ return _alias == that._alias && _t.cycle_eq(that._t);
}
- @Override public SB gprint(SB sb) {
+
+ @Override int nkids() { return 1; }
+ @Override Type at( int idx ) { return _t; }
+ @Override void set( int idx, Type t ) { _t = t; }
+
+ @Override public SB _print(SB sb, BitSet visit, boolean html) {
if( this==BOT ) return sb.p("MemBot");
if( this==TOP ) return sb.p("MemTop");
sb.p("#");
if( _alias==0 ) return sb.p(_t._type==TTOP ? "TOP" : "BOT");
- return _t.gprint(sb.p(_alias).p(":"));
+ return _t.print(sb.p(_alias).p(":"),visit,html);
}
@Override public String str() { return toString(); }
diff --git a/src/main/java/com/seaofnodes/simple/type/TypeMemPtr.java b/src/main/java/com/seaofnodes/simple/type/TypeMemPtr.java
index 34033119..ad744c8f 100644
--- a/src/main/java/com/seaofnodes/simple/type/TypeMemPtr.java
+++ b/src/main/java/com/seaofnodes/simple/type/TypeMemPtr.java
@@ -1,8 +1,9 @@
package com.seaofnodes.simple.type;
-import com.seaofnodes.simple.SB;
-import com.seaofnodes.simple.Utils;
-import java.util.ArrayList;
+import com.seaofnodes.simple.util.Ary;
+import com.seaofnodes.simple.util.SB;
+import com.seaofnodes.simple.util.Utils;
+import java.util.*;
/**
* Represents a Pointer to memory.
@@ -24,35 +25,58 @@ public class TypeMemPtr extends TypeNil {
// (BOT ,false) - a not-nil void* (unspecified struct)
// (TOP ,true ) - a nil
- public final TypeStruct _obj;
+ public TypeStruct _obj;
// Deep read-only access through this pointer; independent of slot finality.
public boolean _ro;
- public final boolean _one; // Singleton instance
+ public boolean _one; // Singleton instance
- private TypeMemPtr(byte nil, TypeStruct obj, boolean singleton) {
- super(TMEMPTR,nil);
- assert obj!=null;
- _obj = obj;
- _ro = obj==TypeStruct.BOT;
- _one = singleton;
+ private static final Ary<TypeMemPtr> FREE = new Ary<>(TypeMemPtr.class);
+ private TypeMemPtr(byte nil, TypeStruct obj, boolean one) { super(TMEMPTR,nil); init(nil,obj,one); }
+ private TypeMemPtr init(byte nil, TypeStruct obj, boolean one) { _nil=nil; _obj=obj; _ro=obj==TypeStruct.BOT; _one=one; return this; }
+ // Return a filled-in TypeMemPtr; either from free list or alloc new.
+ private static TypeMemPtr malloc(byte nil, TypeStruct obj, boolean one) {
+ return FREE.isEmpty() ? new TypeMemPtr(nil,obj,one) : FREE.pop().init(nil,obj,one);
}
- public static TypeMemPtr make(byte nil, TypeStruct obj, boolean one) { return new TypeMemPtr(nil, obj, one).intern(); }
+
+ // All fields directly listed
+ public static TypeMemPtr make(byte nil, TypeStruct obj, boolean one) {
+ return make(nil,obj,one,obj==TypeStruct.BOT);
+ }
+ private static TypeMemPtr make(byte nil, TypeStruct obj, boolean one, boolean ro) {
+ TypeMemPtr tmp = malloc(nil, obj, one).access(ro);
+ TypeMemPtr t2 = tmp.intern();
+ if( t2==tmp ) return tmp;
+ return VISIT.isEmpty() ? t2.free(tmp) : t2.delayFree(tmp);
+ }
+ @Override TypeMemPtr free(Type t) {
+ TypeMemPtr tmp = (TypeMemPtr)t;
+ tmp._nil = -99;
+ tmp._obj = null;
+ tmp._dual = null;
+ tmp._hash = 0;
+ FREE.push(tmp);
+ return this;
+ }
+ @Override boolean isFree() { return _obj==null; }
+
static TypeMemPtr make(byte nil, TypeStruct obj) { return make(nil, obj, false); }
public static TypeMemPtr make (TypeStruct obj) { return make((byte)2, obj); }
public static TypeMemPtr makeNullable(TypeStruct obj) { return make((byte)3, obj); }
- public TypeMemPtr makeFrom(TypeStruct obj) { return obj==_obj ? this : make(_nil, obj, _one).withAccess(_ro); }
+ public TypeMemPtr makeFrom(TypeStruct obj) { return obj==_obj ? this : make(_nil, obj, _one,_ro); }
public TypeMemPtr makeNullable() { return makeFrom((byte)3); }
- @Override TypeMemPtr makeFrom(byte nil) { return nil==_nil ? this : make(nil, _obj, _one).withAccess(_ro); }
- @Override public TypeMemPtr makeRO() { return withAccess(true); }
- @Override public boolean isFinal() { return _ro; }
+ @Override TypeMemPtr makeFrom(byte nil) { return nil==_nil ? this : make(nil, _obj, _one,_ro); }
+
+ private TypeMemPtr access(boolean ro) { _ro=ro; return this; }
public TypeMemPtr withAccess(boolean ro) {
if( _ro==ro ) return this;
- TypeMemPtr ptr = new TypeMemPtr(_nil, _obj, _one);
+ TypeMemPtr ptr = malloc(_nil, _obj, _one);
ptr._ro = ro;
- return ptr.intern();
+ TypeMemPtr res = ptr.intern();
+ if( res==ptr ) return ptr;
+ return VISIT.isEmpty() ? res.free(ptr) : res.delayFree(ptr);
}
// Writable slots can load and store references, so their element access
@@ -61,7 +85,7 @@ public class TypeMemPtr extends TypeNil {
if( !(dst instanceof TypeMemPtr ptr) ) return true;
if( _ro && !ptr._ro ) return false;
if( ptr._ro || _obj._fields==null || ptr._obj._fields==null || !_obj.isAry() || !ptr._obj.isAry() ) return true;
- Type a = _obj._fields[1]._type, b = ptr._obj._fields[1]._type;
+ Type a = _obj._fields[1]._t, b = ptr._obj._fields[1]._t;
return a.accessISA(b) && b.accessISA(a);
}
@@ -77,16 +101,32 @@ public class TypeMemPtr extends TypeNil {
@Override
public TypeNil xmeet(Type t) {
TypeMemPtr that = (TypeMemPtr) t;
- return make(xmeet0(that), (TypeStruct)_obj.meet(that._obj), _one & that._one).withAccess(_ro | that._ro);
+ assert !isFree() && !that.isFree();
+ return make(xmeet0(that), (TypeStruct)_obj.meet(that._obj), _one & that._one,_ro | that._ro);
}
@Override
- public TypeMemPtr dual() { return make( dual0(), _obj.dual(), !_one).withAccess(!_ro); }
+ TypeMemPtr xdual() { return malloc( dual0(), _obj.dual(), _one).access(!_ro); }
+
+ @Override TypeMemPtr rdual() {
+ if( _dual!=null ) return dual();
+ assert !_terned;
+ TypeMemPtr d = malloc(dual0(), null, _one).access(!_ro);
+ (_dual = d)._dual = this; // Cross link duals
+ d._obj = _obj._terned ? _obj.dual() : _obj.rdual();
+ return d;
+ }
+
+ // RHS is NIL; do not deep-dual when crossing the center line
+ @Override Type meet0() { return _nil==3 ? this : make((byte)3,_obj,false,_ro); }
- // RHS is NIL; do not deep-dual when crossing the centerline
- @Override Type meet0() { return _nil==3 ? this : make((byte)3,_obj).withAccess(_ro); }
+ @Override boolean _isConstant() { return _one && _obj._isConstant(); }
+ @Override boolean _isFinal() { return _ro; }
+ @Override TypeMemPtr _makeRO() { return withAccess(true); }
+ @Override boolean _isGLB(boolean mem) { return _ro && _nil==3 && !_one && _obj.isGLB2(); }
+ @Override TypeMemPtr _glb(boolean mem) { return make((byte)3,_obj.glb2(),false,true); }
+ @Override TypeMemPtr _close() { return makeFrom(_obj._close()); }
- @Override public boolean isConstant() { return _one && _obj.isConstant(); }
// True if this "isa" t up to named structures
@Override public boolean shallowISA( Type t ) {
@@ -99,7 +139,6 @@ public class TypeMemPtr extends TypeNil {
throw Utils.TODO(); // return _obj.shallowISA(that._obj);
}
- @Override public TypeMemPtr glb(boolean mem) { return make((byte)3,_obj.glb(true)).withAccess(true); }
// Is forward-reference
@Override public boolean isFRef() { return _obj.isFRef(); }
@@ -109,9 +148,18 @@ public class TypeMemPtr extends TypeNil {
@Override boolean eq(Type t) {
TypeMemPtr ptr = (TypeMemPtr)t; // Invariant
- return _ro==ptr._ro && _obj == ptr._obj && _one == ptr._one && super.eq(ptr);
+ return _ro==ptr._ro && super.eq(ptr) && _one == ptr._one && _obj == ptr._obj;
+ }
+ @Override boolean cycle_eq(Type t) {
+ if( t._type != TMEMPTR ) return false;
+ TypeMemPtr ptr = (TypeMemPtr)t; // Invariant
+ return _ro==ptr._ro && super.eq(ptr) && _one == ptr._one && _obj.cycle_eq(ptr._obj);
}
+ @Override int nkids() { return 1; }
+ @Override Type at( int idx ) { return _obj; }
+ @Override void set( int idx, Type t ) { _obj = (TypeStruct)t; }
+
@Override public String str() {
if( this==BOT ) return "PtrBot";
if( this==TOP ) return "PtrTop";
@@ -119,16 +167,10 @@ public class TypeMemPtr extends TypeNil {
return x()+"*"+_obj.str()+q();
}
- @Override public SB print(SB sb) {
- if( this==BOT ) return sb.p("PtrBot");
- if( this==TOP ) return sb.p("PtrTop");
- if( this== NOTBOT) return sb.p("*void");
- return _obj.print(sb.p(x()).p("*")).p(q());
- }
- @Override public SB gprint(SB sb) {
+ @Override SB _print(SB sb, BitSet visit, boolean html ) {
if( this==BOT ) return sb.p("PtrBot");
if( this==TOP ) return sb.p("PtrTop");
if( this== NOTBOT) return sb.p("*void");
- return _obj.gprint(sb.p(x()).p("*")).p(q());
+ return _obj.print(sb.p(x()).p("*"),visit,html).p(q());
}
}
diff --git a/src/main/java/com/seaofnodes/simple/type/TypeNil.java b/src/main/java/com/seaofnodes/simple/type/TypeNil.java
index 3902c40b..56d73b00 100644
--- a/src/main/java/com/seaofnodes/simple/type/TypeNil.java
+++ b/src/main/java/com/seaofnodes/simple/type/TypeNil.java
@@ -1,7 +1,7 @@
package com.seaofnodes.simple.type;
-import com.seaofnodes.simple.SB;
-import com.seaofnodes.simple.Utils;
+import com.seaofnodes.simple.util.SB;
+import com.seaofnodes.simple.util.Utils;
import java.util.ArrayList;
/**
@@ -12,7 +12,7 @@ public abstract class TypeNil extends Type {
// 1 = high-subclass no nil
// 2 = low -subclass no nil
// 3 = low -subclass also nil
- final byte _nil;
+ byte _nil;
TypeNil(byte t, byte nil ) { super(t); _nil = nil; }
@@ -45,18 +45,18 @@ public abstract class TypeNil extends Type {
}
@Override public boolean isHigh () { return _nil <= 1; }
- @Override public boolean isConstant () { return false; }
@Override public boolean isHighOrConst() { return isHigh() || isConstant(); }
- @Override public Type glb(boolean mem) { return Type.NIL; }
-
public boolean notNull() { return _nil==1 || _nil==2; }
public boolean nullable() { return _nil==3; }
+ @Override boolean _isConstant() { return false; }
+ @Override Type _glb(boolean mem) { return Type.NIL; }
+
final String q() { return _nil == 1 || _nil == 2 ? "" : "?"; }
final String x() { return isHigh() ? "~" : ""; }
int hash() { return _nil<<17; }
- boolean eq(TypeNil ptr) { return _nil == ptr._nil; }
+ @Override boolean eq(Type ptr) { return ptr instanceof TypeNil nil && _nil ==nil._nil; }
}
diff --git a/src/main/java/com/seaofnodes/simple/type/TypePtr.java b/src/main/java/com/seaofnodes/simple/type/TypePtr.java
index d9de5cd7..75478119 100644
--- a/src/main/java/com/seaofnodes/simple/type/TypePtr.java
+++ b/src/main/java/com/seaofnodes/simple/type/TypePtr.java
@@ -1,13 +1,15 @@
package com.seaofnodes.simple.type;
-import com.seaofnodes.simple.SB;
-import com.seaofnodes.simple.Utils;
+import com.seaofnodes.simple.util.SB;
+import com.seaofnodes.simple.util.Utils;
import java.util.ArrayList;
+import java.util.BitSet;
/**
* Represents a Scalar; a single register-sized value.
*/
public class TypePtr extends TypeNil {
+ private static final String[] STRS = new String[]{"~ptr","~nptr","nptr","ptr"};
private TypePtr(byte nil) { super(TPTR,nil); }
@@ -15,8 +17,8 @@ public class TypePtr extends TypeNil {
// Can also be null or not, so 4 choices {TOP,BOT} x {nil,not}
public static TypePtr XPTR = new TypePtr((byte)0).intern();
public static TypePtr XNPTR= new TypePtr((byte)1).intern();
- public static TypePtr NPTR = new TypePtr((byte)2).intern();
- public static TypePtr PTR = new TypePtr((byte)3).intern();
+ public static TypePtr NPTR = (TypePtr)XNPTR.dual();
+ public static TypePtr PTR = (TypePtr)XPTR.dual();
private static final TypePtr[] PTRS = new TypePtr[]{XPTR,XNPTR,NPTR,PTR};
public static void gather(ArrayList<Type> ts) { ts.add(PTR); ts.add(NPTR); }
@@ -28,7 +30,7 @@ public class TypePtr extends TypeNil {
return PTRS[xmeet0(that)];
}
- @Override public TypePtr dual() { return PTRS[dual0()]; }
+ @Override TypePtr xdual() { return new TypePtr((byte)(3-_nil)); }
// High scalar loses, low scalar wins
@Override TypeNil nmeet(TypeNil tn) {
@@ -45,9 +47,9 @@ public class TypePtr extends TypeNil {
// 0->xscalar, 1->nscalar, 2->nscalar, 3->scalar
@Override Type meetX() { return _nil==0 ? XNIL : (_nil==3 ? PTR : NPTR); }
- @Override public TypePtr glb(boolean mem) { return PTR; }
+ boolean _isGLB(boolean mem) { return this==PTR; }
+ @Override TypePtr _glb(boolean mem) { return PTR; }
- private static final String[] STRS = new String[]{"~ptr","~nptr","nptr","ptr"};
@Override public String str() { return STRS[_nil]; }
- @Override public SB print(SB sb) { return sb.p(str()); }
+ @Override public SB _print(SB sb, BitSet visit, boolean html) { return sb.p(str()); }
}
diff --git a/src/main/java/com/seaofnodes/simple/type/TypeRPC.java b/src/main/java/com/seaofnodes/simple/type/TypeRPC.java
index ec2f5f0a..bd5e2923 100644
--- a/src/main/java/com/seaofnodes/simple/type/TypeRPC.java
+++ b/src/main/java/com/seaofnodes/simple/type/TypeRPC.java
@@ -1,7 +1,7 @@
package com.seaofnodes.simple.type;
-import com.seaofnodes.simple.SB;
-import com.seaofnodes.simple.Utils;
+import com.seaofnodes.simple.util.SB;
+import com.seaofnodes.simple.util.Utils;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.HashSet;
@@ -48,10 +48,6 @@ public class TypeRPC extends Type {
return "$["+(_any ? "-" : "")+_rpcs+"]";
}
- @Override public boolean isConstant() {
- return !_any && _rpcs.size()==1;
- }
-
@Override
public TypeRPC xmeet(Type other) {
TypeRPC rpc = (TypeRPC)other;
@@ -87,7 +83,10 @@ public class TypeRPC extends Type {
}
@Override
- public Type dual() { return make(!_any,_rpcs); }
+ Type xdual() { return new TypeRPC(!_any,_rpcs); }
+
+ @Override boolean _isConstant() { return !_any && _rpcs.size()==1; }
+ @Override boolean _isGLB(boolean mem) { return true; }
@Override
int hash() { return _rpcs.hashCode() ^ (_any ? -1 : 0) ; }
diff --git a/src/main/java/com/seaofnodes/simple/type/TypeStruct.java b/src/main/java/com/seaofnodes/simple/type/TypeStruct.java
index cde0bc93..f065126a 100644
--- a/src/main/java/com/seaofnodes/simple/type/TypeStruct.java
+++ b/src/main/java/com/seaofnodes/simple/type/TypeStruct.java
@@ -1,8 +1,8 @@
package com.seaofnodes.simple.type;
-import com.seaofnodes.simple.SB;
-import com.seaofnodes.simple.Utils;
-import java.util.ArrayList;
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.util.*;
+import java.util.*;
/**
* Represents a struct type.
@@ -11,69 +11,130 @@ public class TypeStruct extends Type {
// A Struct has a name and a set of fields; the fields themselves have
// names, types and aliases. The name has no semantic meaning, but is
- // useful for debugging. Briefly during parsing a Struct can be a
- // forward-ref; in this case the _fields array is null.
-
- // Its illegal to attempt to load a field from a forward-ref struct.
-
- // Example: "int rez = new S.x; struct S { int x; } return rez;" // Error, S not defined
- // Rewrite: "struct S { int x; } int rez = new S.x; return rez;" // Ok, no forward ref
- //
- // During the normal optimization run, struct types "bottom out" at further
- // struct references, so we don't have to handle e.g. cyclic types. The
- // "bottom out" is again the forward-ref struct.
- public final String _name;
- public final Field[] _fields;
- public final TypeConAry _con; // Optional, constant array contents
-
- private TypeStruct(String name, TypeConAry con, Field[] fields) {
+ // useful for debugging.
+
+ // During parsing a mid-declaration struct is flagged as a "open". It is
+ // treated as having infinite fields with correct name and type BOTTOM.
+
+ public String _name; // Struct name
+ public boolean _open; // infinite fields are all true:BOTTOM, false:TOP
+ public Field[] _fields;
+
+ private TypeStruct(String name, boolean open, Field[] fields) {
super(TSTRUCT);
_name = name;
+ _open = open;
_fields = fields;
- _con = con;
}
+ private static final Ary<TypeStruct> FREE = new Ary<>(TypeStruct.class);
+ // Return a filled-in TypeStruct; either from free list or alloc new.
+ private static TypeStruct malloc(String name, boolean open, Field[] fields) {
+ if( FREE.isEmpty() ) return new TypeStruct(name,open,fields);
+ TypeStruct ts = FREE.pop();
+ assert ts.isFree();
+ ts._name = name;
+ ts._open = open;
+ ts._fields = fields;
+ return ts;
+ }
+ // Free ts; return this.
+ @Override TypeStruct free(Type t) {
+ TypeStruct ts = (TypeStruct)t;
+ assert !ts.isFree() && !ts._terned;
+ ts._fields = null;
+ ts._offs = null;
+ ts._dual = null;
+ ts._hash = 0;
+ FREE.push(ts);
+ return this;
+ }
+ @Override boolean isFree() { return _fields==null; }
+
+
// All fields directly listed
- public static TypeStruct make(String name, TypeConAry con, Field... fields) { return new TypeStruct(name, con, fields).intern(); }
- public static TypeStruct make(String name, Field... fields) { return make(name, TypeConAry.BOT, fields); }
- public static final TypeStruct TOP = make("$TOP",new Field[0]);
- public static final TypeStruct BOT = make("$BOT",new Field[0]);
- public static final TypeStruct TEST = make("test",new Field[]{Field.TEST});
- // Forward-ref version
- public static TypeStruct makeFRef(String name) { return make(name, (Field[])null); }
- // Make a read-only version
- @Override public TypeStruct makeRO() {
- if( isFinal() ) return this;
- Field[] flds = new Field[_fields.length];
- for( int i=0; i<flds.length; i++ )
- flds[i] = _fields[i].makeRO();
- return make(_name,flds);
+ public static TypeStruct make(String name, boolean open, Field... fields) {
+ TypeStruct ts = malloc(name, open, fields);
+ TypeStruct t2 = ts.intern();
+ if( t2==ts ) return ts;
+ return VISIT.isEmpty() ? t2.free(ts) : ts.delayFree(ts);
}
+ // New open struct with no fields
+ public static TypeStruct open( String name ) { return make(name,true); }
// Array
- public static TypeStruct makeAry(String name, TypeInteger len, int lenAlias, Type body, int bodyAlias) {
- return makeAry(name,len,lenAlias,body,bodyAlias,false,TypeConAry.BOT);
+ public static TypeStruct makeAry(String name, TypeInteger len, int lenAlias, Type body, int bodyAlias, boolean efinal) {
+ return make(name,false,
+ Field.make("#" ,len , lenAlias,true ,false),
+ Field.make("[]",body,bodyAlias,efinal,false));
}
- public static TypeStruct makeAry(String name, TypeInteger len, int lenAlias, Type body, int bodyAlias, boolean efinal, TypeConAry con) {
- assert body instanceof TypeInteger || body instanceof TypeFloat || (body instanceof TypeNil tn && tn.nullable());
- assert efinal || con==TypeConAry.BOT; // No mutable constant arrays
- return make(name,
- con,
- Field.make("#" ,len , lenAlias,true ),
- Field.make("[]",body,bodyAlias,efinal));
+
+ public TypeStruct add( Field f ) {
+ assert _open && find(f._fname)==-1; // No double field names
+ Field[] flds = Arrays.copyOf(_fields,_fields.length+1);
+ flds[_fields.length] = f;
+ return make(_name,true,flds);
+ }
+ public TypeStruct replace( Field f ) {
+ assert !_open;
+ Field[] flds = Arrays.copyOf(_fields,_fields.length);
+ flds[find(f._fname)] = f;
+ return make(_name,false,flds);
}
- // A pair of self-cyclic types
- private static final TypeStruct S1F = makeFRef("S1");
- private static final TypeStruct S2F = makeFRef("S2");
- 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("[i64]",TypeInteger.U32,-1,TypeInteger.BOT,-2);
- private static final TypeStruct STR = makeAry("[u8]",TypeInteger.U32,-1,TypeInteger.U8,-2);
- private static final TypeStruct HELLO= makeAry("[u8]",TypeInteger.constant(3),-1,TypeInteger.U8,-2,true,TypeConAryB.ABC);
+ public final TypeStruct close() {
+ return (TypeStruct)recurOpen()._close().recurClose();
+ }
+ @Override TypeStruct _close() {
+ Object key = isAry() ? _uid : _name;
+ TypeStruct ts = (TypeStruct)VISIT.get(key);
+ if( ts!=null ) return ts;
+ ts = recurPre(key,false);
+ Field[] flds = ts._fields;
+
+ // Now start the recursion
+ for( int i=0; i<flds.length; i++ )
+ flds[i].setType(_fields[i]._t._close());
+
+ return ts;
+ }
+
+ static final AryInt CEQUALS = new AryInt();
+
+ public static final TypeStruct BOT = open("$STRUCT");
+ public static final TypeStruct TOP = BOT.dual();
+ public static final TypeStruct TEST= make("test",false,Field.TEST);
+ private static final TypeStruct ARY = makeAry("[]i64",TypeInteger.U32,-1,TypeInteger.BOT,-2,false);
+ private static final TypeStruct STR = makeAry("[]u8" ,TypeInteger.U32,-1,TypeInteger.U8 ,-4,false);
+ private static final TypeStruct ABC = makeAry("[]u8",TypeInteger.constant(3),-1,TypeConAryB.ABC,-4,true);
- public static void gather(ArrayList<Type> ts) { ts.add(TEST); ts.add(BOT); ts.add(S1); ts.add(S2); ts.add(ARY); ts.add(STR); ts.add(HELLO); }
+ // A pair of self-cyclic types
+ private static final TypeStruct SINT0 = open("%SINT");
+ private static final TypeStruct SFLT0 = open("%SFLT");
+ private static final TypeStruct SINT1 = SINT0.add(Field.make("a", TypeInteger.U32, -1, false, false)).add(Field.make("s2",TypeMemPtr.make((byte)2,SFLT0),-2, false, false));
+ private static final TypeStruct SFLT1 = SFLT0.add(Field.make("b", TypeFloat .F32, -3, false, false)).add(Field.make("s1",TypeMemPtr.make((byte)2,SINT1),-4, false, false));
+ public static final TypeStruct SFLT2 = SFLT1.close();
+
+
+ public static void gather(ArrayList<Type> ts) {
+ ts.add(BOT);
+ ts.add(TEST);
+ ts.add(ARY);
+ ts.add(STR);
+ ts.add(ABC);
+ ts.add(SINT0);
+ ts.add(SFLT0);
+ ts.add(SINT1);
+ ts.add(SFLT1);
+ ts.add(SFLT2);
+ ts.add(((TypeMemPtr)(SFLT2.field("s1")._t))._obj);
+ // Break cyclic init: built a struct
+ Field fcalloc = Field.make("calloc",TypeFunPtr.CALLOC,-2,true,true);
+ TypeStruct scalloc = make("calloc",false,fcalloc);
+ ts.add(scalloc);
+
+ }
// Find field index by name
public int find(String fname) {
@@ -95,10 +156,10 @@ public class TypeStruct extends Type {
return -1;
}
-
@Override
- Type xmeet(Type t) {
+ TypeStruct xmeet(Type t) {
TypeStruct that = (TypeStruct) t;
+ assert !isFree() && !that.isFree();
if( this==TOP ) return that;
if( that==TOP ) return this;
if( this==BOT ) return BOT;
@@ -112,96 +173,154 @@ public class TypeStruct extends Type {
// And the answer is: "don't ask".
if( !_name.equals(that._name) )
return BOT; // It's a struct; that's about all we know
- if( this._fields==null ) return that;
- if( that._fields==null ) return this;
- if( _fields.length != that._fields.length ) return BOT;
- // Just do field meets
- Field[] flds = new Field[_fields.length];
- for( int i=0; i<_fields.length; i++ ) {
+
+ // if equal, no matters.
+ // if short is BOT, chop ; recurPre on short.
+ // if short is TOP, copy long; recurPre on long.
+ TypeStruct min = _fields.length < that._fields.length ? this : that;
+ if( _fields.length != that._fields.length && (min._open ^ min==this) )
+ return that.xmeet(this);
+
+ // Check all other fields are sanely similar; same struct type but
+ // different field contents (e.g. field "age" is either 'int' or '18').
+ int len = Math.min(_fields.length,that._fields.length);
+ for( int i=0; i<len; i++ ) {
Field f0 = _fields[i], f1 = that._fields[i];
- if( !f0._fname.equals(f1._fname) || f0._alias != f1._alias )
+ if( !f0._fname.equals(f1._fname) || f0._alias != f1._alias || f0._one != f1._one )
return BOT;
- flds[i] = (Field)f0.meet(f1);
}
- TypeConAry con = (TypeConAry)_con.meet(that._con);
- return make(_name, con, flds);
+
+ // See if we get a mid-recursion hit; breaks cycles
+ Integer pid = pid(that);
+ TypeStruct ts = (TypeStruct)VISIT.get(pid);
+ if( ts!=null ) return ts;
+ // If not mid-recursion, we are now; flag recursion.
+ boolean notRecur = VISIT.isEmpty();
+ if( notRecur ) recurOpen();
+
+ // Setup and install the type, prior to recursing, so we can find our
+ // recursive self again.
+ ts = recurPre(pid,_open | that._open);
+
+ // Recurse all common fields
+ Field[] flds = ts._fields;
+ for( int i=0; i<len; i++ ) {
+ Field f0 = _fields[i], f1 = that._fields[i];
+ flds[i].setType(f0._t.meet(f1._t ));
+ flds[i]._final = f0._final | f1._final;
+ }
+
+ // Handle extra fields via `_open`
+ for( int i=len; i<_fields.length; i++ )
+ flds[i].setType(_fields[i]._t );
+
+ // Cleanup post-recursion; intern/install at cycle end
+ if( notRecur ) ts = (TypeStruct)ts.recurClose();
+ return ts;
}
@Override
- public TypeStruct dual() {
- if( this==TOP ) return BOT;
- if( this==BOT ) return TOP;
- if( _fields == null ) return this;
+ TypeStruct xdual() {
+ if( _name=="$STRUCT" )
+ return malloc("$STRUCT",false,new Field[0]);
Field[] flds = new Field[_fields.length];
for( int i=0; i<_fields.length; i++ )
flds[i] = _fields[i].dual();
- TypeConAry con = _con.dual();
- return make(_name,con,flds);
+ return malloc(_name,!_open,flds);
}
- // Keeps the same struct, but lower-bounds all fields.
- @Override public TypeStruct glb(boolean mem) {
- if( _glb(mem) ) return this;
- // Need to glb each field
+ // Recursive dual
+ @Override TypeStruct rdual() {
+ if( _dual!=null ) return dual();
+ assert !_terned;
Field[] flds = new Field[_fields.length];
+ TypeStruct d = malloc(_name,!_open,flds);
+ (_dual = d)._dual = this; // Cross link duals
for( int i=0; i<_fields.length; i++ )
- flds[i] = _fields[i].glb(mem);
- return make(_name,flds);
- }
- private boolean _glb(boolean mem) {
- if( _fields!=null )
- for( Field f : _fields )
- if( f.glb(mem) != f )
- return false;
- return true;
+ flds[i] = _fields[i]._terned ? _fields[i].dual() : _fields[i].rdual();
+ return d;
}
// Is forward-reference
- @Override public boolean isFRef() { return _fields==null; }
+ @Override public boolean isFRef() { return _open; }
+
+ @Override boolean _isConstant() {
+ if( VISIT.containsKey(_uid) ) return true; // Cycles assume constant
+ VISIT.put(_uid,this);
+ // Check all fields for being constant
+ for( Field field : _fields )
+ if( !field._isConstant() )
+ return false;
+ return true;
+ }
+
// All fields are final
- @Override public boolean isFinal() {
- if( _fields==null ) return true;
+ @Override boolean _isFinal() {
+ if( _open ) return false; // May have more more non-final fields
+ if( VISIT.containsKey(_uid) ) // Test: been here before?
+ return true; // Cycles assume final
+ VISIT.put(_uid,this); // Set: dont do this again
for( Field fld : _fields )
- if( !fld.isFinal() )
+ if( !fld._isFinal() )
return false;
return true;
}
- @Override public boolean isConstant() {
- int len = _fields.length;
- // If we have a constant array, then ignore the last 2 array fields-
- // they are effectively constant.
-
- // Fields are given sizes according to their types at *offset time*.
- // And thus layout, packing & padding are determined by the Types
- // at *offset-time*, which means *offset-time* has to be picked
- // when types are Stable - post/during TypeCheck.
-
- // The array-length field is shared with other like-sized arrays, so
- // you are allowed to mix "abc" and "very_hugh_>256_char_string" and
- // the resulting TMP can query the length field (getting 3 or >256
- // respectively), and reading from a prolly 16bit length field.
-
- // Basically asking: forward-flow declared-types, are these compatible?
- // Only array-lengths have an issue here? So internally name fields
- // `#0` or `#1` or `#2` or `#3`.
- //
- // Also means I cannot track exact array length without losing the
- // "declared type requires a 4-byte field".
- //
- // Same-same for all fields in a struct, space in memory is based on
- // declared-type at least, possible (future work) optimized where all
- // memory versions of the same field get a smaller footprint (or even a
- // constant past or moved into prototype) past construction
-
- if( _con!=TypeConAry.BOT ) len -= 2;
- // Check all fields for being constant
- for( int i=0; i<len; i++ )
- if( !_fields[i].isConstant() )
+ private TypeStruct recurPre(Object key, boolean open) {
+ // Make a clone of original; suitable for hashing so can build
+ // e.g. TMPs to clone as part of cycles - but will fail the 'eq' check
+ // until the entire cycle is built.
+ Field[] flds = new Field[_fields.length];
+ for( int i=0; i<flds.length; i++ )
+ flds[i] = _fields[i].malloc(); // Blank copy, but can be hashed
+ TypeStruct ts = malloc(_name, open, flds );
+ VISIT.put(key,ts);
+ return ts;
+ }
+
+ // Make a read-only version
+ @Override TypeStruct _makeRO() {
+ // Check for already visited
+ TypeStruct ts = (TypeStruct)VISIT.get(_name);
+ if( ts!=null ) return ts; // Already visited
+ ts = recurPre(_name,_open); // Make a new type with blank fields
+ Field[] flds = ts._fields;
+ for( Field fld : flds ) fld._final = true;
+
+ // Now start the recursion
+ for( int i=0; i<flds.length; i++ )
+ flds[i].setType(_fields[i]._t._makeRO());
+
+ return ts;
+ }
+
+
+ // All fields are at GLB already
+ boolean isGLB2() {
+ if( VISIT.containsKey(_uid) ) return true; // Cycles assume GLB
+ VISIT.put(_uid,this);
+ for( Field fld : _fields )
+ if( !fld.isGLB2() )
return false;
return true;
}
+ // Keeps the same struct, but lower-bounds all fields.
+ public TypeStruct glb2() {
+ TypeStruct ts = (TypeStruct)VISIT.get(_name);
+ if( ts!=null ) return ts;
+ ts = recurPre(_name,_open);
+ Field[] flds = ts._fields;
+ for( int i=0; i<flds.length; i++ )
+ flds[i]._final = true ;
+
+ // Now start the recursion
+ for( int i=0; i<flds.length; i++ )
+ flds[i].setType(_fields[i]._t._glb(true));
+
+ return ts;
+ }
+
// log_size for a struct is not defined, unless its exactly some power of
// 2. *Total size* is well-defined, and is available in the offsets.
@Override public int log_size() { throw Utils.TODO(); }
@@ -209,57 +328,84 @@ public class TypeStruct extends Type {
@Override public int alignment() {
int align = 0;
for( Field f : _fields )
- align = Math.max(align, (f._fname!="[]" || _con==TypeConAry.BOT ? f._type : _con).alignment());
+ align = Math.max(align, f._t.alignment());
return align;
}
- @Override
- boolean eq(Type t) {
+ // If false, always false.
+ // If true , maybe true, need to check recursive fields.
+ private boolean static_eq( TypeStruct ts ) {
+ return _name.equals(ts._name) && _open==ts._open && _fields.length==ts._fields.length;
+ }
+
+ @Override boolean eq(Type t) {
+ // Recursive; so use cyclic equals
+ if( !VISIT.isEmpty() ) {
+ assert CEQUALS.isEmpty();
+ boolean rez = cycle_eq(t);
+ CEQUALS.clear();
+ return rez;
+ }
+ // Normal equals
+ TypeStruct ts = (TypeStruct)t; // Invariant
+ if( !static_eq(ts) ) return false;
+ if( _fields==ts._fields ) return true;
+ for( int i = 0; i < _fields.length; i++ )
+ if( !_fields[i].eq(ts._fields[i]) )
+ return false;
+ return true;
+ }
+
+ @Override boolean cycle_eq(Type t) {
+ if( this==t ) return true;
TypeStruct ts = (TypeStruct)t; // Invariant
- if( !_name.equals(ts._name) || _con!=ts._con )
- return false;
- if( _fields == ts._fields ) return true;
- if( _fields==null || ts._fields==null ) return false;
- if( _fields.length!=ts._fields.length )
- return false;
+ if( !static_eq(ts) ) return false;
+ if( _fields==ts._fields ) return true;
+ // Check to see if we've ever compared this pair of types before;
+ // if so, then assume the cycle is equal here.
+ int pid = pid(ts);
+ if( CEQUALS.find(pid)!= -1 ) return true;
+ CEQUALS.push(pid);
+ // Recursively check fields
for( int i = 0; i < _fields.length; i++ )
- if( _fields[i] != ts._fields[i] )
+ if( !_fields[i].cycle_eq(ts._fields[i]) )
return false;
return true;
}
@Override
int hash() {
- long hash = _name.hashCode();
- if( _fields != null )
- for( Field f : _fields )
- hash = Utils.rot(hash,13) ^ f.hashCode();
- return Utils.fold(hash ^ _con.hashCode());
+ long hash = _name.hashCode() ^ (_open ? 4 : 0);
+ for( Field f : _fields )
+ hash = Utils.rot(hash,13) ^ ((long)f._fname.hashCode() * f._alias);
+ return Utils.fold(hash);
}
+ @Override int nkids() { return _fields.length; }
+ @Override Type at( int idx ) { return _fields[idx]; }
+ @Override void set( int idx, Type t ) { _fields[idx] = (Field)t; }
+
@Override
- public SB print(SB sb) {
+ SB _print(SB sb, BitSet visit, boolean html ) {
if( this==BOT ) return sb.p("StructBot");
if( this==TOP ) return sb.p("StructTop");
- if( _con!=TypeConAry.BOT ) return sb.p(_con.str());
+ if( isFree() ) return sb.p("FREE:").p(_name);
+ if( isAry() && field("[]")._t instanceof TypeConAry con )
+ return sb.p(con.str());
sb.p(_name);
- if( _fields == null || isAry() ) // Forward reference struct, just print the name
- return sb.p(isFinal() ? "" : "!");
+ if( html || isAry() )
+ return sb;
sb.p(" {");
for( Field f : _fields )
- f._type.print(sb).p(f._final ? " " : " !").p(f._fname).p("; ");
+ (f._t ==null ? sb.p("---") : f._t.print(sb,visit,html)).p(f._final ? " " : " !").p(f._fname).p("; ");
+ if( _open ) sb.p("... ");
return sb.p("}");
}
- @Override public SB gprint( SB sb ) {
- if( this==BOT ) return sb.p("StructBot");
- if( this==TOP ) return sb.p("StructTop");
- return sb.p(_name);
- }
@Override public String str() {
if( this==BOT ) return "StructBot";
if( this==TOP ) return "StructTop";
- return _con==TypeConAry.BOT ? _name : _con.str();
+ return (isFree() ? "FREE:":"")+_name;
}
@@ -272,7 +418,7 @@ public class TypeStruct extends Type {
}
public int aryScale() {
assert isAry();
- return _fields[1]._type.log_size();
+ return _fields[1]._t.log_size();
}
@@ -283,42 +429,44 @@ public class TypeStruct extends Type {
return _offs[idx];
}
private int[] offsets() { // Field byte offsets
+ assert CodeGen.CODE._phase.ordinal() >= CodeGen.Phase.Opto.ordinal();
// Compute a layout for a collection of fields
assert _fields != null; // No forward refs
// Compute a layout
- int[] cnts = new int[5]; // Count of fields at log field size
- int flen = _fields.length;
- if( isAry() ) flen--; // Array field is aligned differently
- for( int i=0; i<flen; i++ )
- cnts[_fields[i]._type.log_size()]++; // Log size is 0(byte), 1(i16/u16), 2(i32/f32), 3(i64/dbl)
- int off = 0, idx = 0; // Base common struct fields go here, e.g. Mark/Klass
- // Compute offsets to the start of each power-of-2 aligned fields.
- int[] offs = new int[4];
- for( int i=3; i>=0; i-- ) {
- offs[i] = off;
- off += cnts[i]<<i;
- }
- // Assign offsets to all fields.
- // Really a hidden radix sort.
_offs = new int[_fields.length+1];
- for( int i=0; i<flen; i++ ) {
- int log = _fields[i]._type.log_size();
- _offs[idx++] = offs[log]; // Field offset
- offs[log] += 1<<log; // Next field offset at same alignment
- cnts[log]--; // Count down, should be all zero at end
- }
-
- // Array bits
- if( isAry() ) {
+ if( isAry() ) { // Arrays used fixed u32 len, padding to elems, var length
+ assert _fields[1]._fname=="[]";
// Pad out to the element alignment
- int align = (_fields[flen]._fname!="[]" || _con==TypeConAry.BOT ? _fields[flen]._type : _con).alignment();
- off = (off + ((1<<align)-1)) & -(1<<align);
- _offs[flen] = off;
- if( _con!=TypeConAry.BOT )
- _offs[flen+1] = off+_con.len();
- } else
+ int align = _fields[1]._t.alignment();
+ _offs[1] = (4 + ((1<<align)-1)) & -(1<<align);
+ if( _fields[1]._t instanceof TypeConAry ary ) // Array length is well-defined for constants, not defined for runtime arrays
+ _offs[2] = _offs[1]+ary.len();
+
+ } else {
+ int[] cnts = new int[5]; // Count of fields at log field size
+ int flen = _fields.length;
+ for( int i=0; i<flen; i++ )
+ if( !_fields[i]._one )
+ cnts[_fields[i]._t.log_size()]++; // Log size is 0(byte), 1(i16/u16), 2(i32/f32), 3(i64/dbl)
+ int off = 0, idx = 0; // Base common struct fields go here, e.g. Mark/Klass
+ // Compute offsets to the start of each power-of-2 aligned fields.
+ int[] offs = new int[4];
+ for( int i=3; i>=0; i-- ) {
+ offs[i] = off;
+ off += cnts[i]<<i;
+ }
+ // Assign offsets to all fields.
+ // Really a hidden radix sort.
+ for( int i=0; i<flen; i++ ) {
+ if( _fields[i]._one ) continue;
+ int log = _fields[i]._t.log_size();
+ _offs[idx++] = offs[log]; // Field offset
+ offs[log] += 1<<log; // Next field offset at same alignment
+ cnts[log]--; // Count down, should be all zero at end
+ }
_offs[flen] = off; // Max struct size, no trailing padding
+ }
return _offs;
}
diff --git a/src/main/java/com/seaofnodes/simple/type/TypeTuple.java b/src/main/java/com/seaofnodes/simple/type/TypeTuple.java
index bc57871a..df9aba31 100644
--- a/src/main/java/com/seaofnodes/simple/type/TypeTuple.java
+++ b/src/main/java/com/seaofnodes/simple/type/TypeTuple.java
@@ -1,67 +1,75 @@
package com.seaofnodes.simple.type;
-import com.seaofnodes.simple.SB;
-import com.seaofnodes.simple.Utils;
-import java.util.ArrayList;
+import com.seaofnodes.simple.util.Ary;
+import com.seaofnodes.simple.util.SB;
+import com.seaofnodes.simple.util.Utils;
+import java.util.*;
public class TypeTuple extends Type {
- public final Type[] _types;
+ public Type[] _types;
+ private static final Ary<TypeTuple> FREE = new Ary<>(TypeTuple.class);
private TypeTuple(Type[] types) { super(TTUPLE); _types = types; }
- public static TypeTuple make(Type... types) { return new TypeTuple(types).intern(); }
+ private TypeTuple init( Type[] types ) { _types = types; return this; }
+ private static TypeTuple malloc(Type[] types) {
+ return FREE.isEmpty() ? new TypeTuple(types) : FREE.pop().init(types);
+ }
+ // All fields directly listed
+ public static TypeTuple make(Type... types) {
+ TypeTuple tt = malloc(types);
+ TypeTuple t2 = tt.intern();
+ return t2==tt ? tt : t2.free(tt);
+ }
+ @Override TypeTuple free(Type t) {
+ TypeTuple tt = (TypeTuple)t;
+ tt._types = null;
+ tt._dual = null;
+ tt._hash = 0;
+ FREE.push(tt);
+ return this;
+ }
+ @Override boolean isFree() { return _types==null; }
- public static final TypeTuple BOT = new TypeTuple(new Type[0]).intern();
- public static final TypeTuple TOP = new TypeTuple(null).intern();
+
+ public static final TypeTuple BOT = malloc(new Type[0]).intern();
+ public static final TypeTuple TOP = BOT.dual();
public static final TypeTuple TEST = make(TypeInteger.BOT,TypeMemPtr.TEST);
public static final TypeTuple START= make(Type.CONTROL,TypeMem.TOP,TypeInteger.BOT);
- public static final TypeTuple MAIN = make(TypeInteger.BOT);
public static final TypeTuple RET = make(Type.CONTROL,TypeMem.BOT,Type.BOTTOM);
- public static final TypeTuple CALLOC = make(TypeInteger.BOT,TypeInteger.BOT);
- public static final TypeTuple IF_BOTH = make(new Type[]{Type. CONTROL,Type. CONTROL});
- public static final TypeTuple IF_NEITHER = make(new Type[]{Type.XCONTROL,Type.XCONTROL});
- public static final TypeTuple IF_TRUE = make(new Type[]{Type. CONTROL,Type.XCONTROL});
- public static final TypeTuple IF_FALSE = make(new Type[]{Type.XCONTROL,Type. CONTROL});
+ public static final TypeTuple IF_BOTH = make(Type. CONTROL,Type. CONTROL);
+ public static final TypeTuple IF_NEITHER = make(Type.XCONTROL,Type.XCONTROL);
+ public static final TypeTuple IF_TRUE = make(Type. CONTROL,Type.XCONTROL);
+ public static final TypeTuple IF_FALSE = make(Type.XCONTROL,Type. CONTROL);
- public static void gather(ArrayList<Type> ts) { ts.add(BOT); ts.add(TEST); ts.add(START); ts.add(MAIN); ts.add(IF_TRUE); }
+ public static void gather(ArrayList<Type> ts) { ts.add(BOT); ts.add(TEST); ts.add(START); ts.add(IF_TRUE); }
@Override
Type xmeet(Type other) {
- TypeTuple tt = (TypeTuple)other; // contract from xmeet
- if( this==TOP ) return other;
- if( tt ==TOP ) return this ;
- if( _types.length != tt._types.length )
+ TypeTuple that = (TypeTuple)other; // contract from xmeet
+ if( this==TOP ) return that;
+ if( that==TOP ) return this;
+ if( _types.length != that._types.length )
return BOT;
- Type[] ts = new Type[_types.length];
- for( int i=0; i<_types.length; i++ )
- ts[i] = _types[i].meet(tt._types[i]);
- return make(ts);
+ return make(TypeFunPtr.xmeet(_types,that._types));
}
- @Override public TypeTuple dual() {
- if( this==TOP ) return BOT;
- if( this==BOT ) return TOP;
- Type[] ts = new Type[_types.length];
- for( int i=0; i<_types.length; i++ )
- ts[i] = _types[i].dual();
- return make(ts);
- }
-
- @Override public Type glb(boolean mem) {
- Type[] ts = new Type[_types.length];
- for( int i=0; i<_types.length; i++ )
- ts[i] = _types[i].glb(mem);
- return make(ts);
+ @Override TypeTuple xdual() {
+ if( _types.length==0 ) return malloc(null);
+ return malloc(TypeFunPtr.xdual(_types));
}
- @Override public boolean isConstant() {
+ @Override boolean _isConstant() {
for( Type t : _types )
- if( !t.isConstant() )
+ if( !t._isConstant() )
return false;
return true;
}
+ @Override boolean _isFinal() { throw Utils.TODO(); }
+
+ @Override TypeMemPtr _makeRO() { throw Utils.TODO(); }
@Override public int log_size() { throw Utils.TODO(); }
@Override public int alignment() {
@@ -74,25 +82,17 @@ public class TypeTuple extends Type {
public Type ret() { assert _types.length==3; return _types[2]; }
- @Override public String str() { return print(new SB()).toString(); }
-
- @Override public SB print(SB sb) {
+ @Override SB _print(SB sb, BitSet visit, boolean html ) {
if( this==TOP ) return sb.p("[TOP]");
if( this==BOT ) return sb.p("[BOT]");
sb.p("[ ");
for( Type t : _types )
- t.print(sb).p(", ");
- return sb.unchar(2).p("]");
- }
- @Override public SB gprint(SB sb) {
- if( this==TOP ) return sb.p("[TOP]");
- if( this==BOT ) return sb.p("[BOT]");
- sb.p("[ ");
- for( Type t : _types )
- t.gprint(sb).p(", ");
+ t.print(sb,visit,html).p(", ");
return sb.unchar(2).p("]");
}
+ @Override public String str() { return print(new SB(),new BitSet(),false).toString(); }
+
@Override
int hash() {
int sum = 0;
@@ -102,15 +102,18 @@ public class TypeTuple extends Type {
@Override
boolean eq( Type t ) {
- TypeTuple tt = (TypeTuple)t; // Contract
- if( _types==null && tt._types==null ) return true;
- if( _types==null || tt._types==null ) return false;
- if( _types.length != tt._types.length ) return false;
+ TypeTuple that = (TypeTuple)t; // Contract
+ if( _types==null && that._types==null ) return true;
+ if( _types==null || that._types==null ) return false;
+ if( _types.length != that._types.length ) return false;
for( int i=0; i<_types.length; i++ )
- if( _types[i]!=tt._types[i] )
+ if( _types[i]!=that._types[i] )
return false;
return true;
}
+ @Override int nkids() { return _types.length; }
+ @Override Type at( int idx ) { return _types[idx]; }
+ @Override void set( int idx, Type t ) { _types[idx] = t; }
}
diff --git a/src/main/java/com/seaofnodes/simple/Ary.java b/src/main/java/com/seaofnodes/simple/util/Ary.java
index f43179ad..d67801c2 100644
--- a/src/main/java/com/seaofnodes/simple/Ary.java
+++ b/src/main/java/com/seaofnodes/simple/util/Ary.java
@@ -1,4 +1,4 @@
-package com.seaofnodes.simple;
+package com.seaofnodes.simple.util;
import java.lang.StringBuilder;
import java.lang.reflect.Array;
@@ -36,6 +36,7 @@ public class Ary<E> extends AbstractList<E> implements List<E> {
/** @return last element */
public E last( ) { return at(_len-1); }
+ public E last(int x ) { return at(_len-1+x); }
/** Add element in amortized constant time
* @param e Element to add at end of list
diff --git a/src/main/java/com/seaofnodes/simple/util/AryInt.java b/src/main/java/com/seaofnodes/simple/util/AryInt.java
new file mode 100644
index 00000000..b498fd78
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/util/AryInt.java
@@ -0,0 +1,192 @@
+package com.seaofnodes.simple.util;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.function.IntUnaryOperator;
+
+// ArrayList with saner syntax
+public class AryInt {
+ public int[] _es;
+ public int _len;
+ public AryInt(int[] es) { this(es,es.length); }
+ public AryInt(int[] es, int len) { _es=es; _len=len; }
+ public AryInt() { this(new int[1],0); }
+
+ /** @return list is empty */
+ public boolean isEmpty() { return _len==0; }
+ /** @return active list length */
+ public int len() { return _len; }
+ /** @param i element index
+ * @return element being returned; throws if OOB */
+ public int at( int i ) {
+ range_check(i);
+ return _es[i];
+ }
+ /** @param i element index
+ * @return element being returned, or 0 if OOB */
+ public int atX( int i ) {
+ return i < _len ? _es[i] : 0;
+ }
+ /** @return last element */
+ public int last( ) { return at(_len-1); }
+ public int last(int x ) { return at(_len-1+x); }
+
+ /** @return remove and return last element */
+ public int pop( ) {
+ range_check(0);
+ return _es[--_len];
+ }
+
+ /** Add element in amortized constant time
+ * @param e element to add at end of list
+ * @return 'this' for flow-coding */
+ public AryInt push( int e ) {
+ if( _len >= _es.length ) _es = Arrays.copyOf(_es,Math.max(1,_es.length<<1));
+ _es[_len++] = e;
+ return this;
+ }
+
+ /** Slow, linear-time, element insert. Preserves order.
+ * @param i index to insert at, between 0 and _len inclusive.
+ * @param e intlement to insert
+ */
+ public void insert( int i, int e ) {
+ if( i < 0 || i>_len )
+ throw new ArrayIndexOutOfBoundsException(""+i+" >= "+_len);
+ if( _len >= _es.length ) _es = Arrays.copyOf(_es,Math.max(1,_es.length<<1));
+ System.arraycopy(_es,i,_es,i+1,(_len++)-i);
+ _es[i] = e;
+ }
+
+ /** Fast, constant-time, element removal. Does not preserve order
+ * @param i element to be removed
+ * @return element removed */
+ public int del( int i ) {
+ range_check(i);
+ int tmp = _es[i];
+ _es[i]=_es[--_len];
+ return tmp;
+ }
+
+ /** Slow, linear-time, element removal. Preserves order.
+ * @param i element to be removed
+ * @return element removed */
+ public int remove( int i ) {
+ range_check(i);
+ int e = _es[i];
+ System.arraycopy(_es,i+1,_es,i,(--_len)-i);
+ return e;
+ }
+
+ /** Remove all elements */
+ public void clear( ) { Arrays.fill(_es,0,_len,0); _len=0; }
+
+ // Extend and set
+ public int setX( int i, int e ) {
+ while( i>= _es.length ) _es = Arrays.copyOf(_es,_es.length<<1);
+ if( i >= _len ) _len = i+1;
+ return (_es[i] = e);
+ }
+
+ public int set( int i, int e ) {
+ range_check(i);
+ return (_es[i] = e);
+ }
+
+ public AryInt set_as( int e ) { _es[0] = e; _len=1; return this; }
+ public AryInt set_len( int len ) {
+ if( len > _len )
+ while( len>= _es.length ) _es = Arrays.copyOf(_es,_es.length<<1);
+ _len = len;
+ while( _es.length > (len<<1) ) // Shrink if hugely too large
+ _es = Arrays.copyOf(_es,_es.length>>1);
+ return this;
+ }
+
+ /** @param c Collection to be added */
+ public AryInt addAll( Collection<? extends Integer> c ) { for( int e : c ) push(e); return this; }
+
+ /** @param es Array to be added */
+ public AryInt addAll( int[] es ) {
+ if( es.length==0 ) return this;
+ while( _len+es.length > _es.length ) _es = Arrays.copyOf(_es,_es.length<<1);
+ System.arraycopy(es,0,_es,_len,es.length);
+ _len += es.length;
+ return this;
+ }
+
+ /** @param es Array to be added */
+ public AryInt addAll( AryInt ary ) {
+ if( ary._len==0 ) return this;
+ while( _len+ary._len > _es.length ) _es = Arrays.copyOf(_es,_es.length<<1);
+ System.arraycopy(ary._es,0,_es,_len,ary._len);
+ _len += ary._len;
+ return this;
+ }
+
+ public AryInt map_update( IntUnaryOperator f ) { for( int i = 0; i<_len; i++ ) _es[i] = f.applyAsInt(_es[i]); return this; }
+
+ /** @return compact array version, using the internal base array where possible. */
+ public int[] asAry() { return _len==_es.length ? _es : Arrays.copyOf(_es,_len); }
+
+ /** Sorts in-place */
+ public void sort_update() { Arrays.sort(_es, 0, _len); }
+ /** 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.
+ * @param e intlement to find
+ * @return index of first matching element, or -1 if none */
+ public int find( int e ) {
+ for( int i=0; i<_len; i++ ) if( _es[i]==e ) return i;
+ return -1;
+ }
+
+ @Override public String toString() {
+ SB sb = new SB().p('[');
+ for( int i=0; i<_len; i++ )
+ sb.p(_es[i]).p(',');
+ return sb.unchar().p(']').toString();
+ }
+
+ private void range_check( int i ) {
+ if( i < 0 || i>=_len )
+ throw new ArrayIndexOutOfBoundsException(""+i+" >= "+_len);
+ }
+
+ // Binary search sorted _es. Returns insertion point.
+ // Undefined results if _es is not sorted.
+ public int binary_search( int e ) {
+ int lo=0, hi=_len-1;
+ while( lo <= hi ) {
+ int mid = (hi + lo) >>> 1; // midpoint, rounded down
+ int mval = _es[mid];
+ if( e==mval ) {
+ // If dups, get to the first.
+ while( mid>0 && e==_es[mid-1] ) mid--;
+ return mid;
+ }
+ if( e >mval ) lo = mid+1;
+ else hi = mid-1;
+ }
+ return lo;
+ }
+
+ // Note that the hashCode() and equals() are not invariant to changes in the
+ // underlying array. If the hashCode() is used (e.g., inserting into a
+ // HashMap) and the then the array changes, the hashCode() will change also.
+ @Override public boolean equals( Object o ) {
+ if( this==o ) return true;
+ if( !(o instanceof AryInt ary) ) return false;
+ if( _len != ary._len ) return false;
+ if( _es == ary._es ) return true;
+ for( int i=0; i<_len; i++ )
+ if( _es[i] != ary._es[i] )
+ return false;
+ return true;
+ }
+ @Override public int hashCode( ) {
+ int sum=_len;
+ for( int i=0; i<_len; i++ )
+ sum += _es[i];
+ return sum;
+ }
+}
diff --git a/src/main/java/com/seaofnodes/simple/SB.java b/src/main/java/com/seaofnodes/simple/util/SB.java
index 978fbed9..d7afd6eb 100644
--- a/src/main/java/com/seaofnodes/simple/SB.java
+++ b/src/main/java/com/seaofnodes/simple/util/SB.java
@@ -1,4 +1,4 @@
-package com.seaofnodes.simple;
+package com.seaofnodes.simple.util;
/** Tight/tiny StringBuilder wrapper.
* Short short names on purpose; so they don't obscure the printing.
diff --git a/src/main/java/com/seaofnodes/simple/Utils.java b/src/main/java/com/seaofnodes/simple/util/Utils.java
index 1346c3ea..577a60af 100644
--- a/src/main/java/com/seaofnodes/simple/Utils.java
+++ b/src/main/java/com/seaofnodes/simple/util/Utils.java
@@ -1,4 +1,4 @@
-package com.seaofnodes.simple;
+package com.seaofnodes.simple.util;
import java.util.ArrayList;
diff --git a/src/main/smp/sys.smp b/src/main/smp/sys.smp
index ede08d63..6a6d3ca3 100644
--- a/src/main/smp/sys.smp
+++ b/src/main/smp/sys.smp
@@ -29,6 +29,148 @@ struct sys {
return dst;
};
};
+
+ // Ary/Vector of u8s - a resizable array of bytes.
+ struct aryu8 {
+ u32 !len = 0;
+ u8[] !buf;
+ // Grow as needed
+ val _grow = { int len2 ->
+ if( len+len2 < buf# ) return self;
+ int newlen = buf# * 2;
+ while( len+len2 >= newlen ) newlen *= 2;
+ u8[] buf2 = new u8[newlen];
+ sys.ary.copy(buf2,0,buf,0,buf#);
+ buf = buf2;
+ self;
+ };
+
+ // Add a character to aryu8
+ val add = { u8 c ->
+ _grow(1).buf[len++] = c;
+ self;
+ };
+
+ // Add a plain array to the aryu8
+ val addAry = { u8[~] ary ->
+ _grow(ary#);
+ sys.ary.copy(buf,len,ary,0,ary#);
+ len += ary#;
+ self;
+ };
+
+ // Remove x chars from the tail.
+ val undo = { int x ->
+ len = len >= x ? len-x : 0;
+ self;
+ };
+
+ // Print ASCII number into aryu8
+ val num = { i64 x ->
+ if( !x ) { add('0'); return self; }
+ if( x < 0 ) { add('-'); x = -x; }
+ i64 ten = 1, log = 0;
+ while( ten <= x ) { ten *= 10; log++; }
+ for( int i=0; i<log; i++ ) {
+ ten /= 10;
+ i64 q = x/ten;
+ u8 digit = q+'0';
+ add(digit);
+ x -= q*ten;
+ }
+ self;
+ };
+
+ // Write to fd, returns the number of bytes written (len).
+ val write = { i32 fd -> i64 ptr = buf; sys.libc.write(fd,ptr,len); };
+ };
+
+
+ // Scanner; a simple string parser.
+ struct Scan {
+ int !x;
+ u8[~] buf;
+
+ // Skip whitespace
+ val skip = { ->
+ while( buf[x] <= ' ' )
+ x++;
+ return self;
+ };
+
+ // True if find required character, and skip it; else False.
+ val match = { u8 c ->
+ skip();
+ if( buf[x] != c ) return false;
+ x++;
+ return true;
+ };
+
+ // Parse a simple number
+ val number = { ->
+ skip();
+ bool neg = match('-');
+ i64 n = 0;
+ while( '0' <= buf[x] & buf[x] <= '9' )
+ n = n*10 + (buf[x++] - '0');
+ return neg ? -n : n;
+ };
+ };
+
+ // Ary/Vector of i64s
+ struct aryi64 {
+ u32 !len = 0;
+ i64[] !buf;
+ // Grow as needed
+ val _grow = { int len2 ->
+ if( len+len2 < buf# ) return self;
+ int newlen = buf# * 2;
+ while( len+len2 >= newlen ) newlen *= 2;
+ i64[] buf2 = new i64[newlen];
+ for( int i=0; i<buf#; i++ )
+ buf2[i] = buf[i];
+ buf = buf2;
+ self;
+ };
+
+ // Add a i64 to aryu8
+ val add = { i64 c ->
+ _grow(1).buf[len++] = c;
+ self;
+ };
+
+ // Write to aryu8
+ val str = { ->
+ // TODO: clunky default constructor syntax
+ var str = new sys.aryu8 { buf = new u8[1]; }.add('[');
+ for( int i=0; i<len; i++ )
+ str.num(buf[i]).add(',').add(' ');
+ str.undo(2).add(']');
+ };
+ };
+
+ // Parse a comma-separated list of numbers, wrapped with square brackets,
+ // with optional whitespace e.g. "[ -17, 0,99,12456789 ] ".
+ // parseAryI64 & aryi64.str are bijections modulo whitespace.
+ // Returns null if the parse failed.
+ val parseAryI64 = { u8[~] str ->
+ !sys.Scan !s = new sys.Scan{ buf = str; };
+ if( !s.match('[') ) return null;
+ // Array of parsed results
+ var nums = new sys.aryi64{ buf=new i64[1]; };
+ // Parse a list of numbers
+ if( !s.match(']') ) { // Handle the empty list
+ // Parse "123, " until a something else
+ while( s.x < s.buf# ) {
+ nums.add(s.number());
+ if( !s.match(',') )
+ break;
+ };
+ if( !s.match(']') ) return null; // Close the list
+ }
+ return nums;
+ };
+
struct io {
// Print string to stdout
val p = { u8[~] str ->
diff --git a/src/test/java/com/seaofnodes/simple/Chapter04Test.java b/src/test/java/com/seaofnodes/simple/Chapter04Test.java
index e5a16680..ce808084 100644
--- a/src/test/java/com/seaofnodes/simple/Chapter04Test.java
+++ b/src/test/java/com/seaofnodes/simple/Chapter04Test.java
@@ -91,7 +91,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), 123L).parse();
+ CodeGen code = new CodeGen("return arg; ", TypeInteger.constant(2), 123L, true).parse();
assertEquals("return 2;", code.expr().in(1).toString());
}
diff --git a/src/test/java/com/seaofnodes/simple/Chapter06Test.java b/src/test/java/com/seaofnodes/simple/Chapter06Test.java
index b6bc2bf3..1ff875b4 100644
--- a/src/test/java/com/seaofnodes/simple/Chapter06Test.java
+++ b/src/test/java/com/seaofnodes/simple/Chapter06Test.java
@@ -104,7 +104,7 @@ else if( arg==3 )
else
a=5;
return a;
-""", TypeInteger.constant(2), 123L).parse().opto();
+""", TypeInteger.constant(2), 123L, true).parse().opto();
assertEquals("return 5;", code.print());
}
@@ -124,7 +124,7 @@ else if( arg==3 )
else
a=5;
return a;
-""", TypeInteger.constant(1), 123L).parse().opto();
+""", TypeInteger.constant(1), 123L, true).parse().opto();
assertEquals("return 3;", code.print());
}
@@ -164,7 +164,7 @@ else if( arg==3 )
else
a=5;
return a;
-""", TypeInteger.constant(1), 123L);
+""", TypeInteger.constant(1), 123L, true);
code.parse().opto();
assertEquals("return 3;", code.print());
}
@@ -185,7 +185,7 @@ else if( arg==3 )
else
a=5;
return a;
-""", TypeInteger.constant(3), 123L);
+""", TypeInteger.constant(3), 123L, true);
code.parse().opto();
assertEquals("return 4;", code.print());
}
@@ -220,7 +220,7 @@ if( arg ) {
else b = 3;
}
return a+b;
-""", TypeInteger.constant(1), 123L);
+""", TypeInteger.constant(1), 123L, true);
code.parse().opto();
assertEquals("return 4;", code.print());
}
@@ -238,7 +238,7 @@ if( arg ) {
else b = 3;
}
return a+b;
-""", TypeInteger.constant(0), 123L);
+""", TypeInteger.constant(0), 123L, true);
code.parse().opto();
assertEquals("return 1;", code.print());
}
@@ -277,7 +277,7 @@ if( arg ) {
else b = 3;
}
return a+b+c;
-""", TypeInteger.constant(1), 123L);
+""", TypeInteger.constant(1), 123L, true);
code.parse().opto();
assertEquals("return 6;", code.print());
}
@@ -297,7 +297,7 @@ if( arg ) {
else b = 3;
}
return a+b+c;
-""", TypeInteger.constant(2), 123L);
+""", TypeInteger.constant(2), 123L, true);
code.parse().opto();
assertEquals("return 5;", code.print());
}
diff --git a/src/test/java/com/seaofnodes/simple/Chapter09Test.java b/src/test/java/com/seaofnodes/simple/Chapter09Test.java
index 551880b1..54a14a25 100644
--- a/src/test/java/com/seaofnodes/simple/Chapter09Test.java
+++ b/src/test/java/com/seaofnodes/simple/Chapter09Test.java
@@ -126,9 +126,10 @@ while(v1+arg) {
v0=1;
v1=v2;
}
+return 0;
""");
code.parse().opto();
- assertEquals("Stop[ ]", code.print()); // No explicit return: omit default main.
+ assertEquals("return 0;", code.print());
}
@Test
@@ -208,9 +209,9 @@ return -0+0+0;
@Test
public void testFuzz3() {
- CodeGen code = new CodeGen("int v0=0; while(0==69) while(v0) return 0;");
+ CodeGen code = new CodeGen("int v0=0; while(0==69) while(v0) return 0; return 0;");
code.parse().opto();
- assertEquals("Stop[ ]", code.print()); // The only return is unreachable.
+ assertEquals("return 0;", code.print());
}
@Test
@@ -254,9 +255,10 @@ return 0!=0;
int v0=0;
while(0==1) while(v0)
v0=1+v0;
+return 0;
""");
code.parse().opto();
- assertEquals("Stop[ ]", code.print());
+ assertEquals("return 0;", code.print());
}
@Test
diff --git a/src/test/java/com/seaofnodes/simple/Chapter10Test.java b/src/test/java/com/seaofnodes/simple/Chapter10Test.java
index bcb73eb9..0f4ca169 100644
--- a/src/test/java/com/seaofnodes/simple/Chapter10Test.java
+++ b/src/test/java/com/seaofnodes/simple/Chapter10Test.java
@@ -37,7 +37,8 @@ public class Chapter10Test {
"if (null != p) return p.x; return -1;",
"if (!!!!p) return p.x; return -1;",
"if (!!!p) return -1; return p.x;",
- "int b = !!p; if (b) return p.x + b - 1; return -1;"
+ "int b = !!p; if (b) return p.x + b - 1; return -1;",
+ "if (!(p == null || arg == 0)) return p.x; return -1;"
} ) {
CodeGen code = new CodeGen(NULLABLE_POINT_SOURCE+body).parse().opto().typeCheck();
assertEquals(body,"-1",Eval2.eval(code,0));
@@ -46,6 +47,21 @@ public class Chapter10Test {
}
@Test
+ public void testShortCircuitGuardScheduling() {
+ // The call result exists only on the RHS path, not above the merge.
+ CodeGen code = new CodeGen("""
+ val f = { int n -> (n+1)&7; };
+ int x = 0;
+ if (!!(arg && (x=f(arg)))) return x;
+ return -1;
+ """).driver(CodeGen.Phase.TypeCheck);
+ assertEquals("-1",Eval2.eval(code,0));
+ assertEquals("2",Eval2.eval(code,1));
+ assertEquals("-1",Eval2.eval(code,7));
+ code.driver(CodeGen.Phase.LocalSched);
+ }
+
+ @Test
public void testNullGuardErrors() {
for( String body : new String[] {
"return p.x;",
@@ -173,7 +189,7 @@ if (arg) bar = null;
bar.a = 1;
return bar.a;
""");
- try { code.parse().opto(); fail(); }
+ try { code.parse().opto().typeCheck(); fail(); }
catch( Exception e ) { assertEquals("Type null is not of declared type *Bar",e.getMessage()); }
}
@@ -194,12 +210,12 @@ return bar.a;
public void testIf3() {
CodeGen code = new CodeGen("""
struct Bar { int a; };
-Bar bar = null;
+!Bar !bar = null;
if (arg) bar = null;
bar.a = 1;
return bar.a;
""");
- try { code.parse(); fail(); }
+ try { code.parse().opto().typeCheck(); fail(); }
catch( Exception e ) { assertEquals("Type null is not of declared type *Bar", e.getMessage()); }
}
@@ -348,7 +364,7 @@ return new s0;
int v0=null.f0;
""");
try { code.parse(); fail(); }
- catch( Exception e ) { assertEquals("Accessing unknown field 'f0' from 'null'",e.getMessage()); }
+ catch( Exception e ) { assertEquals("Syntax error, expected ;: .",e.getMessage()); }
}
@Test
diff --git a/src/test/java/com/seaofnodes/simple/Chapter11Test.java b/src/test/java/com/seaofnodes/simple/Chapter11Test.java
index 439333ca..3d5d2686 100644
--- a/src/test/java/com/seaofnodes/simple/Chapter11Test.java
+++ b/src/test/java/com/seaofnodes/simple/Chapter11Test.java
@@ -53,7 +53,7 @@ public class Chapter11Test {
// 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,com.seaofnodes.simple.type.TypeInteger.BOT,seed).parse();
+ var code = new CodeGen(Chapter10Test.NESTED_MEMORY,com.seaofnodes.simple.type.TypeInteger.BOT,seed,false).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 d8b072a2..ac3b81ec 100644
--- a/src/test/java/com/seaofnodes/simple/Chapter12Test.java
+++ b/src/test/java/com/seaofnodes/simple/Chapter12Test.java
@@ -42,22 +42,22 @@ return head.next.i;
public void testLinkedList1() {
CodeGen code = new CodeGen(
"""
-struct LLI { !LLI? !next; int i; };
+struct LLI { !LLI? !next; int q; };
!LLI? !head = null;
while( arg ) {
!LLI !x = new LLI;
x.next = head;
- x.i = arg;
+ x.q = arg;
head = x;
arg = arg-1;
}
if( !head ) return 0;
LLI? next = head.next;
if( next==null ) return 1;
-return next.i;
+return next.q;
""");
- code.parse().opto();
- assertEquals("return Phi(Region,0,1,.i);", code.print());
+ code.parse().opto().typeCheck();
+ assertEquals("return Phi(Region,0,1,.q);", code.print());
assertEquals("2", Eval2.eval(code, 3));
}
@@ -110,10 +110,10 @@ return n.next;
"""
struct M { int m; };
struct N { !M !next; int i; };
-N n = new N { next = null; }
+N n = new N { next = null; };
return n.next;
""");
- try { code.parse().opto(); fail(); }
+ try { code.parse().opto().typeCheck(); fail(); }
catch( Exception e ) { assertEquals("Type null is not of declared type *M",e.getMessage()); }
}
@@ -126,7 +126,7 @@ struct N { !N? !next; int i; };
n.i = 3.14;
return n.i;
""");
- code.parse().opto();
+ code.parse().opto().typeCheck();
assertEquals("3",Eval2.eval(code,0));
}
@@ -134,7 +134,7 @@ return n.i;
public void testNullRef4() {
CodeGen code = new CodeGen("return -null-5/null-5;");
try { code.parse().opto().typeCheck(); fail(); }
- catch( Exception e ) { assertEquals("Cannot '+' null",e.getMessage()); }
+ catch( Exception e ) { assertEquals("Cannot '//' null",e.getMessage()); }
}
@Test public void testNullRef5() {
@@ -182,7 +182,7 @@ return new S1.s=new S2;
public void testcheckNull() {
CodeGen code = new CodeGen(
"""
-struct I {int i;};
+struct I { int i; };
struct P { !I? !pi; };
!P !p1 = new P;
!P !p2 = new P;
@@ -199,32 +199,52 @@ return p1.pi.i + 1;
public void testCoRecur2() {
CodeGen code = new CodeGen(
"""
-struct A { !L? !a; !T? !b; !F? !c; }; !A? !a = new A;
-struct B { !M? !a; !U? !b; !G? !c; }; !B? !b = new B;
-struct C { !N? !a; !V? !b; !H? !c; }; !C? !c = new C;
-struct D { !O? !a; !W? !b; !I? !c; }; !D? !d = new D;
-struct E { !P? !a; !X? !b; !J? !c; }; !E? !e = new E;
-struct F { !Q? !a; !Y? !b; !K? !c; }; !F? !f = new F;
-struct G { !R? !a; !Z? !b; !L? !c; }; !G? !g = new G;
-struct H { !S? !a; !A? !b; !M? !c; }; !H? !h = new H;
-struct I { !T? !a; !B? !b; !N? !c; }; !I? !i = new I;
-struct J { !U? !a; !C? !b; !O? !c; }; !J? !j = new J;
-struct K { !V? !a; !D? !b; !P? !c; }; !K? !k = new K;
-struct L { !W? !a; !E? !b; !Q? !c; }; !L? !l = new L;
-struct M { !X? !a; !F? !b; !R? !c; }; !M? !m = new M;
-struct N { !Y? !a; !G? !b; !S? !c; }; !N? !n = new N;
-struct O { !Z? !a; !H? !b; !T? !c; }; !O? !o = new O;
-struct P { !A? !a; !I? !b; !U? !c; }; !P? !p = new P;
-struct Q { !B? !a; !J? !b; !V? !c; }; !Q? !q = new Q;
-struct R { !C? !a; !K? !b; !W? !c; }; !R? !r = new R;
-struct S { !D? !a; !L? !b; !X? !c; }; !S? !s = new S;
-struct T { !E? !a; !M? !b; !Y? !c; }; !T? !t = new T;
-struct U { !F? !a; !N? !b; !Z? !c; }; !U? !u = new U;
-struct V { !G? !a; !O? !b; !A? !c; }; !V? !v = new V;
-struct W { !H? !a; !P? !b; !B? !c; }; !W? !w = new W;
-struct X { !I? !a; !Q? !b; !C? !c; }; !X? !x = new X;
-struct Y { !J? !a; !R? !b; !D? !c; }; !Y? !y = new Y;
-struct Z { !K? !a; !S? !b; !E? !c; }; !Z? !z = new Z;
+struct A { !B? !f0; !C? !f1; }; !A !a = new A;
+struct B { !C? !f0; !A? !f1; }; !B !b = new B;
+struct C { !A? !f0; !B? !f1; }; !C !c = new C;
+
+a.f0=b; a.f1=c;
+b.f0=c; b.f1=a;
+c.f0=a; c.f1=b;
+
+return a.f0.f1.f0.f1.f0;
+
+""");
+ code.parse().opto().typeCheck().GCM().localSched();
+ assertEquals("return B;", code._stop.toString());
+ assertEquals("B{f0=C},f1=A{f0=$cyclic,f1=$cyclic}}", Eval2.eval(code, 0));
+ }
+
+ @Test
+ public void testCoRecur3() {
+ CodeGen code = new CodeGen(
+"""
+struct A { !L? !a; !T? !b; !F? !c; }; !A !a = new A;
+struct B { !M? !a; !U? !b; !G? !c; }; !B !b = new B;
+struct C { !N? !a; !V? !b; !H? !c; }; !C !c = new C;
+struct D { !O? !a; !W? !b; !I? !c; }; !D !d = new D;
+struct E { !P? !a; !X? !b; !J? !c; }; !E !e = new E;
+struct F { !Q? !a; !Y? !b; !K? !c; }; !F !f = new F;
+struct G { !R? !a; !Z? !b; !L? !c; }; !G !g = new G;
+struct H { !S? !a; !A? !b; !M? !c; }; !H !h = new H;
+struct I { !T? !a; !B? !b; !N? !c; }; !I !i = new I;
+struct J { !U? !a; !C? !b; !O? !c; }; !J !j = new J;
+struct K { !V? !a; !D? !b; !P? !c; }; !K !k = new K;
+struct L { !W? !a; !E? !b; !Q? !c; }; !L !l = new L;
+struct M { !X? !a; !F? !b; !R? !c; }; !M !m = new M;
+struct N { !Y? !a; !G? !b; !S? !c; }; !N !n = new N;
+struct O { !Z? !a; !H? !b; !T? !c; }; !O !o = new O;
+struct P { !A? !a; !I? !b; !U? !c; }; !P !p = new P;
+struct Q { !B? !a; !J? !b; !V? !c; }; !Q !q = new Q;
+struct R { !C? !a; !K? !b; !W? !c; }; !R !r = new R;
+struct S { !D? !a; !L? !b; !X? !c; }; !S !s = new S;
+struct T { !E? !a; !M? !b; !Y? !c; }; !T !t = new T;
+struct U { !F? !a; !N? !b; !Z? !c; }; !U !u = new U;
+struct V { !G? !a; !O? !b; !A? !c; }; !V !v = new V;
+struct W { !H? !a; !P? !b; !B? !c; }; !W !w = new W;
+struct X { !I? !a; !Q? !b; !C? !c; }; !X !x = new X;
+struct Y { !J? !a; !R? !b; !D? !c; }; !Y !y = new Y;
+struct Z { !K? !a; !S? !b; !E? !c; }; !Z !z = new Z;
a.a=l; a.b=t; a.c=f;
b.a=m; b.b=u; b.c=g;
@@ -258,7 +278,6 @@ return a.b.c.a.b.c.a.b.c.a.b.c.a.b.c.a.b.c;
""");
code.parse().opto().typeCheck().GCM().localSched();
assertEquals("return R;", code._stop.toString());
- assertEquals("R{a=C{a=N{a=Y{a=J{a=U{a=F{a=Q{a=B{a=M{a=X{a=I{a=T{a=E{a=P{a=A{a=L{a=W{a=H{a=S{a=D{a=O{a=Z{a=K{a=V{a=G{a=$cyclic,b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic}", Eval2.eval(code, 0));
+ assertEquals("R{a=C{a=N},b=V},c=H}},b=K{a=$cyclic,b=D{a=O},b=W},c=I}},c=P}},c=$cyclic}", Eval2.eval(code, 0));
}
-
}
diff --git a/src/test/java/com/seaofnodes/simple/Chapter13Test.java b/src/test/java/com/seaofnodes/simple/Chapter13Test.java
index 66e12e42..63f1d12c 100644
--- a/src/test/java/com/seaofnodes/simple/Chapter13Test.java
+++ b/src/test/java/com/seaofnodes/simple/Chapter13Test.java
@@ -22,7 +22,7 @@ while(---- -arg) {
}
}
{
- int kzEl_r7EYz=3*----0!=-arg<(37*--97!=0/arg==arg)<=-(--arg!=0);
+ int kzEl_r7EYz = (3*----0!=-arg) < (37*--97!=0/arg==arg) <= -(--arg!=0);
while(kzEl_r7EYz<---1) {
kzEl_r7EYz=kzEl_r7EYz+kzEl_r7EYz*arg>=24;
if(7) continue;
diff --git a/src/test/java/com/seaofnodes/simple/Chapter15Test.java b/src/test/java/com/seaofnodes/simple/Chapter15Test.java
index 074cd8ba..d0a42ccf 100644
--- a/src/test/java/com/seaofnodes/simple/Chapter15Test.java
+++ b/src/test/java/com/seaofnodes/simple/Chapter15Test.java
@@ -4,8 +4,8 @@ import com.seaofnodes.simple.codegen.CodeGen;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
-import org.junit.Ignore;
import org.junit.Test;
+import org.junit.Ignore;
import static org.junit.Assert.*;
public class Chapter15Test {
@@ -51,7 +51,7 @@ output[i] = 1;
return output;
""");
code.parse().opto();
- assertEquals("return [u8];", code.print());
+ assertEquals("return []u8;", code.print());
assertEquals("\u0001", Eval2.eval(code, 0));
}
@@ -103,8 +103,8 @@ struct A { int i; };
return a;
""");
code.parse().opto();
- assertEquals("return [*A?];", code.print());
- assertEquals("*A {int !i; }?[ null,null]", Eval2.eval(code, 0));
+ assertEquals("return []*A?;", code.print());
+ assertEquals("*A {i64 !i; }?[ null,null]", Eval2.eval(code, 0));
}
@Test
@@ -176,7 +176,7 @@ return root;
""");
code.parse().opto();
assertEquals("return Tree;", code.print());
- assertEquals("Tree{_kids=*Tree?[ Tree{_kids=null},null]}", Eval2.eval(code, 0));
+ assertEquals("Tree{_kids=*Tree {*[]*Tree?? !_kids; }?[ Tree{_kids=null},null]}", Eval2.eval(code, 0));
}
@Test
@@ -214,12 +214,12 @@ return ary[1] * 1000 + ary[3]; // 1 * 1000 + 6
assertEquals("1006", Eval2.eval(code, 4));
}
- @Test
+ @Test @Ignore
public void sieveOEratosthenes() throws IOException {
String src = Files.readString(Path.of("src/test/java/com/seaofnodes/simple/progs/sieve.smp"));
CodeGen code = new CodeGen(src+"return sieve(arg);");
code.parse().opto();
- assertEquals("return [u32];", code.print());
+ assertEquals("return []u32;", code.print());
assertEquals("u32[ 2,3,5,7,11,13,17,19]",Eval2.eval(code, 20));
}
@@ -256,9 +256,10 @@ return new flt;
CodeGen code = new CodeGen(
"""
flt is = new int[2];
+return is;
""");
- try { code.parse().opto(); fail(); }
- catch( Exception e ) { assertEquals("Type *[int] is not of declared type flt",e.getMessage()); }
+ try { code.parse().opto().typeCheck(); fail(); }
+ catch( Exception e ) { assertEquals("Type *[]i64 is not of declared type flt",e.getMessage()); }
}
@Test
diff --git a/src/test/java/com/seaofnodes/simple/Chapter16Test.java b/src/test/java/com/seaofnodes/simple/Chapter16Test.java
index 362388d6..f73cbf57 100644
--- a/src/test/java/com/seaofnodes/simple/Chapter16Test.java
+++ b/src/test/java/com/seaofnodes/simple/Chapter16Test.java
@@ -1,8 +1,6 @@
package com.seaofnodes.simple;
import com.seaofnodes.simple.codegen.CodeGen;
-import com.seaofnodes.simple.evaluator.Evaluator;
-import com.seaofnodes.simple.node.StopNode;
import org.junit.Ignore;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
diff --git a/src/test/java/com/seaofnodes/simple/Chapter17aTest.java b/src/test/java/com/seaofnodes/simple/Chapter17aTest.java
index 16ffe967..8597b799 100644
--- a/src/test/java/com/seaofnodes/simple/Chapter17aTest.java
+++ b/src/test/java/com/seaofnodes/simple/Chapter17aTest.java
@@ -1,8 +1,6 @@
package com.seaofnodes.simple;
import com.seaofnodes.simple.codegen.CodeGen;
-import com.seaofnodes.simple.evaluator.Evaluator;
-import com.seaofnodes.simple.node.StopNode;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
@@ -51,7 +49,7 @@ return p;
public void testStructFinal1() {
CodeGen code = new CodeGen("""
struct Point { int x=3, y=4; };
-Point p = new Point { x=5; y=6; };
+~Point p = new Point { x=5; y=6; };
p.x++;
return p;
""");
@@ -63,7 +61,7 @@ return p;
public void testStructFinal2() {
CodeGen code = new CodeGen("""
struct Point { int x=3, y=4; };
-Point p = new Point;
+~Point p = new Point;
p.x++;
return p;
""");
@@ -88,8 +86,8 @@ Point p = new Point;
p.x++;
return p;
""");
- try { code.parse().opto(); fail(); }
- catch( Exception e ) { assertEquals("Cannot reassign final 'x'",e.getMessage()); }
+ try { code.parse().opto().typeCheck(); fail(); }
+ catch( Exception e ) { assertEquals("Cannot modify final field 'x'",e.getMessage()); }
}
@Test
diff --git a/src/test/java/com/seaofnodes/simple/Chapter17bTest.java b/src/test/java/com/seaofnodes/simple/Chapter17bTest.java
index e8f6e0bd..24dc0229 100644
--- a/src/test/java/com/seaofnodes/simple/Chapter17bTest.java
+++ b/src/test/java/com/seaofnodes/simple/Chapter17bTest.java
@@ -125,7 +125,7 @@ return s.x;
@Test public void testInt10() {
CodeGen code = new CodeGen("flt x = 1.5; x++; int i = x; return i;");
- code.parse().opto();
+ code.parse().opto().typeCheck();
assertEquals("2",Eval2.eval(code,0));
}
@@ -172,13 +172,13 @@ return s.x;
}
@Test public void testVar6() {
- CodeGen code = new CodeGen("struct S{int x;}; S s=new S; s.x++; return s.x; // Error initializer so x is immutable ");
+ CodeGen code = new CodeGen("struct S{int x;}; ~S s = new S; s.x++; return s.x; // Error initializer so x is immutable ");
try { code.parse().opto().typeCheck(); fail(); }
catch( Exception e ) { assertEquals("Cannot modify final field 'x'",e.getMessage()); }
}
@Test public void testVar7() {
- CodeGen code = new CodeGen("struct S{int x;}; S s=new S{x=3;}; s.x++; return s.x; // Error initializer so x is immutable ");
+ CodeGen code = new CodeGen("struct S{int x;}; ~S s = new S{x=3;}; s.x++; return s.x; // Error initializer so x is immutable ");
try { code.parse().opto().typeCheck(); fail(); }
catch( Exception e ) { assertEquals("Cannot modify final field 'x'",e.getMessage()); }
}
@@ -216,6 +216,7 @@ foo.bar.x++; // Ok foo and foo.bar and foo.bar.x are all mutable
~Foo xfoo = foo; // Throw away mutability
xfoo.bar.x++; // Error, cannot mutate through xfoo
+return xfoo.bar.x;
""");
try { code.parse().opto().typeCheck(); fail(); }
catch( Exception e ) { assertEquals("Cannot modify final field 'x'",e.getMessage()); }
@@ -276,7 +277,7 @@ return new A {
if (arg) b = new B; // Constructor ends with partial init of b
}.b;
""");
- try { code.parse().opto(); fail(); }
+ try { code.parse(); fail(); }
catch( Exception e ) { assertEquals("'A' is not fully initialized, field 'b' needs to be set in a constructor",e.getMessage()); }
}
@@ -286,7 +287,7 @@ return new A {
"""
struct S{};
val x = 1;
-val s = new S{x = 2;};
+~S s = new S{x = 2;};
return x;
"""
);
@@ -309,7 +310,7 @@ focus(me);
return me;
"""
);
- try { code.parse().opto().typeCheck(); fail(); }
+ try { code.parse(); fail(); }
catch( Exception e ) { assertEquals("Syntax error, expected =expression: ;",e.getMessage()); }
}
@@ -493,15 +494,15 @@ return new A;
@Test
public void testForward1() {
CodeGen code = new CodeGen("""
-struct A{
+struct A {
!B?[]? !nil_array_of_b;
- !B?[] not_array_of_b = new !B?[0];
+ !B?[] !array_of_b;
};
-return new A.not_array_of_b;
+return new A{array_of_b = new !B?[0]; }.array_of_b;
""");
code.parse().opto();
- assertEquals("return [*B?];", code.print());
- assertEquals("*B?[]", Eval2.eval(code,0));
+ assertEquals("return []*B?;", code.print());
+ assertEquals("*B {... }?[]", Eval2.eval(code,0));
}
// ---------------------------------------------------------------
@@ -550,8 +551,8 @@ for( int j=0; j<nprimes; j++ )
return rez;
""");
code.parse().opto().typeCheck().GCM();
- assertEquals("return [int];", code.print());
- assertEquals("int[ 2,3,5,7,11,13,17,19]",Eval2.eval(code, 20));
+ assertEquals("return []i64;", code.print());
+ assertEquals("i64[ 2,3,5,7,11,13,17,19]",Eval2.eval(code, 20));
}
}
diff --git a/src/test/java/com/seaofnodes/simple/Chapter18Test.java b/src/test/java/com/seaofnodes/simple/Chapter18Test.java
index 6eb4d6f4..902344ed 100644
--- a/src/test/java/com/seaofnodes/simple/Chapter18Test.java
+++ b/src/test/java/com/seaofnodes/simple/Chapter18Test.java
@@ -102,13 +102,13 @@ public class Chapter18Test {
@Test public void testPrintingForwardReferenceScope() throws Exception {
new CodeGen("return 0;").parse();
- var declared = TypeMemPtr.make(TypeStruct.makeFRef("PrintForward"));
+ var declared = TypeMemPtr.make(TypeStruct.open("PrintForward"));
var scope = new ScopeNode();
scope.define("ptr",declared,false,new ConstantNode(TypeInteger.ZERO),null);
- Parser.TYPES.put("PrintForward",TypeMemPtr.make(TypeStruct.make("PrintForward")));
+ Parser.TYPES.put("PrintForward",TypeMemPtr.make(TypeStruct.make("PrintForward",false)));
var field = Var.class.getDeclaredField("_type");
field.setAccessible(true);
- var v = scope._vars.get(0);
+ var v = scope.var(0);
org.junit.Assert.assertSame(declared,field.get(v));
scope.toString();
org.junit.Assert.assertSame(declared,field.get(v));
@@ -171,7 +171,7 @@ return x2;
};
""");
code.parse().opto();
- assertEquals("return (Parm_x(sq,int)*x);", code._stop.toString());
+ assertEquals("return (Parm_x(sq,i64)*x);", code._stop.toString());
//assertEquals("{ int -> int #1}", Eval2.eval(code, 3));
}
@@ -185,7 +185,7 @@ var sq = { int x ->
return sq(arg)+sq(3);
""");
code.driver(Phase.LocalSched);
- assertEquals("Stop[ return (Parm_x(sq,int)*x); return (#2+#2); ]", code._stop.toString());
+ assertEquals("Stop[ return (#2+#2); return (Parm_x(sq,i64)*x); ]", code._stop.toString());
assertEquals("13", Eval2.eval(code, 2));
}
@@ -223,7 +223,7 @@ var fcn = arg ? { int x -> x*x; } : { int x -> x+x; };
return fcn(3);
""");
code.parse().opto();
- assertEquals("Stop[ return (Parm_x($fun2,int,3)<<1); return (Parm_x($fun1,int,3)*x); return #2; ]", code._stop.toString());
+ assertEquals("Stop[ return #2; return (Parm_x($fun21,i64,3)*x); return (Parm_x($fun22,i64,3)<<1); ]", code._stop.toString());
assertEquals("6", Eval2.eval(code, 0));
assertEquals("9", Eval2.eval(code, 1));
}
@@ -233,7 +233,7 @@ return fcn(3);
public void testFcn5() {
CodeGen code = new CodeGen("val fact = { int x -> x <= 1 ? 1 : x*fact(x-1); }; return fact(arg);");
code.parse().opto().typeCheck();
- assertEquals("Stop[ return Phi(Region,1,(Parm_x(fact,int,arg,(x-1))*#2)); return #2; ]", code._stop.toString());
+ assertEquals("Stop[ return #2; return Phi(Region,1,(Parm_x(fact,i64,arg,(x-1))*#2)); ]", code._stop.toString());
assertEquals( "1", Eval2.eval(code, 0));
assertEquals( "1", Eval2.eval(code, 1));
assertEquals( "2", Eval2.eval(code, 2));
@@ -265,7 +265,7 @@ val g = {->2;};
return 2;
""");
code.parse().opto().typeCheck().GCM();
- assertEquals("Stop[ return 2; return 1; return 1; ]", code._stop.toString());
+ assertEquals("Stop[ return 1; return 1; return 2; ]", code._stop.toString());
assertEquals("1", Eval2.eval(code, 0));
}
@@ -283,7 +283,7 @@ for(;;) {
}
""");
code.driver(Phase.LocalSched);
- assertEquals("Stop[ return Parm_i(x,int); return #2; ]", code._stop.toString());
+ assertEquals("Stop[ return #2; return Parm_i(x,i64); ]", code._stop.toString());
assertEquals("3", Eval2.eval(code, 0));
}
@@ -359,7 +359,7 @@ for(;;) {
return 0;
""");
try { code.parse().opto().typeCheck(); fail(); }
- catch( Exception e ) { assertEquals("Might be null calling { int -> int #1}?",e.getMessage()); }
+ catch( Exception e ) { assertEquals("Might be null calling { i64 -> i64 #21}?",e.getMessage()); }
}
@@ -455,7 +455,7 @@ if (i2i) return i2i(arg);
return f2f(o)(1);
""");
code.driver(Phase.LocalSched);
- assertEquals("Stop[ return Parm_i(i2i.o,int); return Phi(Region,#2,1); ]", code._stop.toString());
+ assertEquals("Stop[ return Phi(Region,#2,1); return Parm_i(i2i.o,i64); ]", code._stop.toString());
assertEquals("1", Eval2.eval(code, 2));
}
diff --git a/src/test/java/com/seaofnodes/simple/Chapter19Test.java b/src/test/java/com/seaofnodes/simple/Chapter19Test.java
index 4564e513..23b45e46 100644
--- a/src/test/java/com/seaofnodes/simple/Chapter19Test.java
+++ b/src/test/java/com/seaofnodes/simple/Chapter19Test.java
@@ -15,7 +15,7 @@ import static org.junit.Assert.*;
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 tfp = TypeFunPtr.make((byte)2,false,new Type[]{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.
@@ -49,7 +49,7 @@ return 0;
public void testString() throws IOException {
String src = Files.readString(Path.of("src/test/java/com/seaofnodes/simple/progs/stringHash.smp"));
CodeGen code = new CodeGen(src).parse().opto().typeCheck().GCM().localSched();
- assertEquals("Stop[ return Phi(Region,._hashCode,Phi(Region,123456789,Phi(Loop,0,(.[]+((Phi_hash<<5)-Phi_hash))))); return Phi(Region,1,0,0,1); ]", code._stop.toString());
+ assertEquals("Stop[ return Phi(Region,1,0,0,1); return Phi(Region,._hashCode,Phi(Region,123456789,Phi(Loop,0,(.[]+((Phi_hash<<5)-Phi_hash))))); ]", code._stop.toString());
//assertEquals("-4898613127354160978", Eval2.eval(code, 2));
}
@@ -327,7 +327,7 @@ return A[1];
public void sieveOfEratosthenes() throws IOException {
String src = Files.readString(Path.of("src/test/java/com/seaofnodes/simple/progs/sieve.smp"));
CodeGen code = new CodeGen(src).driver(Phase.LocalSched,"x86_64_v2", "SystemV");
- assertEquals("return [u32];", code.print());
+ assertEquals("return []u32;", code.print());
//assertEquals("u32[ 2,3,5,7,11,13,17,19]",Eval2.eval(code, 20));
}
@@ -340,7 +340,7 @@ val fcn = arg ? { int x -> x*x; } : { int x -> x+x; };
return fcn(2)*10 + fcn(3);
""");
code.driver(Phase.LocalSched,"x86_64_v2", "SystemV");
- assertEquals("Stop[ return (shli,Parm_x($fun2,int)); return (mul,Parm_x($fun1,int),x); return (add,#2,(muli,#2)); ]", code.print());
+ assertEquals("Stop[ return (add,#2,(muli,#2)); return (mul,Parm_x($fun21,i64),x); return (shli,Parm_x($fun22,i64)); ]", code.print());
}
@Test
@@ -351,7 +351,7 @@ val sq = { int x -> x*x; };
return sq(arg) + sq(3);
""");
code.driver(Phase.LocalSched,"x86_64_v2", "SystemV");
- assertEquals("Stop[ return (mul,Parm_x(sq,int),x); return (add,#2,#2); ]", code.print());
+ assertEquals("Stop[ return (add,#2,#2); return (mul,Parm_x(sq,i64),x); ]", code.print());
}
@Test public void testSelectedMemory() {
diff --git a/src/test/java/com/seaofnodes/simple/Chapter20Test.java b/src/test/java/com/seaofnodes/simple/Chapter20Test.java
index 3575e461..08a23ef0 100644
--- a/src/test/java/com/seaofnodes/simple/Chapter20Test.java
+++ b/src/test/java/com/seaofnodes/simple/Chapter20Test.java
@@ -71,7 +71,7 @@ return f(s);
// The entry of g can disappear before its Return is folded into f.
// Exercise both worklist orders; deleting the entry does not kill 123.
for( int seed=0; seed<64; seed++ ) {
- CodeGen code = new CodeGen(src,com.seaofnodes.simple.type.TypeInteger.BOT,seed).parse().opto().typeCheck();
+ CodeGen code = new CodeGen(src,com.seaofnodes.simple.type.TypeInteger.BOT,seed,true).parse().opto().typeCheck();
assertEquals("seed "+seed, "123", Eval2.eval(code,0));
}
}
@@ -121,9 +121,9 @@ return f(s);
}
@Test public void testAlloc0() {
- testTarget("return new u8[arg];","x86_64_v2","SystemV",4,"return [u8];");
- testTarget("return new u8[arg];","riscv","SystemV",5,"return [u8];");
- testTarget("return new u8[arg];","arm","SystemV",5,"return [u8];");
+ testTarget("return new u8[arg];","x86_64_v2","SystemV",4,"return []u8;");
+ testTarget("return new u8[arg];","riscv","SystemV",5,"return []u8;");
+ testTarget("return new u8[arg];","arm","SystemV",5,"return []u8;");
}
@Test public void testBasic1() {
@@ -238,14 +238,9 @@ s.cs[0] = 67; // C
s.cs[1] = 108; // l
hashCode(s);
""";
- // Without an explicit return, default main must be removed completely.
- // Seed 0 previously left a loop with a missing branch successor.
- for( String cpu : new String[]{"x86_64_v2","riscv","arm"} )
- new CodeGen(src,com.seaofnodes.simple.type.TypeInteger.BOT,0)
- .driver(CodeGen.Phase.Encoding,cpu,"SystemV");
- testTarget(src,"x86_64_v2", "SystemV",0,null);
- testTarget(src,"riscv" , "SystemV",1,null);
- testTarget(src,"arm" , "SystemV",0,null);
+ testTarget(src,"x86_64_v2", "SystemV",9,null);
+ testTarget(src,"riscv" , "SystemV",4,null);
+ testTarget(src,"arm" , "SystemV",1,null);
}
@Test
diff --git a/src/test/java/com/seaofnodes/simple/Chapter21AllocTest.java b/src/test/java/com/seaofnodes/simple/Chapter21AllocTest.java
index b81483d8..bab7a86a 100644
--- a/src/test/java/com/seaofnodes/simple/Chapter21AllocTest.java
+++ b/src/test/java/com/seaofnodes/simple/Chapter21AllocTest.java
@@ -46,7 +46,7 @@ return sqrt(arg) + sqrt(arg+2);
@Test
public void testString() throws IOException {
- String src = Files.readString(Path.of("src/test/java/com/seaofnodes/simple/progs/stringHash.smp"));
+ String src = Files.readString(Path.of("src/test/java/com/seaofnodes/simple/progs/stringHash21.smp"));
Chapter21Test.testCPU(src,"x86_64_v2", "SystemV", 9,null);
Chapter21Test.testCPU(src,"riscv" , "SystemV", 3,null);
Chapter21Test.testCPU(src,"arm" , "SystemV", 1,null);
diff --git a/src/test/java/com/seaofnodes/simple/Chapter21Test.java b/src/test/java/com/seaofnodes/simple/Chapter21Test.java
index 2c91de0d..30d22fc9 100644
--- a/src/test/java/com/seaofnodes/simple/Chapter21Test.java
+++ b/src/test/java/com/seaofnodes/simple/Chapter21Test.java
@@ -8,6 +8,7 @@ import com.seaofnodes.simple.codegen.RegAllocTestSupport.CheckedCodeGen;
import com.seaofnodes.simple.codegen.CodeGen;
import com.seaofnodes.simple.node.cpus.arm.arm;
import com.seaofnodes.simple.node.cpus.riscv.riscv;
+import com.seaofnodes.simple.util.SB;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
@@ -150,6 +151,30 @@ public class Chapter21Test {
assertTrue("Ordinary calls must survive optimization",checkArmCalls(src,"run",4,27)>0);
}
+ @Test public void testArmVectorGrowth() throws IOException {
+ String src = """
+ struct _Vec {
+ u32 !len;
+ int[] !buf;
+ val grow = { int sz ->
+ var buf2 = new int[sz];
+ for( int i=0; i<len; i++ ) buf2[i]=buf[i];
+ buf=buf2;
+ };
+ val add = { int e ->
+ if( len>=buf# ) grow(buf#*2);
+ buf[len++]=e;
+ return self;
+ };
+ };
+ val run = { int arg ->
+ val v = new _Vec{buf=new int[4];}.add(2).add(3).add(5).add(7).add(11);
+ return v.len*100+v.buf[arg];
+ };
+ """;
+ checkArmCalls(src,"run",4,511);
+ }
+
private static int checkArmCalls(String src, String entryName, int arg, int result) throws IOException {
CodeGen code = new CodeGen(src).driver("arm","SystemV",null);
byte[] image = new byte[1<<20];
@@ -192,19 +217,8 @@ public class Chapter21Test {
}
- @Test public void testArmSubtractRegisters() {
- EvalArm64 cpu = new EvalArm64(new byte[16],16);
- cpu.st4(0,0xCB020020); // SUB X0,X1,X2, no shift or flag update.
- for( long[] pair : new long[][]{{7,3},{3,7},{Long.MIN_VALUE,1},{1L<<40,3}} ) {
- cpu._pc = 0;
- cpu.regs[1] = pair[0]; cpu.regs[2] = pair[1];
- cpu.N=true; cpu.Z=false; cpu.C=true; cpu.V=true;
- assertEquals(0,cpu.step(1));
- assertEquals(pair[0]-pair[1],cpu.regs[0]);
- assertTrue(cpu.N); assertFalse(cpu.Z); assertTrue(cpu.C); assertTrue(cpu.V);
- }
- }
-
+ @Test public void testCoalescing() { com.seaofnodes.simple.codegen.RegAllocTestSupport.coalescing(); }
+ @Test public void testCopyForward() throws Exception { com.seaofnodes.simple.codegen.RegAllocTestSupport.copyForward(); }
@Test public void testNarrowStores() throws IOException {
for( String type : new String[]{"i8","u8","i16","u16"} ) {
@@ -252,8 +266,20 @@ public class Chapter21Test {
fail("A failing native program must fail the test, even with empty stdout");
}
- @Test public void testCoalescing() { com.seaofnodes.simple.codegen.RegAllocTestSupport.coalescing(); }
- @Test public void testCopyForward() throws Exception { com.seaofnodes.simple.codegen.RegAllocTestSupport.copyForward(); }
+ @Test public void testArmSubtractRegisters() {
+ EvalArm64 cpu = new EvalArm64(new byte[16],16);
+ cpu.st4(0,0xCB020020); // SUB X0,X1,X2, no shift or flag update.
+ for( long[] pair : new long[][]{{7,3},{3,7},{Long.MIN_VALUE,1},{1L<<40,3}} ) {
+ cpu._pc = 0;
+ cpu.regs[1] = pair[0]; cpu.regs[2] = pair[1];
+ cpu.N=true; cpu.Z=false; cpu.C=true; cpu.V=true;
+ assertEquals(0,cpu.step(1));
+ assertEquals(pair[0]-pair[1],cpu.regs[0]);
+ assertTrue(cpu.N); assertFalse(cpu.Z); assertTrue(cpu.C); assertTrue(cpu.V);
+ }
+ }
+
+
@Test public void testRisc64BitStore() {
byte[] mem = new byte[24];
EvalRisc5 cpu = new EvalRisc5(mem,mem.length);
@@ -283,6 +309,7 @@ public class Chapter21Test {
}
+ // Enabled in Chapter 23; measured there by Chapter23AllocTest.
@Test @Ignore
public void testJig() throws IOException {
String src = Files.readString(Path.of("src/test/java/com/seaofnodes/simple/progs/jig.smp"));
@@ -389,7 +416,7 @@ public class Chapter21Test {
String sprimes = sb.p("]").toString();
// Compile, link against native C; expect the above string of primes to be printed out by C
- TestC.run("sieve",sprimes, 185);
+ TestC.run("sieve",sprimes, 186);
// Evaluate on RISC5 emulator; expect return of an array of primes in
// the simulated heap.
@@ -405,7 +432,7 @@ public class Chapter21Test {
// Evaluate on ARM5 emulator; expect return of an array of primes in
// the simulated heap.
- EvalArm64 A5 = TestArm64.build("sieve", 100, 91, false);
+ EvalArm64 A5 = TestArm64.build("sieve", 100, 94, false);
int trap_arm = A5.step(10000);
assertEquals(0, trap_arm);
int ary_arm = (int)A5.regs[arm.X0];
@@ -419,14 +446,14 @@ public class Chapter21Test {
String fib = "[1, 1, 2, 3, 5, 8, 13, 21, 34, 55]";
TestC.run("fib", fib, 24);
- EvalRisc5 R5 = TestRisc5.build("fib", 9, 16, false);
+ EvalRisc5 R5 = TestRisc5.build("fib", 9, 17, false);
int trap = R5.step(100);
assertEquals(0,trap);
// Return register A0 holds fib(8)==55
assertEquals(55,R5.regs[riscv.A0]);
// arm
- EvalArm64 A5 = TestArm64.build("fib", 9, 16, false);
+ EvalArm64 A5 = TestArm64.build("fib", 9, 17, false);
int trap_arm = A5.step(100);
assertEquals(0,trap_arm);
// Return register X0 holds fib(8)==55
diff --git a/src/test/java/com/seaofnodes/simple/Chapter22Test.java b/src/test/java/com/seaofnodes/simple/Chapter22Test.java
index 2d94b8f0..ea210e3f 100644
--- a/src/test/java/com/seaofnodes/simple/Chapter22Test.java
+++ b/src/test/java/com/seaofnodes/simple/Chapter22Test.java
@@ -84,7 +84,7 @@ public class Chapter22Test {
val test_cmp={->return c_i32()==-1;};
""";
for( String conv : new String[]{"SystemV","Win64"} )
- TestC.run(src,conv,conv.equals("SystemV") ? "sysv_abi" : "ms_abi",
+ TestC.run(src,conv,com.seaofnodes.simple.type.TypeInteger.BOT,conv.equals("SystemV") ? "sysv_abi" : "ms_abi",
"src/test/java/com/seaofnodes/simple/progs/c_returns.c",
"build/objs/c_returns"+conv,"S","",-1);
}
@@ -108,7 +108,7 @@ public class Chapter22Test {
assertEquals(0,cpu.step(1000));
assertEquals(0,cpu._pc);
cpu._pc = (int)cpu.regs[riscv.A0]; // Call the returned address.
- cpu.regs[riscv.A0] = 0x2000;
+ cpu.regs[library ? riscv.A1 : riscv.A0] = 0x2000; // Methods also receive self.
assertEquals(0,cpu.step(1000));
assertEquals(0,cpu._pc);
if( library ) assertEquals("ok",cpu._stdout.toString());
@@ -119,7 +119,7 @@ public class Chapter22Test {
assertEquals(0,cpu.step(1000));
assertEquals(0,cpu._pc);
cpu._pc = (int)cpu.regs[0];
- cpu.regs[0] = 0x2000;
+ cpu.regs[library ? 1 : 0] = 0x2000;
assertEquals(0,cpu.step(1000));
assertEquals(0,cpu._pc);
if( library ) assertEquals("ok",cpu._stdout.toString());
@@ -138,6 +138,7 @@ public class Chapter22Test {
"build/objs/funptr"+(TestC.OS.startsWith("Windows") ? ".exe" : "")));
}
+
@Test public void testRiscvRightShifts() {
// SRAI, SRLI, SRA, SRL: a0 = -32 shifted by 4.
int[] ops = {0x40455513,0x00455513,0x40b55533,0x00b55533};
@@ -185,7 +186,7 @@ public class Chapter22Test {
testCPU(src,"arm","SystemV",0,"return Top;");
}
-
+ // Enabled in Chapter 23; measured there by Chapter23AllocTest.
@Test @Ignore
public void testJig() throws IOException {
String src = Files.readString(Path.of("src/test/java/com/seaofnodes/simple/progs/jig.smp"));
@@ -351,7 +352,7 @@ return cc.cz;
@Test
public void testHelloWorld() throws IOException {
String src = Files.readString(Path.of("src/test/java/com/seaofnodes/simple/progs/helloWorld.smp"));
- TestC.run(src,TestC.CALL_CONVENTION,null,null,"build/objs/helloWorld","","Hello, World!",0);
+ TestC.run(src,TestC.CALL_CONVENTION,null, null,null,"build/objs/helloWorld","","Hello, World!",0);
// Evaluate on RISC5 emulator
EvalRisc5 R5 = TestRisc5.build("helloWorld", 0, 2, false);
@@ -392,7 +393,7 @@ return sum(is);
@Test @Ignore
public void testEcho() throws IOException {
String src = Files.readString(Path.of("src/test/java/com/seaofnodes/simple/progs/echo.smp"));
- TestC.run(src,TestC.CALL_CONVENTION,null,null,"build/objs/echo","","",0);
+ TestC.run(src,TestC.CALL_CONVENTION,null, null,null,"build/objs/echo","","",0);
// Evaluate on RISC5 emulator
EvalRisc5 R5 = TestRisc5.build("echo", 0, 2, false);
diff --git a/src/test/java/com/seaofnodes/simple/Chapter23AllocTest.java b/src/test/java/com/seaofnodes/simple/Chapter23AllocTest.java
new file mode 100644
index 00000000..f3cc7512
--- /dev/null
+++ b/src/test/java/com/seaofnodes/simple/Chapter23AllocTest.java
@@ -0,0 +1,37 @@
+package com.seaofnodes.simple;
+
+import java.io.IOException;
+import java.nio.file.*;
+import org.junit.Ignore;
+import org.junit.Test;
+
+// Revised or newly enabled allocation examples in Chapter 23.
+public class Chapter23AllocTest {
+ @Test
+ public void testString() throws IOException {
+ String src = Files.readString(Path.of("src/test/java/com/seaofnodes/simple/progs/stringHash.smp"));
+ Chapter23Test.testCPU(src,"x86_64_v2", "SystemV", 9,null);
+ Chapter23Test.testCPU(src,"riscv" , "SystemV", 3,null);
+ Chapter23Test.testCPU(src,"arm" , "SystemV", 1,null);
+ }
+
+ @Ignore
+ @Test
+ public void testJig21() throws IOException {
+ String src = Files.readString(Path.of("src/test/java/com/seaofnodes/simple/progs/jig.smp"));
+ Chapter23Test.testCPU(src,"x86_64_v2", "Win64" ,-1,null);
+ Chapter23Test.testCPU(src,"riscv" , "SystemV",-1,null);
+ Chapter23Test.testCPU(src,"arm" , "SystemV",-1,null);
+ }
+
+ @Ignore
+ @Test
+ public void testJig22() throws IOException {
+ String src = Files.readString(Path.of("src/test/java/com/seaofnodes/simple/progs/jig.smp"));
+ //String src = Files.readString(Path.of("docs/examples/BubbleSort.smp"));
+ Chapter23Test.testCPU(src,"x86_64_v2", "Win64" ,-1,null);
+ Chapter23Test.testCPU(src,"riscv" , "SystemV",-1,null);
+ Chapter23Test.testCPU(src,"arm" , "SystemV",-1,null);
+ }
+
+}
diff --git a/src/test/java/com/seaofnodes/simple/Chapter23Test.java b/src/test/java/com/seaofnodes/simple/Chapter23Test.java
new file mode 100644
index 00000000..0feaa5de
--- /dev/null
+++ b/src/test/java/com/seaofnodes/simple/Chapter23Test.java
@@ -0,0 +1,319 @@
+package com.seaofnodes.simple;
+
+import com.seaofnodes.isa.eval.EvalRisc5;
+
+import com.seaofnodes.isa.eval.EvalArm64;
+
+import com.seaofnodes.simple.codegen.RegAllocTestSupport.CheckedCodeGen;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.type.*;
+
+
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.node.cpus.riscv.riscv;
+import com.seaofnodes.simple.type.TypeInteger;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import org.junit.Ignore;
+import org.junit.Test;
+import static org.junit.Assert.*;
+
+public class Chapter23Test {
+ @Test public void testPopularUses() { com.seaofnodes.simple.codegen.RegAllocTestSupport.popularUses(); }
+
+ @Test public void testDiagnosticTypeAccessors() throws Exception {
+ new CodeGen("return 0;").parse();
+ var tfp = TypeFunPtr.TEST;
+ var field = Type.class.getDeclaredField("VISIT");
+ field.setAccessible(true);
+ var visit = (java.util.Map<Object,Type>)field.get(null);
+ var marker = new Object();
+ visit.put(marker,Type.BOTTOM);
+ try {
+ assertEquals(0,TypeInteger.ZERO.alignment());
+ assertEquals(0,TypeInteger.ZERO.value());
+ org.junit.Assert.assertTrue(tfp._isConstant());
+ org.junit.Assert.assertSame(Type.BOTTOM,visit.get(marker));
+ assertEquals(1,visit.size());
+ } finally { visit.clear(); }
+ }
+
+
+ @Test
+ public void testOrRhsLoopLocal() throws IOException {
+ checkOrRhsLoop("""
+ struct S { int f; };
+ int x = 0;
+ int result = (arg & 1) || (new S {
+ while (x < 3) x++;
+ f = ++x;
+ }).f;
+ return x * 10 + result;
+ """);
+ }
+
+ @Test
+ public void testOrRhsLoopMemory() throws IOException {
+ checkOrRhsLoop("""
+ struct S { int f; };
+ !S !s = new S;
+ int result = (arg & 1) || (new S {
+ while (s.f < 3) s.f++;
+ s.f++;
+ f = s.f;
+ }).f;
+ return s.f * 10 + result;
+ """);
+ }
+
+ private static void checkOrRhsLoop(String src) throws IOException {
+ CodeGen code = new CheckedCodeGen(src).driver("riscv", "SystemV", null);
+ SpillStats.record(code,"Chapter23","riscv","SystemV");
+ for (int arg : new int[]{0, 1, 2, 3}) {
+ byte[] image = new byte[1 << 20];
+ System.arraycopy(code._encoding.bits(), 0, image, 0, code._encoding._bits.size());
+ EvalRisc5 r5 = new EvalRisc5(image, 1 << 16);
+ r5.regs[riscv.A0] = arg;
+ assertEquals("Execution must finish", 0, r5.step(1000));
+ // False LHS runs the loop and final increment, returning 4.
+ // True LHS skips all RHS effects and returns the LHS value, 1.
+ assertEquals("OR result and side effects for arg=" + arg,
+ (arg & 1) == 0 ? 44 : 1, r5.regs[riscv.A0]);
+ }
+ }
+
+ @Ignore
+ @Test
+ public void testJig() throws IOException {
+ String src =
+"""
+var apply = { i64 x, { i64 -> u32 } fcn ->
+ return fcn(x);
+}
+""";
+ }
+
+ @Test @Ignore
+ public void myOwnSake2() throws IOException {
+ TestC.runS("print","Or",0);
+ }
+
+ static CodeGen testCPU( String src, String cpu, String os, int spills, String stop ) {
+ CodeGen code = new CheckedCodeGen(src).driver(CodeGen.Phase.Encoding,cpu,os);
+ SpillStats.record(code,"Chapter23",cpu,os);
+ SpillStats.checkSpills(spills,code._regAlloc._spillScaled);
+ if( stop != null )
+ assertEquals(stop, code._stop.toString());
+ return code;
+ }
+
+ @Test
+ public void testOr() throws IOException {
+ TestC.runS("or1","Or",0);
+
+ // Evaluate on RISC5 emulator
+ EvalRisc5 R5 = TestRisc5.build("or1", 0, 2, false);
+ int trap = R5.step(100);
+ assertEquals(0,trap);
+ assertEquals(0,R5.regs[riscv.A0]);
+ assertEquals("Or",R5._stdout.toString());
+
+ // Evaluate on ARM emulator
+ EvalArm64 arm = TestArm64.build("or1", 0, 2, false);
+ trap = arm.step(100);
+ assertEquals(0,trap);
+ assertEquals(0,arm.regs[0]);
+ assertEquals("Or",arm._stdout.toString());
+ }
+
+ @Test
+ public void testAnd() throws IOException {
+ TestC.runS("and1","And",0);
+
+ // Evaluate on RISC5 emulator
+ EvalRisc5 R5 = TestRisc5.build("and1", 0, 2, false);
+ int trap = R5.step(100);
+ assertEquals(0,trap);
+ assertEquals(0,R5.regs[riscv.A0]);
+ assertEquals("And",R5._stdout.toString());
+
+ // Evaluate on ARM emulator
+ EvalArm64 arm = TestArm64.build("and1", 0, 2, false);
+ trap = arm.step(100);
+ assertEquals(0,trap);
+ assertEquals(0,arm.regs[0]);
+ assertEquals("And",arm._stdout.toString());
+ }
+
+ @Test
+ public void testAndPtr() throws IOException {
+ String src =
+"""
+struct S { !S? !fld; };
+val ptr = arg == 1 ? null : new S{fld = arg==1 ? null : new S{fld = null;};};
+return ptr && ptr.fld ? "true" : "false";
+ """;
+ CodeGen code = new CodeGen(src).parse().opto().typeCheck();
+ assertEquals("[97-117][ 116,114,117,101]", Eval2.eval(code, 0));
+
+ // Evaluate on RISC5 emulator
+ EvalRisc5 R5 = TestRisc5.build("ptrand", 1, 8, false);
+ int trap = R5.step(100);
+ assertEquals(0,trap);
+ assertEquals(0,R5.regs[riscv.A0]);
+ assertEquals("false",R5._stdout.toString());
+
+ // Evaluate on ARM emulator
+ EvalArm64 arm = TestArm64.build("ptrand", 1, 8, false);
+ trap = arm.step(100);
+ assertEquals(0,trap);
+ assertEquals(0,arm.regs[0]);
+ assertEquals("false",arm._stdout.toString());
+ }
+
+ // conditional side effect
+ @Test
+ public void testCondSideEffAnd() throws IOException {
+ TestC.runS("and2","Effected",0);
+
+ // Evaluate on RISC5 emulator
+ EvalRisc5 R5 = TestRisc5.build("and2", 0, 2, false);
+ int trap = R5.step(100);
+ assertEquals(0,trap);
+ assertEquals(0,R5.regs[riscv.A0]);
+ assertEquals("Effected",R5._stdout.toString());
+
+ // Evaluate on ARM emulator
+ EvalArm64 arm = TestArm64.build("and2", 0, 2, false);
+ trap = arm.step(100);
+ assertEquals(0,trap);
+ assertEquals(0,arm.regs[0]);
+ assertEquals("Effected",arm._stdout.toString());
+ }
+
+
+ @Test
+ public void testCondSideEffOr() throws IOException {
+ TestC.runS("or2","Effected",0);
+
+ // Evaluate on RISC5 emulator
+ EvalRisc5 R5 = TestRisc5.build("or2", 0, 2, false);
+ int trap = R5.step(100);
+ assertEquals(0,trap);
+ assertEquals(0,R5.regs[riscv.A0]);
+ assertEquals("Effected",R5._stdout.toString());
+
+ // Evaluate on ARM emulator
+ EvalArm64 arm = TestArm64.build("or2", 0, 2, false);
+ trap = arm.step(100);
+ assertEquals(0,trap);
+ assertEquals(0,arm.regs[0]);
+ assertEquals("Effected",arm._stdout.toString());
+ }
+
+ // test it with function calls
+ @Test
+ public void testFuncCall() throws IOException {
+ TestC.runS("and3","Or",0);
+
+ // Evaluate on RISC5 emulator
+ EvalRisc5 R5 = TestRisc5.build("and3", 0, 2, false);
+ int trap = R5.step(100);
+ assertEquals(0,trap);
+ assertEquals(0,R5.regs[riscv.A0]);
+ assertEquals("Or",R5._stdout.toString());
+
+ // Evaluate on ARM emulator
+ EvalArm64 arm = TestArm64.build("and3", 0, 2, false);
+ trap = arm.step(100);
+ assertEquals(0,trap);
+ assertEquals(0,arm.regs[0]);
+ assertEquals("Or",arm._stdout.toString());
+ }
+
+ @Test
+ public void testFRefFields() {
+ String src =
+"""
+// A hypothetical scanner class
+struct Scan {
+ int !x;
+ u8[~] !buf;
+ // Skip whitespace
+ val skip = { !Scan s ->
+ while( s.buf[s.x] <= ' ' )
+ s.x++;
+ };
+ // Peek a character; if matched consume it, else false.
+ val peek = { !Scan !s, u8 c ->
+ skip(s);
+ if( s.buf[s.x] != c ) return false;
+ s.x++;
+ return true;
+ };
+};
+!Scan !s = new Scan{ buf = " q"; };
+return Scan.peek(s,'q');
+ """;
+
+ CodeGen code = new CodeGen(src).parse().opto().typeCheck();
+ assertEquals("return Phi(Region,0,1);", code._stop.toString());
+ assertEquals("1", Eval2.eval(code, 0));
+ testCPU(src,"x86_64_v2", "Win64" ,23,null);
+ };
+
+ @Test
+ public void testFRefFields2() {
+ String src =
+"""
+// A hypothetical scanner class
+struct Scan {
+ int !x;
+ u8[~] !buf;
+ // Skip whitespace
+ val skip = { ->
+ while( buf[x] <= ' ' )
+ x++;
+ return self;
+ };
+};
+~Scan s = new Scan{ buf = " q"; };
+return s.skip().x;
+ """;
+
+ try { new CodeGen(src).parse().opto().typeCheck(); fail(); }
+ catch( Exception e ) { org.junit.Assert.assertTrue(e.getMessage().startsWith("Argument #0 isa")); }
+ };
+
+
+ @Test
+ public void testMethod() {
+ String src =
+"""
+// A hypothetical scanner class
+struct Scan {
+ int !x;
+ u8[~] !buf;
+ // Skip whitespace
+ val skip = { ->
+ while( buf[x] <= ' ' )
+ x++;
+ };
+ val require = { u8 ch ->
+ skip();
+ buf[x++]==ch;
+ };
+};
+!Scan !s = new Scan{ buf = " [1,2]"; };
+return s.require('[');
+ """;
+
+ CodeGen code = new CodeGen(src).parse().opto().typeCheck();
+ assertEquals("return (.[]==91);", code._stop.toString());
+ assertEquals("1", Eval2.eval(code, 0));
+ testCPU(src,"x86_64_v2", "Win64" ,4,null);
+ };
+
+
+}
diff --git a/src/test/java/com/seaofnodes/simple/Eval2.java b/src/test/java/com/seaofnodes/simple/Eval2.java
index bbf88f1a..908cc2e0 100644
--- a/src/test/java/com/seaofnodes/simple/Eval2.java
+++ b/src/test/java/com/seaofnodes/simple/Eval2.java
@@ -4,6 +4,8 @@ import com.seaofnodes.simple.codegen.CodeGen;
import com.seaofnodes.simple.node.*;
import com.seaofnodes.simple.print.IRPrinter;
import com.seaofnodes.simple.type.*;
+import com.seaofnodes.simple.util.SB;
+import com.seaofnodes.simple.util.Utils;
import java.util.*;
public abstract class Eval2 {
@@ -115,7 +117,7 @@ public abstract class Eval2 {
public static String eval( CodeGen code, long arg ) { return eval(code,arg,1000); }
public static String eval( CodeGen code, long arg, int timeout ) {
if( code._start.uctrl()==null ) return ""; // The empty program
- SB trace = null; // new SB(); // TRACE, set to null for off, new SB() for on
+ SB trace = null; // = new SB(); // TRACE, set to null for off, new SB() for on
// Force local scheduling phase
code.driver(CodeGen.Phase.LocalSched);
// Set global, so don't have to pass everywhere
@@ -311,9 +313,22 @@ public abstract class Eval2 {
return switch ( t ) {
case TypeInteger i -> i.isConstant() ? (Long )i.value() : "INT";
case TypeFloat f -> f.isConstant() ? (Double)f.value() : "FLT";
- case TypeMemPtr tmp -> tmp;
case TypeFunPtr tfp -> tfp;
case TypeMem mem -> "MEM";
+ case TypeMemPtr tmp -> {
+ // Unknown-caller inputs are type placeholders, not heap constants.
+ if( !tmp.isConstant() ) yield tmp;
+ if( tmp._obj.isAry() ) { // Constant array ptr
+ TypeConAry con = (TypeConAry)tmp._obj.field("[]")._t;
+ Object[] xs = new Object[con.len()];
+ for( int i=0; i<con.len(); i++ )
+ xs[i] = con.at8(i);
+ yield xs;
+
+ } else {
+ throw Utils.TODO(); // Constant non-array ptr
+ }
+ }
default -> null;
};
}
@@ -322,8 +337,7 @@ public abstract class Eval2 {
private static int offToIdx( long off, TypeStruct t) {
off -= t.aryBase();
int scale = t.aryScale();
- long mask = (1L << scale)-1;
- assert (off & mask) == 0;
+ assert (off & ((1L << scale)-1)) == 0;
return (int)(off>>scale);
}
@@ -337,7 +351,7 @@ public abstract class Eval2 {
if( n!=x || n<0 )
throw new NegativeArraySizeException(""+n);
Object[] ary = new Object[n]; // Array body
- var elem = type._fields[1]._type;
+ var elem = type._fields[1]._t;
if (elem instanceof TypeInteger) {
Arrays.fill( ary, 0L );
} else if (elem instanceof TypeFloat) {
@@ -351,7 +365,7 @@ public abstract class Eval2 {
int num = type._fields.length;
Object[] ptr = new Object[num];
for( int i=0; i<num; i++ )
- ptr[i] = con(type._fields[i]._type.makeZero());
+ ptr[i] = con(type._fields[i]._t.makeZero());
return ptr;
}
@@ -359,11 +373,12 @@ public abstract class Eval2 {
Object f = val(ld.ptr());
// Check for dense constant array
if( f instanceof TypeMemPtr tmp ) {
- assert tmp._obj._con != TypeConAry.BOT;
- if( ld._name.equals("#") )
- return (long)tmp._obj._con.len();
- int idx = offToIdx(x(ld.off()),tmp._obj);
- return tmp._obj._con.at(idx);
+ //assert tmp._obj._con != TypeConAry.BOT;
+ //if( ld._name.equals("#") )
+ // return (long)tmp._obj._con.len();
+ //int idx = offToIdx(x(ld.off()),tmp._obj);
+ //return tmp._obj._con.at(idx);
+ throw Utils.TODO();
}
Object[] fs = (Object[])f;
if( ld._name.equals("#") )
@@ -421,14 +436,11 @@ public abstract class Eval2 {
case TypeMemPtr tmp -> {
if( visit.containsKey(x) ) yield sb.p("$cyclic");
visit.put(x,x);
- // Since never can close the type cycle, without cyclic types, have
- // to handle FRefs even here
- if( tmp.isFRef() )
- tmp = tmp.makeFrom(((TypeMemPtr)Parser.TYPES.get(tmp._obj._name))._obj);
+ assert !tmp.isFRef();
Object[] xs = (Object[])x; // Array of fields
if( tmp._obj.isAry() ) {
- Type elem = tmp._obj._fields[1]._type;
+ Type elem = tmp._obj._fields[1]._t;
if( elem == TypeInteger.U8 ) {
// Shortcut u8[] as a String
for( Object o : xs )
@@ -436,6 +448,7 @@ public abstract class Eval2 {
} else {
// Array of elements
elem.print(sb).p("[ ");
+ if( elem instanceof TypeConAry ) elem = TypeInteger.ZERO;
for( Object o : xs )
_print( elem, o, sb, visit ).p( "," );
sb.unchar().p("]");
@@ -444,7 +457,7 @@ public abstract class Eval2 {
sb.p(tmp._obj._name).p("{");
Field[] flds = tmp._obj._fields;
for( int i=0; i<flds.length; i++ )
- _print( flds[i]._type,xs[i], sb.p(flds[i]._fname).p("="), visit ).p(",");
+ _print( flds[i]._t,xs[i], sb.p(flds[i]._fname).p("="), visit ).p(",");
sb.unchar().p("}");
}
yield sb;
diff --git a/src/test/java/com/seaofnodes/simple/LiteralTest.java b/src/test/java/com/seaofnodes/simple/LiteralTest.java
index 2375f213..09ee30f5 100644
--- a/src/test/java/com/seaofnodes/simple/LiteralTest.java
+++ b/src/test/java/com/seaofnodes/simple/LiteralTest.java
@@ -22,7 +22,7 @@ public class LiteralTest {
private static void assertString(String text, byte[] expected) {
var code = new CodeGen("return \"" + text + "\";").parse().opto().typeCheck();
TypeMemPtr ptr = (TypeMemPtr)code.expr()._type;
- TypeConAryB bytes = (TypeConAryB)ptr._obj._con;
+ TypeConAryB bytes = (TypeConAryB)ptr._obj.field("[]")._t;
assertArrayEquals(text, expected, bytes._ary);
}
diff --git a/src/test/java/com/seaofnodes/simple/MemoryEncodingTest.java b/src/test/java/com/seaofnodes/simple/MemoryEncodingTest.java
index 4bd36256..52c0a573 100644
--- a/src/test/java/com/seaofnodes/simple/MemoryEncodingTest.java
+++ b/src/test/java/com/seaofnodes/simple/MemoryEncodingTest.java
@@ -176,7 +176,7 @@ public class MemoryEncodingTest {
c.append("return 0;\n}\n");
Files.writeString(Path.of(file+".c"),c);
TestC.run("val memory = { int arg -> "+SOURCES[p]+" };",
- TestC.CALL_CONVENTION,"",file+".c",file,"S","",-1);
+ TestC.CALL_CONVENTION,null,"",file+".c",file,"S","",-1);
}
}
}
diff --git a/src/test/java/com/seaofnodes/simple/MergeSortTest.java b/src/test/java/com/seaofnodes/simple/MergeSortTest.java
index 788bd9c2..39cacd98 100644
--- a/src/test/java/com/seaofnodes/simple/MergeSortTest.java
+++ b/src/test/java/com/seaofnodes/simple/MergeSortTest.java
@@ -3,6 +3,7 @@ package com.seaofnodes.simple;
import com.seaofnodes.isa.eval.EvalRisc5;
import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.util.SB;
import com.seaofnodes.simple.node.cpus.riscv.riscv;
import java.io.IOException;
import java.nio.file.Files;
diff --git a/src/test/java/com/seaofnodes/simple/Simple.java b/src/test/java/com/seaofnodes/simple/Simple.java
index cd795fae..aef810d1 100644
--- a/src/test/java/com/seaofnodes/simple/Simple.java
+++ b/src/test/java/com/seaofnodes/simple/Simple.java
@@ -2,12 +2,12 @@ package com.seaofnodes.simple;
import com.seaofnodes.simple.codegen.CodeGen;
import com.seaofnodes.simple.print.*;
-
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.io.InputStreamReader;
+import com.seaofnodes.simple.util.Utils;
import java.io.BufferedReader;
import java.io.IOException;
+import java.io.InputStreamReader;
+import java.nio.file.Files;
+import java.nio.file.Path;
import java.util.stream.Collectors;
public class Simple {
diff --git a/src/test/java/com/seaofnodes/simple/SpillStats.java b/src/test/java/com/seaofnodes/simple/SpillStats.java
index 82cffef8..6af51b33 100644
--- a/src/test/java/com/seaofnodes/simple/SpillStats.java
+++ b/src/test/java/com/seaofnodes/simple/SpillStats.java
@@ -28,7 +28,8 @@ public class SpillStats extends RunListener {
// The same native helpers serve old and new program cohorts.
public static void recordNative(CodeGen code, String cpu, String abi) {
- String cohort = ACTIVE!=null && ACTIVE._test.getTestClass()==Chapter22Test.class
+ String cohort = ACTIVE!=null && ACTIVE._test.getTestClass()==Chapter23Test.class
+ ? "Chapter23" : ACTIVE!=null && ACTIVE._test.getTestClass()==Chapter22Test.class
? "Chapter22" : "Chapter21";
record(code,cohort,cpu,abi);
}
@@ -46,7 +47,7 @@ public class SpillStats extends RunListener {
}
public static void main(String[] args) throws ClassNotFoundException {
- if( args.length==0 ) args = new String[]{"Chapter20Test","Chapter21Test","Chapter21AllocTest","Chapter22Test","BrainFuckTest","MergeSortTest"};
+ if( args.length==0 ) args = new String[]{"Chapter20Test","Chapter21Test","Chapter21AllocTest","Chapter22Test","Chapter23Test","Chapter23AllocTest","BrainFuckTest","MergeSortTest"};
Class<?>[] tests = new Class<?>[args.length];
for( int i=0; i<args.length; i++ )
tests[i] = Class.forName("com.seaofnodes.simple."+args[i]);
diff --git a/src/test/java/com/seaofnodes/simple/TestC.java b/src/test/java/com/seaofnodes/simple/TestC.java
index 44c06128..2e61211a 100644
--- a/src/test/java/com/seaofnodes/simple/TestC.java
+++ b/src/test/java/com/seaofnodes/simple/TestC.java
@@ -2,6 +2,9 @@ package com.seaofnodes.simple;
import com.seaofnodes.simple.codegen.RegAllocTestSupport.CheckedCodeGen;
import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.type.TypeInteger;
+import com.seaofnodes.simple.util.Ary;
+import com.seaofnodes.simple.util.Utils;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
@@ -28,7 +31,7 @@ public abstract class TestC {
}
// Compile and run a simple program
- public static void run( String dir, String file, String expected, int spills ) throws IOException {
+ public static void run( String dir, String file, TypeInteger arg, String expected, int spills, boolean standalone) throws IOException {
// Files
String cfile = dir+"/"+file+".c" ;
String sfile = dir+"/"+file+".smp";
@@ -36,16 +39,29 @@ public abstract class TestC {
// Compile and export Simple
String src = Files.readString(Path.of(sfile));
- run(src,CALL_CONVENTION,"",cfile,efile,"S",expected,spills);
+ run(src,CALL_CONVENTION,arg, "",standalone? null: cfile,efile,"S",expected,spills);
+ }
+ public static void run(String dir, String file, String expected, int spills ) throws IOException {
+ run(dir, file,null, expected, spills, false);
+ }
+
+ public static void runS(String file, String expected, int spills ) throws IOException {
+ run("src/test/java/com/seaofnodes/simple/progs", file, null, expected, spills, true);
}
- public static void run( String src, String simple_conv, String c_conv, String cfile, String efile, String xtn, String expected, int spills ) throws IOException {
+ public static void runS(String file, TypeInteger arg, String expected, int spills ) throws IOException {
+ run("src/test/java/com/seaofnodes/simple/progs", file, arg, expected, spills, true);
+ }
+
+
+ public static void run( String src, String simple_conv, TypeInteger arg, String c_conv, String cfile, String efile, String xtn, String expected, int spills ) throws IOException {
String bin = efile+xtn;
String obj = bin+".o";
String exe = OS.startsWith("Windows") ? bin+".exe" : bin;
// Compile simple, emit ELF
- CodeGen code = new CheckedCodeGen(src).driver( CPU_PORT, simple_conv, obj);
+ CodeGen code = new CheckedCodeGen(src, arg).driver( CPU_PORT, simple_conv, obj);
SpillStats.recordNative(code,CPU_PORT,simple_conv);
+
String result = gcc(obj, c_conv, cfile, false, exe );
assertEquals(expected,result);
diff --git a/src/test/java/com/seaofnodes/simple/TypeTest.java b/src/test/java/com/seaofnodes/simple/TypeTest.java
index 551bddad..b21af9ee 100644
--- a/src/test/java/com/seaofnodes/simple/TypeTest.java
+++ b/src/test/java/com/seaofnodes/simple/TypeTest.java
@@ -7,27 +7,50 @@ import static org.junit.Assert.assertSame;
public class TypeTest {
+ // Struct hashes omit field types, so interning compares unlike leaf kinds.
+ @Test
+ public void testCyclicLeafKinds() {
+ TypeStruct bottom = TypeStruct.make("B14LeafKinds",false,
+ Field.make("x",Type.BOTTOM,2,false, false));
+ TypeStruct one = TypeStruct.make("B14LeafKinds",false,
+ Field.make("x",TypeFloat.constant(1.0),2,false, false));
+ TypeStruct two = TypeStruct.make("B14LeafKinds",false,
+ Field.make("x",TypeFloat.constant(2.0),2,false, false));
+
+ // Meeting creates a new struct while cyclic equality is active. Its
+ // float leaf must compare unequal to the previously interned BOTTOM.
+ TypeStruct meet = (TypeStruct)one.meet(two);
+ assertSame(TypeFloat.F32,meet._fields[0]._t);
+ assertSame(meet,two.meet(one));
+ assertSame(meet,TypeStruct.make("B14LeafKinds",false,
+ Field.make("x",TypeFloat.F32,2,false, false)));
+ assertSame(bottom,meet.meet(bottom));
+ assertSame(meet.dual(),one.dual().join(two.dual()));
+ }
+
// Test basic properties and GLB
@Test
public void testTypeAdHoc() {
Assert.assertEquals( Type.BOTTOM, TypeInteger.TRUE.meet(TypeNil.NIL) );
- Assert.assertEquals( Type.BOTTOM, TypeInteger.TOP.meet(TypeNil.NIL) );
-
-
- TypeStruct s1 = TypeStruct.make("s1", new Field[]{
- Field.make("a", TypeInteger.BOT,-1, false),
- Field.make("b", TypeInteger.BOT,-2, false) });
- TypeStruct s2 = TypeStruct.make("s2", new Field[]{
- Field.make("a", TypeInteger.BOT,-3, false),
- Field.make("b", TypeInteger.BOT,-4, false) });
- Assert.assertEquals(s1.makeRO(), s1.glb(false));
+ Assert.assertEquals( Type.BOTTOM, TypeInteger.TOP .meet(TypeNil.NIL) );
+
+ TypeStruct s1 = TypeStruct.make("s1",false,
+ Field.make("a", TypeInteger.BOT,-1, false, false),
+ Field.make("b", TypeInteger.BOT,-2, false, false) );
+ TypeStruct s2 = TypeStruct.make("s2",false,
+ Field.make("a", TypeInteger.BOT,-3, false, false),
+ Field.make("b", TypeInteger.BOT,-4, false, false) );
+ TypeStruct x1ro = (TypeStruct)s1.makeRO();
+ TypeMemPtr p1 = TypeMemPtr.make(s1);
+ Assert.assertEquals(x1ro, ((TypeMemPtr)p1.glb(false))._obj);
Assert.assertNotEquals(s1, s1.dual());
- Assert.assertEquals(s1.makeRO(), s1.dual().glb(false));
+ TypeStruct s1dglb = ((TypeMemPtr)p1.dual().glb(false))._obj;
+ Assert.assertTrue(x1ro.isa(s1dglb));
- TypeMem m1 = TypeMem.make(1,TypeNil.NIL);
- TypeMem m2 = TypeMem.make(2,TypeInteger.U16);
- TypeMem m3 = TypeMem.make(3,TypeFloat.F64);
- TypeMem m4 = TypeMem.make(4,TypeInteger.BOT);
+ TypeMem m1 = TypeMem.make(2,TypeNil.NIL);
+ TypeMem m2 = TypeMem.make(3,TypeInteger.U16);
+ TypeMem m3 = TypeMem.make(4,TypeFloat.F64);
+ TypeMem m4 = TypeMem.make(5,TypeInteger.BOT);
Assert.assertNotEquals(m1, m2);
Assert.assertNotEquals(m2, m3);
@@ -35,11 +58,11 @@ public class TypeTest {
Assert.assertEquals(TypeStruct.BOT, s1.meet(s2));
Assert.assertEquals(TypeMem .BOT, m1.meet(m2));
- Assert.assertEquals(TypeMem.make(0,Type.BOTTOM), m1.meet(m3));
+ Assert.assertEquals(TypeMem.make(1,Type.BOTTOM), m1.meet(m3));
Assert.assertEquals(TypeMem .BOT, m3.meet(m4));
- Assert.assertEquals(TypeMem.make(1,Type.BOTTOM), m1.glb(false));
- Assert.assertEquals(TypeMem.make(1,Type.XNIL), m1.dual());
+ Assert.assertEquals(TypeMem.make(2,Type.BOTTOM), m1.glb(false));
+ Assert.assertEquals(TypeMem.make(2,Type.XNIL), m1.dual());
Assert.assertEquals(m4.dual(), m4.glb(false).dual());
TypeMemPtr ptr1 = TypeMemPtr.make(s1);
@@ -55,11 +78,13 @@ public class TypeTest {
Assert.assertEquals(s2, ptr2nil._obj);
Assert.assertNotEquals(ptr1, ptr2);
- Assert.assertNotEquals(ptr1, ptr1.glb(false));
- Assert.assertEquals(ptr1nil.makeFrom(s1.glb(false)).makeRO(), ptr1.glb(false));
+ Type p1glb = ptr1.glb(false);
+ Assert.assertNotEquals(ptr1, p1glb );
+ Assert.assertTrue(((TypeMemPtr)p1glb)._ro);
+ Assert.assertTrue(ptr1nil.isa(p1glb));
Assert.assertEquals(ptr1, ptr1.dual().dual());
- Assert.assertEquals(ptr1.glb(false).makeFrom(s1.dual().glb(false)), ptr1.dual().glb(false));
+ Assert.assertTrue(p1glb.makeRO().isa( ptr1.dual().glb(false)));
Assert.assertEquals(TypeMemPtr.makeNullable(TypeStruct.BOT).withAccess(false), ptr1.meet(ptr2nil));
Assert.assertEquals(ptr1nil, ptr1.meet(TypeNil.NIL));
@@ -77,6 +102,19 @@ public class TypeTest {
Assert.assertEquals(TypePtr.XNPTR, NULL_join_PTR);
Type ptr1_dual = ptr1.dual();
Type nullableptr1_dual = ptr1nil.dual();
+
+ // Cyclic check
+ TypeStruct S1 = ((TypeMemPtr)TypeStruct.SFLT2.field("s1")._t)._obj;
+ Assert.assertFalse(s1.isFinal());
+ Type s1ro = S1.makeRO();
+ Type s1ro2 = S1.makeRO();
+ Assert.assertSame(s1ro,s1ro2);
+ Assert.assertTrue(s1ro.isFinal());
+
+ Assert.assertFalse(S1.isConstant());
+ Type s1glb = S1.field("s2")._t.glb(false);
+ Type s1glb2 = S1.field("s2")._t.glb(false);
+ Assert.assertSame(s1glb,s1glb2);
}
// Test theoretical properties.
@@ -150,9 +188,32 @@ public class TypeTest {
// Test cyclic types and meets
@Test
public void testCyclic0() {
- Type d0 = TypeStruct.S1.dual();
+ Type d0 = TypeStruct.SFLT2.dual();
Type d1 = d0.dual();
- assertSame(TypeStruct.S1,d1);
+ assertSame(TypeStruct.SFLT2,d1);
+ }
+
+ @Test
+ public void testGLB() {
+ Type[] ts = Type.gather();
+ for( Type t0 : ts )
+ if( !(t0 instanceof Field || t0 instanceof TypeStruct || t0 instanceof TypeTuple || t0 instanceof TypeConAry ) )
+ Assert.assertTrue(t0.isa(t0.glb(false)));
}
+ @Test
+ public void testList() {
+ TypeStruct list = TypeStruct.open("List");
+ TypeMemPtr plist = TypeMemPtr.makeNullable(list);
+ list = list.add(Field.make("next", plist, 2, false, false ) );
+ list = list.add(Field.make("x", TypeInteger.BOT, 3, false, false));
+ // Make a cyclic type
+ list = list.close();
+ // Fields are mutable
+ Assert.assertFalse(list.isFinal());
+ TypeStruct flist = (TypeStruct)list.makeRO();
+ Assert.assertTrue(flist.isFinal());
+ Assert.assertNotSame( list, flist );
+
+ }
}
diff --git a/src/test/java/com/seaofnodes/simple/codegen/RegAllocTestSupport.java b/src/test/java/com/seaofnodes/simple/codegen/RegAllocTestSupport.java
index 2d9860a6..ad898be3 100644
--- a/src/test/java/com/seaofnodes/simple/codegen/RegAllocTestSupport.java
+++ b/src/test/java/com/seaofnodes/simple/codegen/RegAllocTestSupport.java
@@ -118,9 +118,52 @@ public class RegAllocTestSupport {
assertTrue(BuildLRG.run(0,new RegAlloc(code)));
}
+ private static class CallOp extends CallNode implements MachNode {
+ CallOp(Node ctrl, Node def) { super(null,ctrl,null,def,def); }
+ @Override public RegMask regmap(int i) { return i==2 ? A : i==3 ? B : null; }
+ @Override public RegMask outregmap() { return null; }
+ @Override public void encoding(Encoding enc) { throw new AssertionError(); }
+ }
+
+ public static void popularUses() {
+ for( int calls=0; calls<=2; calls++ ) {
+ CodeGen code = graph();
+ RegAlloc alloc = new RegAlloc(code);
+ RegionNode block = new RegionNode(null,code._start,code._start);
+ Op def = new Op(new RegMask(7L),null,null,false,block);
+ LRG lrg = alloc.newLRG(def);
+ lrg._machDef = def;
+ lrg._mask = new RegMask(0L);
+ Op ab = new Op(null,new RegMask(3L),null,false,block,def);
+ Op bc = new Op(null,new RegMask(6L),null,false,block,def);
+ Op a = new Op(null,A,null,false,block,def,def);
+ Op b = new Op(null,B,null,false,block,def);
+ Op dep = new Op(null,null,null,false,block,def);
+ CallOp call = calls>0 ? new CallOp(block,def) : null;
+ if( calls==2 ) new CallOp(block,def);
+ boolean split = alloc.splitEmptyMaskByUse((byte)0,lrg);
+ assertEquals(calls<2,split);
+ if( !split ) { assertSame(def,a.in(1)); assertSame(def,b.in(1)); continue; }
+ // Two shared copies, even with overlapping masks and repeated inputs.
+ assertTrue(a.in(1) instanceof SplitNode);
+ assertTrue(b.in(1) instanceof SplitNode);
+ assertNotSame(a.in(1),b.in(1));
+ assertSame(a.in(1),a.in(2));
+ assertSame(b.in(1),ab.in(1));
+ assertSame(b.in(1),bc.in(1));
+ assertSame(def,dep.in(1)); // A scheduling dependency has no register.
+ assertEquals(3,def.nOuts()); // Two copies and the dependency.
+ if( call!=null ) {
+ assertSame(a.in(1),call.in(2));
+ assertSame(b.in(1),call.in(3));
+ }
+ }
+ }
+
// Check the scheduled graph before encoding adds untyped branches or rewrites tail calls.
public static class CheckedCodeGen extends CodeGen {
public CheckedCodeGen(String src) { super(src); }
+ public CheckedCodeGen(String src, TypeInteger arg) { super(src,arg); }
@Override public CodeGen regAlloc() {
super.regAlloc();
checkRegisters(this);
diff --git a/src/test/java/com/seaofnodes/simple/evaluator/Evaluator.java b/src/test/java/com/seaofnodes/simple/evaluator/Evaluator.java
index cb98971b..20aac367 100644
--- a/src/test/java/com/seaofnodes/simple/evaluator/Evaluator.java
+++ b/src/test/java/com/seaofnodes/simple/evaluator/Evaluator.java
@@ -1,9 +1,8 @@
package com.seaofnodes.simple.evaluator;
-import com.seaofnodes.simple.Utils;
import com.seaofnodes.simple.node.*;
import com.seaofnodes.simple.type.*;
-
+import com.seaofnodes.simple.util.Utils;
import java.util.*;
public class Evaluator {
@@ -190,7 +189,7 @@ public class Evaluator {
if( n < 0 )
throw new NegativeArraySizeException(""+n);
body = new Object[(int)n+1]; // Array body
- var elem = type._fields[1]._type;
+ var elem = type._fields[1]._t;
if (elem instanceof TypeInteger) {
for (int i=0; i<n; i++) body[i+1] = 0L;
} else if (elem instanceof TypeFloat) {
@@ -203,7 +202,7 @@ public class Evaluator {
} else {
body = new Object[num = type._fields.length];
for( int i=0; i<num; i++ )
- body[i] = switch( alloc._ptr._obj._fields[i]._type ) {
+ body[i] = switch( alloc._ptr._obj._fields[i]._t ) {
case TypeInteger ti -> 0L;
case TypeFloat tf -> 0D;
default -> null;
diff --git a/src/test/java/com/seaofnodes/simple/evaluator/Scheduler.java b/src/test/java/com/seaofnodes/simple/evaluator/Scheduler.java
index 7ba9272f..fee0f2a8 100644
--- a/src/test/java/com/seaofnodes/simple/evaluator/Scheduler.java
+++ b/src/test/java/com/seaofnodes/simple/evaluator/Scheduler.java
@@ -1,9 +1,8 @@
package com.seaofnodes.simple.evaluator;
-import com.seaofnodes.simple.Utils;
import com.seaofnodes.simple.node.*;
import com.seaofnodes.simple.type.Type;
-
+import com.seaofnodes.simple.util.Utils;
import java.util.*;
/**
diff --git a/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java b/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java
index 586924b2..b29eb2c3 100644
--- a/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java
+++ b/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java
@@ -22,7 +22,7 @@ import java.util.function.Consumer;
*/
public class Fuzzer {
- private static final int EVAL_TIMEOUT = 1000;
+ private static final int EVAL_TIMEOUT = 10000;
/**
* List of exceptions already encountered.
@@ -82,10 +82,10 @@ public class Fuzzer {
private static void runCheck(String script, boolean valid) {
CodeGen code1;
try {
- code1 = FuzzerUtils.parse(script, 123);
+ code1 = FuzzerUtils.parse(script, 123, true);
} catch (RuntimeException e1) {
try {
- FuzzerUtils.parse(script, 456);
+ FuzzerUtils.parse(script, 456, false);
} catch (RuntimeException e2) {
if (FuzzerUtils.isExceptionFromSameCause(e1, e2)) {
if (!valid || e1.getClass() == Parser.ParseException.class) return;
@@ -95,7 +95,7 @@ public class Fuzzer {
}
throw e1;
}
- CodeGen code2 = FuzzerUtils.parse(script, 456);
+ CodeGen code2 = FuzzerUtils.parse(script, 456, false);
checkGraphs(code1, code2, 0);
checkGraphs(code1, code2, 1);
checkGraphs(code1, code2, 10);
diff --git a/src/test/java/com/seaofnodes/simple/fuzzer/FuzzerUtils.java b/src/test/java/com/seaofnodes/simple/fuzzer/FuzzerUtils.java
index b2be5b5f..45558541 100644
--- a/src/test/java/com/seaofnodes/simple/fuzzer/FuzzerUtils.java
+++ b/src/test/java/com/seaofnodes/simple/fuzzer/FuzzerUtils.java
@@ -94,13 +94,13 @@ class FuzzerUtils {
/**
* Parse and optimize using the given worklist seed
*/
- public static CodeGen parse(String script, long workListSeed) {
+ public static CodeGen parse(String script, long workListSeed, boolean reset) {
var err = System.err;
var out = System.out;
try {
System.setErr(NULL_PRINT_STREAM);
System.setOut(NULL_PRINT_STREAM);
- var code = new CodeGen(script, TypeInteger.BOT, workListSeed);
+ var code = new CodeGen(script, TypeInteger.BOT, workListSeed, reset);
return code.driver(CodeGen.Phase.LocalSched);
} finally {
System.setErr(err);
diff --git a/src/test/java/com/seaofnodes/simple/fuzzer/Reducer.java b/src/test/java/com/seaofnodes/simple/fuzzer/Reducer.java
index 3731b40c..856e6a24 100644
--- a/src/test/java/com/seaofnodes/simple/fuzzer/Reducer.java
+++ b/src/test/java/com/seaofnodes/simple/fuzzer/Reducer.java
@@ -41,14 +41,15 @@ public class Reducer {
new Replace(Pattern.compile("(?<=\\n|^)[^\\n]+(?=\\n|$)"), true, ""),
new Replace(Pattern.compile("(?<=[+\\-*/<>=(])--"), ""),
new Replace(Pattern.compile("\\b--"), "+"),
- new Replace(Pattern.compile("-"), ""),
+ new Replace(Pattern.compile("[-!]"), ""),
new Replace(Pattern.compile("\\b\\+-"), "-"),
- new Replace(Pattern.compile("(?<![+\\-*/<>=])(?:-|\\*|/|>=|<=|>|<|!=|==)(?![+\\-*/<>=])"), "+"),
+ new Replace(Pattern.compile("(?<![+\\-*/<>=&|])(?:-|\\*|/|>=|<=|>>>|>>|>|<<|<|!=|==|&&|\\|\\||&|\\||^)(?![+*/<>=&|])"), "+"),
+ new Replace(Pattern.compile("\\?([^;]+):"), "+"),
new Replace(Pattern.compile("\\{((?:[^{}]|\\{})+)(?:\\n[ \\t]*)?}"), "", "{}", 1),
new Replace(Pattern.compile("(?<=\\n|^)([ \\t]*)\\{\\n(.*?)\\n\\1}", Pattern.DOTALL), 2),
new Replace(Pattern.compile("\\(([^()]+)\\)"), 1),
- new Replace(Pattern.compile("\\d+\\+"), ""),
- new Replace(Pattern.compile("\\+\\d+"), ""),
+ new Replace(Pattern.compile("\\d+ ?\\+ ?"), ""),
+ new Replace(Pattern.compile(" ?\\+ ?\\d+"), ""),
new Replace(Pattern.compile("\\n\\n+"), "\n"),
new Replace(Pattern.compile("^\\n+|\\n+$"), ""),
new Replace(Pattern.compile("else *"), ""),
@@ -105,7 +106,7 @@ public class Reducer {
* Clean up variable names from the random ones generated by the fuzzer.
*/
private String cleanVariables(String script) {
- var matcher = Pattern.compile("\\b([0-9a-zA-Z_]+) ([0-9a-zA-Z_]+)").matcher(script);
+ var matcher = Pattern.compile("\\b([0-9a-zA-Z_]+) !?([0-9a-zA-Z_]+)").matcher(script);
int[] num = {0, 0};
while (matcher.find()) {
var type = matcher.group(1);
diff --git a/src/test/java/com/seaofnodes/simple/fuzzer/ScriptGenerator.java b/src/test/java/com/seaofnodes/simple/fuzzer/ScriptGenerator.java
index 79547c0d..6ec066e9 100644
--- a/src/test/java/com/seaofnodes/simple/fuzzer/ScriptGenerator.java
+++ b/src/test/java/com/seaofnodes/simple/fuzzer/ScriptGenerator.java
@@ -20,12 +20,9 @@ import static com.seaofnodes.simple.Parser.KEYWORDS;
public class ScriptGenerator {
private static final int MAX_STATEMENTS_PER_BLOCK = 5;
- private static final int MAX_BINARY_EXPRESSIONS_PER_EXPRESSION = 10;
- private static final int MAX_UNARY_EXPRESSIONS_PER_EXPRESSION = 6;
private static final int MAX_NAME_LENGTH = 10;
private static final int MAX_BLOCK_DEPTH = 4;
private static final int MAX_EXPRESSION_DEPTH = 10;
- private static final int MAX_SUFFIX_RECURSIVE_DEPTH = 10;
/**
* Number of spaces per indentation
@@ -44,14 +41,6 @@ public class ScriptGenerator {
* Flag for a statement that it contains a final if statement without an else branch.
*/
private static final int FLAG_IF_WITHOUT_ELSE = 0x02;
- /**
- * Binary operators.
- */
- private static final String[] BINARY_OP = {"==", "!=", "<", "<=", ">", ">=", "+", "-", "*", "/", "&", ">>", ">>>", "<<"};
- /**
- * Unary operators.
- */
- private static final String[] UNARY_OP = {"-"};
private static class Type {
final String name;
@@ -147,10 +136,6 @@ public class ScriptGenerator {
*/
private int depth = MAX_BLOCK_DEPTH;
/**
- * The depth of expressions allowed.
- */
- private int exprDepth = MAX_EXPRESSION_DEPTH;
- /**
* Current variables in scope.
*/
private final ArrayList<Variable> variables = new ArrayList<>();
@@ -568,7 +553,7 @@ public class ScriptGenerator {
*/
public int genIf() {
sb.append("if(");
- genExpression(TYPE_BOOL, true);
+ genExpression(TYPE_BOOL, true, true);
sb.append(") ");
var stop = genStatementBlock();
if ((stop & FLAG_IF_WITHOUT_ELSE) == 0 && random.nextInt(10) > 3) {
@@ -614,7 +599,7 @@ public class ScriptGenerator {
*/
public int genWhile() {
sb.append("while(");
- genExpression(TYPE_BOOL, true);
+ genExpression(TYPE_BOOL, true, true);
sb.append(") ");
loopDepth++;
genStatementBlock();
@@ -640,17 +625,17 @@ public class ScriptGenerator {
indentation += INDENTATION;
String name = getVarName();
printIndentation().append("int ").append(name).append("=");
- genExpression(TYPE_INT, true);
+ genExpression(TYPE_INT, true, false);
sb.append(";\n");
addVariable(name, TYPE_INT);
printIndentation().append("while(").append(name).append("<");
- genExpression(TYPE_INT, true);
+ genShift(TYPE_INT, true, false, MAX_EXPRESSION_DEPTH);
sb.append(") {\n");
indentation += INDENTATION;
depth--;
loopDepth++;
printIndentation().append(name).append("=").append(name).append("+");
- genExpression(TYPE_INT, true);
+ genExpression(TYPE_INT, true, false);
sb.append(";\n");
genStatements();
loopDepth--;
@@ -677,7 +662,7 @@ public class ScriptGenerator {
sb.append(generateInvalid() ? getRandomName() : declType(type)).append(" !").append(name);
if (!(type instanceof TypeNullable) || random.nextBoolean()) {
sb.append("=");
- genExpression(type, true);
+ genExpression(type, true, type==TYPE_BOOL);
}
sb.append(";");
addVariable(name, type);
@@ -712,7 +697,7 @@ public class ScriptGenerator {
declared = field.type;
}
sb.append("=");
- genExpression(declared, true);
+ genExpression(declared, true, declared==TYPE_BOOL);
sb.append(";");
return 0;
}
@@ -724,43 +709,127 @@ public class ScriptGenerator {
public int genReturn() {
var type = getType();
sb.append("return ");
- genExpression(type, true);
+ genExpression(type, true, false);
sb.append(";");
return FLAG_STOP;
}
- /**
- * Generate a binary expression.
- * This method does not care about operator precedence.
- */
- public void genExpression(Type type, boolean change) {
+ public void genExpression(Type type, boolean change, boolean preferLogical) {
+ genExpression(type, change, preferLogical, MAX_EXPRESSION_DEPTH);
+ }
+
+ public void genExpression(Type type, boolean change, boolean preferLogical, int d) {
if (change && generateInvalid()) type = getType();
- if (!(type instanceof TypeInt) && type != TYPE_FLT) {
- genUnary(type, false);
- return;
+ if (d > 0 && random.nextInt(10)==0) {
+ genLogical(TYPE_INT, true, true, d);
+ sb.append("?");
+ genExpression(type, true, preferLogical, d-1);
+ sb.append(":");
+ genExpression(type, false, preferLogical, d-1);
+ } else {
+ genLogical(type, false, preferLogical, d);
+ }
+ }
+
+ public void genLogical(Type type, boolean change, boolean preferLogical, int d) {
+ if (change && generateInvalid()) type = getType();
+ genBitwise(type, false, preferLogical, d);
+ while (d-->0 && random.nextInt(preferLogical?5:50) == 0) {
+ sb.append(random.nextBoolean()?"&&":"||");
+ genBitwise(type, true, preferLogical, d);
+ }
+ }
+
+ public void genBitwise(Type type, boolean change, boolean preferLogical, int d) {
+ if (change && generateInvalid()) type = getType();
+ genComparison(type, false, preferLogical, d);
+ while (type instanceof TypeInt && d-->0 && random.nextInt(preferLogical?50:5)==0) {
+ sb.append("&|^".charAt(random.nextInt(3)));
+ genComparison(type, true, preferLogical, d);
+ }
+ }
+
+ public void genComparison(Type type, boolean change, boolean preferLogical, int d) {
+ if (change && generateInvalid()) type = getType();
+ if (d > 0 && type instanceof TypeInt && random.nextInt(preferLogical?5:50)==0) {
+ type = getType();
+ genComparison(type, true, preferLogical, d-1);
+ sb.append(random.nextBoolean()?"==":"!=");
+ }
+ genChaining(type, false, preferLogical, d);
+ }
+
+ public void genChaining(Type type, boolean change, boolean preferLogical, int d) {
+ if (change && generateInvalid()) type = getType();
+ if (d>0 && type instanceof TypeInt && random.nextInt(preferLogical?5:50)==0) {
+ genShift(TYPE_INT, true, false, d);
+ char c = random.nextBoolean()?'>':'<';
+ sb.append(c);
+ if (random.nextInt(4)==0) sb.append('=');
+ genShift(TYPE_INT, true, false, --d);
+ while (d-->0 && random.nextInt(preferLogical?5:50)==0) {
+ if (generateInvalid()) c = c=='>' ? '<' : '>';
+ sb.append(c);
+ if (random.nextInt(4)==0) sb.append('=');
+ genShift(TYPE_INT, true, false, d);
+ }
+ } else {
+ genShift(type, false, preferLogical, d);
}
- var num = randLog(MAX_BINARY_EXPRESSIONS_PER_EXPRESSION);
- while(num-->0) {
- genUnary(type, false);
- sb.append(BINARY_OP[random.nextInt(BINARY_OP.length)]);
+ }
+
+ private static final String[] SHIFTS = {">>", ">>>", "<<"};
+
+ public void genShift(Type type, boolean change, boolean preferLogical, int d) {
+ if (change && generateInvalid()) type = getType();
+ genAddition(type, false, preferLogical, d);
+ while (d-- > 0 && type instanceof TypeInt && random.nextInt(50)==0) {
+ sb.append(SHIFTS[random.nextInt(3)]);
+ genAddition(type, true, preferLogical, d);
+ }
+ }
+
+ public void genAddition(Type type, boolean change, boolean preferLogical, int d) {
+ if (change && generateInvalid()) type = getType();
+ genMultiplication(type, false, preferLogical, d);
+ while (d-- > 0 && (type instanceof TypeInt || type==TYPE_FLT) && random.nextInt(50)==0) {
+ sb.append(random.nextBoolean()?" + ":" - ");
+ genMultiplication(type, true, preferLogical, d);
+ }
+ }
+
+ public void genMultiplication(Type type, boolean change, boolean preferLogical, int d) {
+ if (change && generateInvalid()) type = getType();
+ genUnary(type, false, preferLogical, d);
+ while (d-- > 0 && (type instanceof TypeInt || type==TYPE_FLT) && random.nextInt(50)==0) {
+ sb.append(random.nextBoolean()?'*':'/');
+ genUnary(type, true, preferLogical, d);
}
- genUnary(type, false);
}
/**
* Generate a unary expression.
*/
- public void genUnary(Type type, boolean change) {
+ public void genUnary(Type type, boolean change, boolean preferLogical, int d) {
if (change && generateInvalid()) type = getType();
if (!(type instanceof TypeInt) && type != TYPE_FLT) {
- genSuffix(type, false);
+ genSuffix(type, false, preferLogical, d);
return;
}
- var num = randLog(MAX_UNARY_EXPRESSIONS_PER_EXPRESSION);
- while(num-->0) {
- sb.append(UNARY_OP[random.nextInt(UNARY_OP.length)]);
+ while (d>0 && random.nextInt(5)==0) {
+ d--;
+ boolean what = type instanceof TypeInt && random.nextInt(preferLogical?2:20) == 0;
+ sb.append(what?'!':'-');
+ }
+ if (random.nextInt(10)==0) {
+ var t = type;
+ var v = findVisibleVariablesMatching(va->va.type.isa(t));
+ if (v != null) {
+ sb.append(random.nextBoolean()?"--":"++").append(v.name);
+ return;
+ }
}
- genSuffix(type, false);
+ genSuffix(type, false, preferLogical, d);
}
public TypeStruct.Field getField(Type type) {
@@ -780,45 +849,45 @@ public class ScriptGenerator {
throw new AssertionError();
}
- /**
- * Generate
- */
- public void genSuffix(Type type, boolean change) {
- genSuffixRecursive(type, change, MAX_SUFFIX_RECURSIVE_DEPTH);
- }
-
- public void genSuffixRecursive(Type type, boolean change, int depth) {
+ public void genSuffix(Type type, boolean change, boolean preferLogical, int d) {
if (change && generateInvalid()) type = getType();
- if (depth > 0 && random.nextBoolean()) {
- if (generateInvalid()) {
- var field = getRandomName();
- var t = getType();
- genSuffixRecursive(t, true, depth-1);
- sb.append(".").append(field);
+ if (d > 0 && random.nextBoolean() && genField(type, d-1)) return;
+ if (d > 0 && random.nextInt(10) == 0) {
+ if (random.nextBoolean() && genField(type, d-1)) return;
+ var t = type;
+ var v = findVisibleVariablesMatching(va -> va.type.isa(t));
+ if (v != null) {
+ sb.append(v.name).append(random.nextBoolean() ? "--" : "++");
return;
- } else {
- var field = getField(type);
- if (field != null) {
- genSuffixRecursive(field.struct, true, depth-1);
- sb.append(".").append(field.name);
- return;
- }
}
}
- genPrimary(type, false);
+ genPrimary(type, false, preferLogical, d);
+ }
+
+ public boolean genField(Type type, int d) {
+ if (generateInvalid()) {
+ var field = getRandomName();
+ var t = getType();
+ genSuffix(t, true, false, d);
+ sb.append(".").append(field);
+ } else {
+ var field = getField(type);
+ if (field == null) return false;
+ genSuffix(field.struct, true, false, d);
+ sb.append(".").append(field.name);
+ }
+ return true;
}
/**
* Generate a primary expression.
*/
- public void genPrimary(Type type, boolean change) {
+ public void genPrimary(Type type, boolean change, boolean preferLogical, int d) {
if (change && generateInvalid()) type = getType();
var rand = random.nextInt(10);
- if (rand == 0 && exprDepth != 0) {
+ if (d > 0 && rand == 0) {
sb.append("(");
- exprDepth--;
- genExpression(type, false);
- exprDepth++;
+ genExpression(type, false, preferLogical, d-1);
sb.append(")");
} else if (rand < 6) {
genVariable(type);
diff --git a/src/test/java/com/seaofnodes/simple/progs/and1.smp b/src/test/java/com/seaofnodes/simple/progs/and1.smp
new file mode 100644
index 00000000..ef4c904f
--- /dev/null
+++ b/src/test/java/com/seaofnodes/simple/progs/and1.smp
@@ -0,0 +1,10 @@
+// -*- mode: java; -*-
+int a = 1;
+int b = 1;
+
+if(a && b) {
+ sys.io.p("And");
+} else {
+ sys.io.p("Or");
+}
+return 0;
diff --git a/src/test/java/com/seaofnodes/simple/progs/and2.smp b/src/test/java/com/seaofnodes/simple/progs/and2.smp
new file mode 100644
index 00000000..2a463c94
--- /dev/null
+++ b/src/test/java/com/seaofnodes/simple/progs/and2.smp
@@ -0,0 +1,16 @@
+// -*- mode: java; -*-
+int a = 1;
+int b = 1;
+
+int x=1;
+int y=1;
+int z=0;
+
+int g = x++ && y++ && z++;
+
+if(x == 2 && y == 2 && z == 1 && g == 0) {
+ sys.io.p("Effected");
+} else {
+ sys.io.p("Not effected");
+}
+return 0;
diff --git a/src/test/java/com/seaofnodes/simple/progs/and3.smp b/src/test/java/com/seaofnodes/simple/progs/and3.smp
new file mode 100644
index 00000000..aadbdfed
--- /dev/null
+++ b/src/test/java/com/seaofnodes/simple/progs/and3.smp
@@ -0,0 +1,14 @@
+// -*- mode: java; -*-
+int a = 1;
+int b = 1;
+
+var sq = { int x ->
+ x*x;
+};
+
+if(a && sq(0)) {
+ sys.io.p("And");
+} else {
+ sys.io.p("Or");
+}
+return 0;
diff --git a/src/test/java/com/seaofnodes/simple/progs/funptr.c b/src/test/java/com/seaofnodes/simple/progs/funptr.c
index 7545992d..2abe6e74 100644
--- a/src/test/java/com/seaofnodes/simple/progs/funptr.c
+++ b/src/test/java/com/seaofnodes/simple/progs/funptr.c
@@ -2,7 +2,7 @@
#include <stdint.h>
typedef int64_t (*Function)(void);
-typedef int64_t (*Printer)(void *);
+typedef int64_t (*Printer)(void *, void *);
extern Function factory(int64_t);
extern Printer printer(void);
@@ -10,6 +10,6 @@ int main(void) {
assert(factory(1)() == 42);
assert(factory(0)() == 43);
struct { uint32_t length; char data[2]; } text = {2, {'o','k'}};
- printer()(&text);
+ printer()(0, &text); // Unused class receiver, then the byte array.
return 0;
}
diff --git a/src/test/java/com/seaofnodes/simple/progs/large.smp b/src/test/java/com/seaofnodes/simple/progs/large.smp
new file mode 100644
index 00000000..73271e66
--- /dev/null
+++ b/src/test/java/com/seaofnodes/simple/progs/large.smp
@@ -0,0 +1,5344 @@
+
+struct stbi_io_callbacks {
+ { i64 u8[] int -> int } read = "C"; // fill 'data' with 'size' bytes. return number of bytes actually read
+ { i64 int -> int } skip = "C"; // skip the next 'n' bytes, or 'unget' the last -n bytes if negative
+ { i64 -> int } eof = "C"; // returns nonzero if we are at end of file/data
+};
+
+struct stbi__context {
+ u32 img_x, img_y;
+ int img_n, img_out_n;
+
+ !stbi_io_callbacks !io;
+ u8[]io_user_data;
+
+ int read_from_callbacks;
+ int buflen;
+ u8[] buffer_start;
+ int callback_already_read;
+
+ u8[] img_buffer;
+ int img_buffer_end;
+ u8[] img_buffer_original;
+ int img_buffer_original_end;
+};
+
+{ stbi__context -> u8 } stbi__get8 = "C";
+
+val sample = { !stbi__context !s, int pal_len ->
+ var palette = new u8[1024];
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+ for (int i=0; i < pal_len; ++i) {
+ palette[i*4+3] = 0;
+
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ for (int j=0; j < pal_len; ++j) {
+ palette[j*4+3] = 0;
+
+ palette[j*4+0] = stbi__get8(s);
+ palette[j*4+1] = stbi__get8(s);
+ palette[j*4+2] = stbi__get8(s);
+ palette[j*4+3] = 255;
+ }
+ }
+
+ return palette;
+};
diff --git a/src/test/java/com/seaofnodes/simple/progs/or1.smp b/src/test/java/com/seaofnodes/simple/progs/or1.smp
new file mode 100644
index 00000000..f5f526c3
--- /dev/null
+++ b/src/test/java/com/seaofnodes/simple/progs/or1.smp
@@ -0,0 +1,12 @@
+// -*- mode: java; -*-
+int a = 1;
+int b = 0;
+
+if(a++ || b++ ) {
+ if(b == 0 && a == 2) {
+ sys.io.p("Or");
+ }
+} else{
+ sys.io.p("And");
+}
+return 0;
diff --git a/src/test/java/com/seaofnodes/simple/progs/or2.smp b/src/test/java/com/seaofnodes/simple/progs/or2.smp
new file mode 100644
index 00000000..ac688b5a
--- /dev/null
+++ b/src/test/java/com/seaofnodes/simple/progs/or2.smp
@@ -0,0 +1,23 @@
+// -*- mode: java; -*-
+int a = 1;
+int b = 1;
+
+int x = -1;
+int y=1;
+int z= -1;
+
+int g = x++ || y++ || z++;
+
+int switch = 0;
+if(x == 4 || y == 4 || z == 4) {
+ switch = -1;
+} else {
+ switch = 1;
+}
+int cd = 1;
+if((x == 0 && y == 1 && z==-1) && switch && g) {
+ sys.io.p("Effected");
+} else {
+ sys.io.p("Not effected");
+}
+return 0;
diff --git a/src/test/java/com/seaofnodes/simple/progs/print.smp b/src/test/java/com/seaofnodes/simple/progs/print.smp
new file mode 100644
index 00000000..3bb86268
--- /dev/null
+++ b/src/test/java/com/seaofnodes/simple/progs/print.smp
@@ -0,0 +1,8 @@
+int x = 1;
+
+int y = 2;
+if(x == 1) {
+y = 1;
+}
+
+return y; \ No newline at end of file
diff --git a/src/test/java/com/seaofnodes/simple/progs/ptrand.smp b/src/test/java/com/seaofnodes/simple/progs/ptrand.smp
new file mode 100644
index 00000000..8b8af571
--- /dev/null
+++ b/src/test/java/com/seaofnodes/simple/progs/ptrand.smp
@@ -0,0 +1,10 @@
+struct S { !S? !fld; };
+
+val ptr = arg == 1 ? null : new S{fld = arg==1 ? null : new S{fld = null;};};
+if( ptr && ptr.fld ) {
+ sys.io.p("true");
+} else {
+ sys.io.p("false");
+}
+
+return 0;
diff --git a/src/test/java/com/seaofnodes/simple/progs/stringHash.smp b/src/test/java/com/seaofnodes/simple/progs/stringHash.smp
index 257f79f9..7344fcdd 100644
--- a/src/test/java/com/seaofnodes/simple/progs/stringHash.smp
+++ b/src/test/java/com/seaofnodes/simple/progs/stringHash.smp
@@ -23,9 +23,8 @@ val hashCode = { !String 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];
+ for( int i=0; i< self.cs#; i++ )
+ hash = hash*31 + self.cs[i];
if( !hash ) hash = 123456789;
return hash;
};
diff --git a/src/test/java/com/seaofnodes/simple/progs/stringHash21.smp b/src/test/java/com/seaofnodes/simple/progs/stringHash21.smp
new file mode 100644
index 00000000..257f79f9
--- /dev/null
+++ b/src/test/java/com/seaofnodes/simple/progs/stringHash21.smp
@@ -0,0 +1,31 @@
+// -*- mode: java; -*-
+struct String {
+ u8[] cs;
+ int _hashCode;
+};
+
+// Compare two Strings
+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;
+};
+
+// Return the String hashCode (cached, and never 0)
+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;
+};