From deafd9a6fa34938c0c8c9589097e8c84e39bfa1b Mon Sep 17 00:00:00 2001 From: Cliff Click Date: Thu, 22 Oct 2026 15:47:50 -0700 Subject: Split out the constructor chapter --- .../main/java/com/seaofnodes/simple/IterPeeps.java | 230 ++ .../main/java/com/seaofnodes/simple/Parser.java | 2540 ++++++++++++++++++++ .../src/main/java/com/seaofnodes/simple/Var.java | 56 + .../com/seaofnodes/simple/codegen/BuildLRG.java | 95 + .../com/seaofnodes/simple/codegen/Coalesce.java | 105 + .../com/seaofnodes/simple/codegen/CodeGen.java | 407 ++++ .../com/seaofnodes/simple/codegen/ElfExpr.java | 87 + .../com/seaofnodes/simple/codegen/ElfFile.java | 382 +++ .../com/seaofnodes/simple/codegen/Encoding.java | 543 +++++ .../com/seaofnodes/simple/codegen/FinalizeIR.java | 76 + .../simple/codegen/GlobalCodeMotion.java | 311 +++ .../java/com/seaofnodes/simple/codegen/IFG.java | 539 +++++ .../com/seaofnodes/simple/codegen/InstSelect.java | 112 + .../java/com/seaofnodes/simple/codegen/LRG.java | 235 ++ .../com/seaofnodes/simple/codegen/LinkMem.java | 23 + .../seaofnodes/simple/codegen/ListScheduler.java | 261 ++ .../com/seaofnodes/simple/codegen/Machine.java | 109 + .../java/com/seaofnodes/simple/codegen/Opto.java | 244 ++ .../com/seaofnodes/simple/codegen/RIPRelSize.java | 14 + .../com/seaofnodes/simple/codegen/RegAlloc.java | 704 ++++++ .../com/seaofnodes/simple/codegen/RegMask.java | 122 + .../com/seaofnodes/simple/codegen/RegMaskRW.java | 36 + .../com/seaofnodes/simple/codegen/TypeCheck.java | 44 + .../java/com/seaofnodes/simple/node/AddFNode.java | 32 + .../java/com/seaofnodes/simple/node/AddNode.java | 178 ++ .../java/com/seaofnodes/simple/node/AndNode.java | 48 + .../com/seaofnodes/simple/node/ArithFNode.java | 31 + .../java/com/seaofnodes/simple/node/ArithNode.java | 206 ++ .../java/com/seaofnodes/simple/node/BoolNode.java | 202 ++ .../com/seaofnodes/simple/node/BulkMemPhiNode.java | 243 ++ .../java/com/seaofnodes/simple/node/CFGNode.java | 222 ++ .../java/com/seaofnodes/simple/node/CProjNode.java | 69 + .../com/seaofnodes/simple/node/CallEndMach.java | 36 + .../com/seaofnodes/simple/node/CallEndNode.java | 234 ++ .../java/com/seaofnodes/simple/node/CallNode.java | 187 ++ .../com/seaofnodes/simple/node/CalleeSaveNode.java | 23 + .../java/com/seaofnodes/simple/node/CastMach.java | 19 + .../java/com/seaofnodes/simple/node/CastNode.java | 79 + .../com/seaofnodes/simple/node/ConFldOffNode.java | 41 + .../com/seaofnodes/simple/node/ConstantNode.java | 67 + .../com/seaofnodes/simple/node/ConvertNode.java | 103 + .../java/com/seaofnodes/simple/node/CtrlNode.java | 16 + .../java/com/seaofnodes/simple/node/DivFNode.java | 27 + .../java/com/seaofnodes/simple/node/DivNode.java | 39 + .../com/seaofnodes/simple/node/EscapeMach.java | 12 + .../com/seaofnodes/simple/node/EscapeNode.java | 58 + .../com/seaofnodes/simple/node/ExternNode.java | 25 + .../java/com/seaofnodes/simple/node/FRefNode.java | 35 + .../com/seaofnodes/simple/node/FldOffNode.java | 24 + .../java/com/seaofnodes/simple/node/FunNode.java | 278 +++ .../com/seaofnodes/simple/node/FunPtrNode.java | 12 + .../java/com/seaofnodes/simple/node/GuardMach.java | 17 + .../java/com/seaofnodes/simple/node/GuardNode.java | 66 + .../java/com/seaofnodes/simple/node/IfNode.java | 105 + .../java/com/seaofnodes/simple/node/LoadNode.java | 322 +++ .../java/com/seaofnodes/simple/node/LoopNode.java | 104 + .../seaofnodes/simple/node/MachConcreteNode.java | 27 + .../java/com/seaofnodes/simple/node/MachNode.java | 60 + .../com/seaofnodes/simple/node/MemMergeNode.java | 117 + .../java/com/seaofnodes/simple/node/MemOpNode.java | 156 ++ .../com/seaofnodes/simple/node/MemPhiNode.java | 50 + .../com/seaofnodes/simple/node/MinusFNode.java | 32 + .../java/com/seaofnodes/simple/node/MinusNode.java | 77 + .../java/com/seaofnodes/simple/node/ModeNode.java | 12 + .../java/com/seaofnodes/simple/node/MulFNode.java | 37 + .../java/com/seaofnodes/simple/node/MulNode.java | 64 + .../java/com/seaofnodes/simple/node/MultiNode.java | 29 + .../java/com/seaofnodes/simple/node/NeverNode.java | 23 + .../java/com/seaofnodes/simple/node/NewNode.java | 112 + .../main/java/com/seaofnodes/simple/node/Node.java | 705 ++++++ .../java/com/seaofnodes/simple/node/NotNode.java | 26 + .../java/com/seaofnodes/simple/node/OrNode.java | 43 + .../java/com/seaofnodes/simple/node/ParmNode.java | 85 + .../java/com/seaofnodes/simple/node/PhiNode.java | 306 +++ .../java/com/seaofnodes/simple/node/ProjNode.java | 54 + .../com/seaofnodes/simple/node/PtrToIntMach.java | 17 + .../com/seaofnodes/simple/node/PtrToIntNode.java | 35 + .../com/seaofnodes/simple/node/ReadOnlyMach.java | 19 + .../com/seaofnodes/simple/node/ReadOnlyNode.java | 27 + .../com/seaofnodes/simple/node/RegionNode.java | 174 ++ .../com/seaofnodes/simple/node/ReturnNode.java | 186 ++ .../com/seaofnodes/simple/node/RoundF32Node.java | 34 + .../java/com/seaofnodes/simple/node/SarNode.java | 36 + .../java/com/seaofnodes/simple/node/ScopeNode.java | 521 ++++ .../java/com/seaofnodes/simple/node/ShlNode.java | 48 + .../java/com/seaofnodes/simple/node/ShrNode.java | 31 + .../java/com/seaofnodes/simple/node/SplitNode.java | 28 + .../java/com/seaofnodes/simple/node/StartNode.java | 49 + .../java/com/seaofnodes/simple/node/StopNode.java | 76 + .../java/com/seaofnodes/simple/node/StoreNode.java | 204 ++ .../com/seaofnodes/simple/node/StructNode.java | 56 + .../java/com/seaofnodes/simple/node/SubFNode.java | 28 + .../java/com/seaofnodes/simple/node/SubNode.java | 55 + .../com/seaofnodes/simple/node/ToFloatNode.java | 27 + .../com/seaofnodes/simple/node/ToIntegerNode.java | 23 + .../java/com/seaofnodes/simple/node/XCtrlNode.java | 16 + .../java/com/seaofnodes/simple/node/XorNode.java | 42 + .../seaofnodes/simple/node/cpus/arm/AddARM.java | 21 + .../seaofnodes/simple/node/cpus/arm/AddFARM.java | 23 + .../seaofnodes/simple/node/cpus/arm/AddIARM.java | 28 + .../seaofnodes/simple/node/cpus/arm/AndARM.java | 22 + .../seaofnodes/simple/node/cpus/arm/AndIARM.java | 25 + .../seaofnodes/simple/node/cpus/arm/AsrARM.java | 19 + .../seaofnodes/simple/node/cpus/arm/AsrIARM.java | 32 + .../seaofnodes/simple/node/cpus/arm/BranchARM.java | 78 + .../seaofnodes/simple/node/cpus/arm/CallARM.java | 51 + .../seaofnodes/simple/node/cpus/arm/CallRRARM.java | 33 + .../seaofnodes/simple/node/cpus/arm/CmpARM.java | 23 + .../seaofnodes/simple/node/cpus/arm/CmpFARM.java | 22 + .../seaofnodes/simple/node/cpus/arm/CmpIARM.java | 41 + .../seaofnodes/simple/node/cpus/arm/DivARM.java | 20 + .../seaofnodes/simple/node/cpus/arm/DivFARM.java | 20 + .../seaofnodes/simple/node/cpus/arm/ExtendARM.java | 31 + .../seaofnodes/simple/node/cpus/arm/F8ToIARM.java | 21 + .../seaofnodes/simple/node/cpus/arm/FloatARM.java | 44 + .../seaofnodes/simple/node/cpus/arm/FunARM.java | 22 + .../seaofnodes/simple/node/cpus/arm/I2F8ARM.java | 26 + .../seaofnodes/simple/node/cpus/arm/IntARM.java | 36 + .../seaofnodes/simple/node/cpus/arm/LoadARM.java | 34 + .../seaofnodes/simple/node/cpus/arm/LslARM.java | 20 + .../seaofnodes/simple/node/cpus/arm/LslIARM.java | 34 + .../seaofnodes/simple/node/cpus/arm/LsrARM.java | 20 + .../seaofnodes/simple/node/cpus/arm/LsrIARM.java | 29 + .../seaofnodes/simple/node/cpus/arm/MemOpARM.java | 79 + .../seaofnodes/simple/node/cpus/arm/MulARM.java | 20 + .../seaofnodes/simple/node/cpus/arm/MulFARM.java | 19 + .../seaofnodes/simple/node/cpus/arm/NegARM.java | 25 + .../seaofnodes/simple/node/cpus/arm/NewARM.java | 38 + .../seaofnodes/simple/node/cpus/arm/NotARM.java | 28 + .../com/seaofnodes/simple/node/cpus/arm/OrARM.java | 21 + .../seaofnodes/simple/node/cpus/arm/OrIARM.java | 25 + .../seaofnodes/simple/node/cpus/arm/ParmARM.java | 17 + .../seaofnodes/simple/node/cpus/arm/ProjARM.java | 15 + .../seaofnodes/simple/node/cpus/arm/RetARM.java | 16 + .../seaofnodes/simple/node/cpus/arm/SetARM.java | 34 + .../seaofnodes/simple/node/cpus/arm/SplitARM.java | 66 + .../simple/node/cpus/arm/SplitFlagsARM.java | 21 + .../simple/node/cpus/arm/SplitGPRARM.java | 11 + .../seaofnodes/simple/node/cpus/arm/StoreARM.java | 38 + .../seaofnodes/simple/node/cpus/arm/SubARM.java | 20 + .../seaofnodes/simple/node/cpus/arm/SubFARM.java | 19 + .../seaofnodes/simple/node/cpus/arm/SubIARM.java | 29 + .../seaofnodes/simple/node/cpus/arm/TFPARM.java | 58 + .../seaofnodes/simple/node/cpus/arm/TMPARM.java | 55 + .../seaofnodes/simple/node/cpus/arm/UJmpARM.java | 48 + .../seaofnodes/simple/node/cpus/arm/XorARM.java | 20 + .../seaofnodes/simple/node/cpus/arm/XorIARM.java | 27 + .../com/seaofnodes/simple/node/cpus/arm/arm.java | 467 ++++ .../seaofnodes/simple/node/cpus/riscv/AUIPC.java | 43 + .../simple/node/cpus/riscv/AddFRISC.java | 18 + .../simple/node/cpus/riscv/AddIRISC.java | 22 + .../seaofnodes/simple/node/cpus/riscv/AddRISC.java | 21 + .../simple/node/cpus/riscv/AndIRISC.java | 13 + .../seaofnodes/simple/node/cpus/riscv/AndRISC.java | 16 + .../simple/node/cpus/riscv/BranchRISC.java | 95 + .../simple/node/cpus/riscv/CallRISC.java | 63 + .../simple/node/cpus/riscv/CallRRISC.java | 32 + .../simple/node/cpus/riscv/DivFRISC.java | 20 + .../seaofnodes/simple/node/cpus/riscv/DivRISC.java | 17 + .../simple/node/cpus/riscv/ExtendRISC.java | 32 + .../simple/node/cpus/riscv/F8ToIRISC.java | 19 + .../seaofnodes/simple/node/cpus/riscv/FltRISC.java | 47 + .../seaofnodes/simple/node/cpus/riscv/FunRISC.java | 23 + .../simple/node/cpus/riscv/I2F8RISC.java | 24 + .../seaofnodes/simple/node/cpus/riscv/ImmRISC.java | 55 + .../simple/node/cpus/riscv/Int8RISC.java | 47 + .../seaofnodes/simple/node/cpus/riscv/IntRISC.java | 31 + .../com/seaofnodes/simple/node/cpus/riscv/LUI.java | 33 + .../simple/node/cpus/riscv/LoadRISC.java | 30 + .../simple/node/cpus/riscv/MemOpRISC.java | 80 + .../simple/node/cpus/riscv/MulFRISC.java | 18 + .../seaofnodes/simple/node/cpus/riscv/MulRISC.java | 17 + .../seaofnodes/simple/node/cpus/riscv/NegRISC.java | 21 + .../seaofnodes/simple/node/cpus/riscv/NewRISC.java | 48 + .../seaofnodes/simple/node/cpus/riscv/NotRISC.java | 22 + .../seaofnodes/simple/node/cpus/riscv/OrIRISC.java | 13 + .../seaofnodes/simple/node/cpus/riscv/OrRISC.java | 16 + .../simple/node/cpus/riscv/ParmRISC.java | 17 + .../simple/node/cpus/riscv/ProjRISC.java | 15 + .../seaofnodes/simple/node/cpus/riscv/RetRISC.java | 22 + .../simple/node/cpus/riscv/SetFRISC.java | 34 + .../simple/node/cpus/riscv/SetIRISC.java | 21 + .../seaofnodes/simple/node/cpus/riscv/SetRISC.java | 20 + .../simple/node/cpus/riscv/SllIRISC.java | 13 + .../seaofnodes/simple/node/cpus/riscv/SllRISC.java | 17 + .../simple/node/cpus/riscv/SplitRISC.java | 67 + .../simple/node/cpus/riscv/SraIRISC.java | 15 + .../seaofnodes/simple/node/cpus/riscv/SraRISC.java | 17 + .../simple/node/cpus/riscv/SrlIRISC.java | 13 + .../seaofnodes/simple/node/cpus/riscv/SrlRISC.java | 17 + .../simple/node/cpus/riscv/StoreRISC.java | 50 + .../simple/node/cpus/riscv/SubFRISC.java | 18 + .../seaofnodes/simple/node/cpus/riscv/SubRISC.java | 16 + .../seaofnodes/simple/node/cpus/riscv/TFPRISC.java | 49 + .../seaofnodes/simple/node/cpus/riscv/TMPRISC.java | 42 + .../simple/node/cpus/riscv/UJmpRISC.java | 49 + .../simple/node/cpus/riscv/XorIRISC.java | 14 + .../seaofnodes/simple/node/cpus/riscv/XorRISC.java | 16 + .../seaofnodes/simple/node/cpus/riscv/riscv.java | 429 ++++ .../simple/node/cpus/x86_64_v2/AddFMemX86.java | 33 + .../simple/node/cpus/x86_64_v2/AddFX86.java | 24 + .../simple/node/cpus/x86_64_v2/AddIX86.java | 23 + .../simple/node/cpus/x86_64_v2/AddMemX86.java | 24 + .../simple/node/cpus/x86_64_v2/AddX86.java | 11 + .../simple/node/cpus/x86_64_v2/AndIX86.java | 11 + .../simple/node/cpus/x86_64_v2/AndX86.java | 11 + .../simple/node/cpus/x86_64_v2/CallRX86.java | 30 + .../simple/node/cpus/x86_64_v2/CallX86.java | 49 + .../simple/node/cpus/x86_64_v2/CmpFX86.java | 24 + .../simple/node/cpus/x86_64_v2/CmpIX86.java | 38 + .../simple/node/cpus/x86_64_v2/CmpMemX86.java | 44 + .../simple/node/cpus/x86_64_v2/CmpX86.java | 28 + .../simple/node/cpus/x86_64_v2/DivFX86.java | 24 + .../simple/node/cpus/x86_64_v2/DivX86.java | 25 + .../simple/node/cpus/x86_64_v2/ExtendX86.java | 29 + .../simple/node/cpus/x86_64_v2/F8ToIX86.java | 22 + .../simple/node/cpus/x86_64_v2/FltX86.java | 36 + .../simple/node/cpus/x86_64_v2/FunX86.java | 31 + .../simple/node/cpus/x86_64_v2/I2f8X86.java | 21 + .../simple/node/cpus/x86_64_v2/ImmX86.java | 32 + .../simple/node/cpus/x86_64_v2/IntX86.java | 40 + .../simple/node/cpus/x86_64_v2/JmpX86.java | 65 + .../simple/node/cpus/x86_64_v2/LeaX86.java | 38 + .../simple/node/cpus/x86_64_v2/LoadX86.java | 44 + .../simple/node/cpus/x86_64_v2/MemAddX86.java | 28 + .../simple/node/cpus/x86_64_v2/MemOpX86.java | 89 + .../simple/node/cpus/x86_64_v2/MulFX86.java | 24 + .../simple/node/cpus/x86_64_v2/MulIX86.java | 27 + .../simple/node/cpus/x86_64_v2/MulX86.java | 24 + .../simple/node/cpus/x86_64_v2/NegX86.java | 20 + .../simple/node/cpus/x86_64_v2/NewX86.java | 23 + .../simple/node/cpus/x86_64_v2/NotX86.java | 21 + .../simple/node/cpus/x86_64_v2/OrIX86.java | 11 + .../simple/node/cpus/x86_64_v2/OrX86.java | 11 + .../simple/node/cpus/x86_64_v2/ParmX86.java | 17 + .../simple/node/cpus/x86_64_v2/ProjX86.java | 15 + .../simple/node/cpus/x86_64_v2/RegX86.java | 22 + .../simple/node/cpus/x86_64_v2/RetX86.java | 15 + .../simple/node/cpus/x86_64_v2/SarIX86.java | 32 + .../simple/node/cpus/x86_64_v2/SarX86.java | 21 + .../simple/node/cpus/x86_64_v2/SetX86.java | 44 + .../simple/node/cpus/x86_64_v2/ShlIX86.java | 32 + .../simple/node/cpus/x86_64_v2/ShlX86.java | 21 + .../simple/node/cpus/x86_64_v2/ShrIX86.java | 27 + .../simple/node/cpus/x86_64_v2/ShrX86.java | 21 + .../simple/node/cpus/x86_64_v2/SplitFlagsX86.java | 25 + .../simple/node/cpus/x86_64_v2/SplitX86.java | 52 + .../simple/node/cpus/x86_64_v2/StoreX86.java | 48 + .../simple/node/cpus/x86_64_v2/SubFX86.java | 23 + .../simple/node/cpus/x86_64_v2/SubX86.java | 10 + .../simple/node/cpus/x86_64_v2/TFPX86.java | 53 + .../simple/node/cpus/x86_64_v2/TMPX86.java | 40 + .../simple/node/cpus/x86_64_v2/UJmpX86.java | 41 + .../simple/node/cpus/x86_64_v2/XorIX86.java | 11 + .../simple/node/cpus/x86_64_v2/XorX86.java | 11 + .../simple/node/cpus/x86_64_v2/x86_64_v2.java | 565 +++++ .../com/seaofnodes/simple/print/ASMPrinter.java | 85 + .../com/seaofnodes/simple/print/IRPrinter.java | 61 + .../simple/print/SimpleGraphAdapter.java | 57 + .../simple/print/SimpleGraphObserver.java | 35 + .../src/main/java/com/seaofnodes/simple/sys.java | 25 + .../java/com/seaofnodes/simple/type/Field.java | 140 ++ .../main/java/com/seaofnodes/simple/type/Type.java | 543 +++++ .../com/seaofnodes/simple/type/TypeConAry.java | 79 + .../com/seaofnodes/simple/type/TypeConAryB.java | 20 + .../com/seaofnodes/simple/type/TypeConAryI.java | 16 + .../java/com/seaofnodes/simple/type/TypeFloat.java | 110 + .../com/seaofnodes/simple/type/TypeFunPtr.java | 282 +++ .../com/seaofnodes/simple/type/TypeInteger.java | 183 ++ .../java/com/seaofnodes/simple/type/TypeMem.java | 154 ++ .../com/seaofnodes/simple/type/TypeMemPtr.java | 181 ++ .../java/com/seaofnodes/simple/type/TypeNil.java | 62 + .../java/com/seaofnodes/simple/type/TypePtr.java | 55 + .../java/com/seaofnodes/simple/type/TypeRPC.java | 111 + .../com/seaofnodes/simple/type/TypeScalar.java | 31 + .../com/seaofnodes/simple/type/TypeStruct.java | 514 ++++ .../java/com/seaofnodes/simple/type/TypeTuple.java | 134 ++ .../main/java/com/seaofnodes/simple/type/XInt.java | 241 ++ .../main/java/com/seaofnodes/simple/util/Ary.java | 229 ++ .../java/com/seaofnodes/simple/util/AryInt.java | 192 ++ .../main/java/com/seaofnodes/simple/util/SB.java | 99 + .../java/com/seaofnodes/simple/util/Utils.java | 53 + 282 files changed, 23953 insertions(+) create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/IterPeeps.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/Parser.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/Var.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/codegen/BuildLRG.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/codegen/Coalesce.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/codegen/CodeGen.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/codegen/ElfExpr.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/codegen/ElfFile.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/codegen/Encoding.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/codegen/FinalizeIR.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/codegen/GlobalCodeMotion.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/codegen/IFG.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/codegen/InstSelect.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/codegen/LRG.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/codegen/LinkMem.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/codegen/ListScheduler.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/codegen/Machine.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/codegen/Opto.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/codegen/RIPRelSize.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/codegen/RegAlloc.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/codegen/RegMask.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/codegen/RegMaskRW.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/codegen/TypeCheck.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/AddFNode.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/AddNode.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/AndNode.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/ArithFNode.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/ArithNode.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/BoolNode.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/BulkMemPhiNode.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/CFGNode.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/CProjNode.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/CallEndMach.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/CallEndNode.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/CallNode.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/CalleeSaveNode.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/CastMach.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/CastNode.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/ConFldOffNode.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/ConstantNode.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/ConvertNode.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/CtrlNode.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/DivFNode.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/DivNode.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/EscapeMach.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/EscapeNode.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/ExternNode.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/FRefNode.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/FldOffNode.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/FunNode.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/FunPtrNode.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/GuardMach.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/GuardNode.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/IfNode.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/LoadNode.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/LoopNode.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/MachConcreteNode.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/MachNode.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/MemMergeNode.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/MemOpNode.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/MemPhiNode.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/MinusFNode.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/MinusNode.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/ModeNode.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/MulFNode.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/MulNode.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/MultiNode.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/NeverNode.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/NewNode.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/Node.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/NotNode.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/OrNode.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/ParmNode.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/PhiNode.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/ProjNode.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/PtrToIntMach.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/PtrToIntNode.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/ReadOnlyMach.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/ReadOnlyNode.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/RegionNode.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/ReturnNode.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/RoundF32Node.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/SarNode.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/ScopeNode.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/ShlNode.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/ShrNode.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/SplitNode.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/StartNode.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/StopNode.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/StoreNode.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/StructNode.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/SubFNode.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/SubNode.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/ToFloatNode.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/ToIntegerNode.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/XCtrlNode.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/XorNode.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/arm/AddARM.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/arm/AddFARM.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/arm/AddIARM.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/arm/AndARM.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/arm/AndIARM.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/arm/AsrARM.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/arm/AsrIARM.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/arm/BranchARM.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/arm/CallARM.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/arm/CallRRARM.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/arm/CmpARM.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/arm/CmpFARM.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/arm/CmpIARM.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/arm/DivARM.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/arm/DivFARM.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/arm/ExtendARM.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/arm/F8ToIARM.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/arm/FloatARM.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/arm/FunARM.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/arm/I2F8ARM.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/arm/IntARM.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/arm/LoadARM.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/arm/LslARM.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/arm/LslIARM.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/arm/LsrARM.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/arm/LsrIARM.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/arm/MemOpARM.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/arm/MulARM.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/arm/MulFARM.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/arm/NegARM.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/arm/NewARM.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/arm/NotARM.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/arm/OrARM.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/arm/OrIARM.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/arm/ParmARM.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/arm/ProjARM.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/arm/RetARM.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/arm/SetARM.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/arm/SplitARM.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/arm/SplitFlagsARM.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/arm/SplitGPRARM.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/arm/StoreARM.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/arm/SubARM.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/arm/SubFARM.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/arm/SubIARM.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/arm/TFPARM.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/arm/TMPARM.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/arm/UJmpARM.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/arm/XorARM.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/arm/XorIARM.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/arm/arm.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AUIPC.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AddFRISC.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AddIRISC.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AddRISC.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AndIRISC.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AndRISC.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/riscv/BranchRISC.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CallRISC.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CallRRISC.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/riscv/DivFRISC.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/riscv/DivRISC.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/riscv/ExtendRISC.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/riscv/F8ToIRISC.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/riscv/FltRISC.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/riscv/FunRISC.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/riscv/I2F8RISC.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/riscv/ImmRISC.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/riscv/Int8RISC.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/riscv/IntRISC.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/riscv/LUI.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/riscv/LoadRISC.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/riscv/MemOpRISC.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/riscv/MulFRISC.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/riscv/MulRISC.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/riscv/NegRISC.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/riscv/NewRISC.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/riscv/NotRISC.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/riscv/OrIRISC.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/riscv/OrRISC.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/riscv/ParmRISC.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/riscv/ProjRISC.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/riscv/RetRISC.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SetFRISC.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SetIRISC.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SetRISC.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SllIRISC.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SllRISC.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SplitRISC.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SraIRISC.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SraRISC.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SrlIRISC.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SrlRISC.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/riscv/StoreRISC.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SubFRISC.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SubRISC.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/riscv/TFPRISC.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/riscv/TMPRISC.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/riscv/UJmpRISC.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/riscv/XorIRISC.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/riscv/XorRISC.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/riscv/riscv.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddFMemX86.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddFX86.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddIX86.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddMemX86.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddX86.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AndIX86.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AndX86.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CallRX86.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CallX86.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CmpFX86.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CmpIX86.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CmpMemX86.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CmpX86.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/DivFX86.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/DivX86.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ExtendX86.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/F8ToIX86.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/FltX86.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/FunX86.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/I2f8X86.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ImmX86.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/IntX86.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/JmpX86.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/LeaX86.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/LoadX86.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MemAddX86.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MemOpX86.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MulFX86.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MulIX86.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MulX86.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/NegX86.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/NewX86.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/NotX86.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/OrIX86.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/OrX86.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ParmX86.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ProjX86.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/RegX86.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/RetX86.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SarIX86.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SarX86.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SetX86.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShlIX86.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShlX86.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShrIX86.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShrX86.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SplitFlagsX86.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SplitX86.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/StoreX86.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SubFX86.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SubX86.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/TFPX86.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/TMPX86.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/UJmpX86.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/XorIX86.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/XorX86.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/x86_64_v2.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/print/ASMPrinter.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/print/IRPrinter.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/print/SimpleGraphAdapter.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/print/SimpleGraphObserver.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/sys.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/type/Field.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/type/Type.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/type/TypeConAry.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/type/TypeConAryB.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/type/TypeConAryI.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/type/TypeFloat.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/type/TypeFunPtr.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/type/TypeInteger.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/type/TypeMem.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/type/TypeMemPtr.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/type/TypeNil.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/type/TypePtr.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/type/TypeRPC.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/type/TypeScalar.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/type/TypeStruct.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/type/TypeTuple.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/type/XInt.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/util/Ary.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/util/AryInt.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/util/SB.java create mode 100644 chapter-construction/src/main/java/com/seaofnodes/simple/util/Utils.java (limited to 'chapter-construction/src/main/java/com') diff --git a/chapter-construction/src/main/java/com/seaofnodes/simple/IterPeeps.java b/chapter-construction/src/main/java/com/seaofnodes/simple/IterPeeps.java new file mode 100644 index 00000000..2fe4e172 --- /dev/null +++ b/chapter-construction/src/main/java/com/seaofnodes/simple/IterPeeps.java @@ -0,0 +1,230 @@ +package com.seaofnodes.simple; + +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.type.Type; +import com.seaofnodes.simple.type.TypeInteger; +import com.seaofnodes.simple.util.Ary; +import com.seaofnodes.simple.node.*; +import java.util.Arrays; +import java.util.BitSet; +import java.util.Random; + +/** + * The IterPeeps runs after parsing. It iterates the peepholes to a fixed point + * so that no more peepholes apply. This should be linear because peepholes rarely + * (never?) increase code size. The graph should monotonically reduce in some + * dimension, which is usually size. It might also reduce in e.g. number of + * MulNodes or Load/Store nodes, swapping out more "expensive" Nodes for cheaper + * ones. + *
+ * The theoretical overall worklist is mindless just grabbing the next thing and + * doing it. If the graph changes, put the neighbors on the worklist. + *
+ * Lather, Rinse, Repeat until the worklist runs dry. + *

+ * The main issues we have to deal with: + * + *

+ */ +public class IterPeeps { + + public final WorkList _work; + + private final WorkList _workInline; + + public IterPeeps( long seed ) { + _work = new WorkList<>(seed); + _workInline = new WorkList<>(seed); + } + + @SuppressWarnings("unchecked") + public N add( N n ) { return (N)_work.push(n); } + + public void addAll( Ary ary ) { _work.addAll(ary); } + + /** + * Iterate peepholes to a fixed point + */ + public void iterate( CodeGen code ) { + while( true ) { + iteratePeeps(code); + CallEndNode cend; + while( (cend=_workInline.pop())!=null ) + if( cend.inlineSmall() ) break; + if( cend==null ) return; + } + } + + // Only try growing inlines once the shrinking peepholes reach a fixed point. + private void iteratePeeps( CodeGen code ) { + assert progressOnList(code, _work, true); + int cnt=0; + + Node n; + while( (n=_work.pop()) != null ) { + if( n.isDead() ) continue; + if( n instanceof CallEndNode cend ) _workInline.push(cend); + cnt++; // Useful for debugging, searching which peephole broke things + var obs = CodeGen.CODE._obs; + if( obs != null ) obs.before(n); + Node x = n.peepholeOpt(); + if( x != null ) { + if( x.isDead() ) { + if( obs != null ) obs.after(n, x, true); + continue; + } + // peepholeOpt can return brand-new nodes, needing an initial type set + if( x._type==null ) x.setType(x.compute()); + // Changes require neighbors onto the worklist + if( x != n || !(x instanceof ConstantNode) ) { + // All outputs of n (changing node) not x (prior existing node). + for( Node z : n._outputs ) _work.push(z); + // Everybody gets a free "go again" in case they didn't get + // made in their final form. + _work.push(x); + // If the result is not self, revisit all inputs (because + // there's a new user), and replace in the graph. + if( x != n ) { + for( Node z : n. _inputs ) _work.push(z); + for( Node z : x._outputs ) _work.push(z); + n.subsume(x); + } + } + // If there are distant neighbors, move to worklist + n.moveDepsToWorklist(); + assert progressOnList(code, _work, true); // Very expensive assert + } + if( n.isUnused() && !(n instanceof StopNode) ) + n.kill(); // Just plain dead + if( obs != null ) obs.after(n, x, true); + } + + } + + // Visit ALL nodes and confirm the invariant: + // Either you are on the _work worklist OR running `iter()` makes no progress. + + // This invariant ensures that no progress is missed, i.e., when the + // worklist is empty we have indeed done all that can be done. To help + // with debugging, the {@code assert} is broken out in a place where it is easy to + // stop if a change is found. + + // Also, the normal usage of `iter()` may attempt peepholes with distance + // neighbors and these should fail, but will then try to add dependencies + // {@link #Node.addDep} which is a side effect in an assert. The {@link + // #midAssert} is used to stop this side effect. + // Pessimistic solver assert + public static boolean progressOnList(CodeGen code, WorkList list, boolean dir ) { + code._midAssert = true; + Node changed = code._stop.walk( n -> { + Node m = n; + Type nval = n.compute(); + if( (!n.iskeep() || n._nid<=8) && // Types must be forwards, even if on worklist + ( dir + ? nval.isa(n._type) // Pesi: new value lifts over old + : n._type.isa(nval) // Opto: new value falls over old + ) ) { + if( list.on(n) ) return null; + m = n.peepholeOpt(); + if( m==null ) return null; + } + System.err.println("BREAK HERE FOR BUG"); + return m; + }); + code._midAssert = false; + return changed==null; + } + + /** + * Classic WorkList, with a fast add/remove, dup removal, random pull. + * The Node's nid is used to check membership in the worklist. + */ + @SuppressWarnings("unchecked") + public static class WorkList { + + private Node[] _es; + private int _len; + private final BitSet _on; // Bit set if Node._nid is on WorkList + private final Random _R; // For randomizing pull from the WorkList + private final long _seed; + + /* Useful stat - how many nodes are processed in the post parse iterative opt */ + private long _totalWork = 0; + + public WorkList() { this(123); } + WorkList(long seed) { + _es = new Node[1]; + _len=0; + _on = new BitSet(); + _seed = seed; + _R = new Random(); + _R.setSeed(_seed); + } + + /** + * Pushes a Node on the WorkList, ensuring no duplicates + * If Node is null it will not be added. + */ + public E push( E x ) { + if( x==null ) return null; + int idx = x._nid; + if( !_on.get(idx) ) { + _on.set(idx); + if( _len==_es.length ) + _es = Arrays.copyOf(_es,_len<<1); + _es[_len++] = x; + _totalWork++; + } + return x; + } + + public void addAll( Ary ary ) { + for( E n : ary ) + push(n); + } + + /** + * True if Node is on the WorkList + */ + public boolean on( E x ) { return _on.get(x._nid); } + public boolean isEmpty() { return _len==0; } + + /** + * Removes a random Node from the WorkList; null if WorkList is empty + */ + public E pop() { + if( _len == 0 ) return null; + int idx = _R.nextInt(_len); + E x = (E)_es[idx]; + _es[idx] = _es[--_len]; // Compress array + _on.clear(x._nid); + return x; + } + + public void clear() { + _len = 0; + _on.clear(); + _R.setSeed(_seed); + _totalWork = 0; + } + } +} diff --git a/chapter-construction/src/main/java/com/seaofnodes/simple/Parser.java b/chapter-construction/src/main/java/com/seaofnodes/simple/Parser.java new file mode 100644 index 00000000..5ce7c410 --- /dev/null +++ b/chapter-construction/src/main/java/com/seaofnodes/simple/Parser.java @@ -0,0 +1,2540 @@ +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.*; + +/** + * The Parser converts a Simple source program to the Sea of Nodes intermediate + * representation directly in one pass. There is no intermediate Abstract + * Syntax Tree structure. + *

+ * This is a simple recursive descent parser. All lexical analysis is done here as well. + */ +public class Parser { + + public static ConstantNode ZERO; // Very common node, cached here + public static ConstantNode NIL; // Very common node, cached here + public static XCtrlNode XCTRL; // Very common node, cached here + + // Compile driver + public final CodeGen _code; + + // The Lexer. Thin wrapper over a byte[] buffer with a cursor. + private Lexer _lexer; + + /** + * Current ScopeNode - ScopeNodes change as we parse code, but at any point of time + * there is one current ScopeNode. The reason the current ScopeNode can change is to do with how + * we handle branching. See {@link #parseIf()}. + *

+ * Each ScopeNode contains a stack of lexical scopes, each scope is a symbol table that binds + * variable names to Nodes. The top of this stack represents current scope. + *

+ * We keep a list of all ScopeNodes so that we can show them in graphs. + * @see #parseIf() + * @see #_xScopes + */ + public ScopeNode _scope; + + /** + * List of keywords disallowed as identifiers + */ + public static final HashSet KEYWORDS = new HashSet<>(){{ + add("bool"); + add("break"); + add("byte"); + add("continue"); + add("else"); + add("f32"); + add("f64"); + add("false"); + add("flt"); + add("i16"); + add("i32"); + add("i64"); + add("i8"); + add("if"); + add("int"); + add("new"); + add("null"); + add("return"); + add("struct"); + add("true"); + add("u1"); + add("u16"); + add("u32"); + add("u8"); + add("while"); + }}; + + /** + * We clone ScopeNodes when control flows branch; it is useful to have + * a list of all active ScopeNodes for purposes of visualization of the SoN graph + */ + public final Stack _xScopes = new Stack<>(); + + ScopeNode _continueScope; + ScopeNode _breakScope; // Merge all the while-breaks here + FunNode _fun; // Current function being parsed + + // Mapping from a type name to a Type. The string name matches + // `type.str()` call. + public static HashMap TYPES = defaultTypes(); + + // Mapping from a type name to the constructor for a Type. + public final HashMap INITS; + + + public Parser(CodeGen code, TypeInteger arg) { + _code = code; + _scope = new ScopeNode(); + _continueScope = _breakScope = null; + ZERO = con(TypeInteger.ZERO).keep(); + NIL = con(Type.NIL).keep(); + XCTRL= new XCtrlNode().peephole().keep(); + TYPES = defaultTypes(); + INITS = new HashMap<>(); + } + + @Override + public String toString() { return _lexer.toString(); } + + public static HashMap defaultTypes() { + return new HashMap<>() {{ + put("bool",TypeInteger.U1 ); + put("byte",TypeInteger.U8 ); + put("f32" ,TypeFloat .F32); + put("f64" ,TypeFloat .F64); + put("flt" ,TypeFloat .F64); + put("i16" ,TypeInteger.I16); + put("i32" ,TypeInteger.I32); + put("i64" ,TypeInteger.BOT); + put("i8" ,TypeInteger.I8 ); + put("int" ,TypeInteger.BOT); + put("u1" ,TypeInteger.U1 ); + put("u16" ,TypeInteger.U16); + put("u32" ,TypeInteger.U32); + put("u8" ,TypeInteger.U8 ); + put("val" ,Type.TOP); // Marker type, indicates type inference + put("var" ,Type.BOTTOM); // Marker type, indicates type inference + }}; + } + + // Debugging utility to find a Node by index + public Node f(int nid) { return _code.f(nid); } + + private Node ctrl() { return _scope.ctrl(); } + private N ctrl(N n) { return _scope.ctrl(n); } + + public void parse() { + // Graph callbacks during scope setup need a source position. + _lexer = new Lexer(sys.SYS); + + _scope.define(ScopeNode.CTRL, Type.CONTROL , false, null, _lexer); + _scope.define(ScopeNode.MEM0, TypeMem.BOT , false, null, _lexer); + _scope.define(ScopeNode.ARG0, TypeInteger.BOT, false, null, _lexer); + + ctrl(XCTRL); + _scope.mem(con(TypeMem.BOT)); + + // Parse the sys import + while( !_lexer.isEOF() ) { + parseStatement(); + _lexer.skipWhiteSpace(); + } + + // Reset lexer for program text + _lexer = new Lexer(_code._src); + _xScopes.push(_scope); + + // Parse whole program, as-if function header "{ int arg -> body }" + 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 + // default main). + FunNode main = _code.link(_code._main); + StopNode stop = _code._stop; + // Gather an explicit main + FunNode xmain = null; + for( Node n : stop._inputs ) + if( n instanceof ReturnNode ret && "main".equals(ret.fun()._name) ) + if( xmain != null ) throw Utils.TODO(); + else xmain = ret.fun(); + + if( main.ret().expr()._type==Type.TOP && main.uctrl()==null ) { + // Kill an empty default main; so it does not attempt to put a + // "main" in any final ELF file + main.setDef(1,XCTRL); // Delete default start input + stop.delDef(stop._inputs.find(main.ret())); + if( xmain != null ) + _code.setMain(xmain); + + } else { + // We have a non-empty default main. + if( xmain != null ) + // Found an explicit "main" AND we have a default "main" + throw error("Cannot define both an explicit main and a default main"); + main._name = "main"; + } + + if( !_lexer.isEOF() ) throw _errorSyntax("unexpected"); + + // Clean up and reset + _xScopes.pop(); + _scope.kill(); + for( StructNode init : INITS.values() ) + init.unkeep().kill(); + INITS.clear(); + for( Ary captures : CAPTURES.values() ) + for( Node value : captures ) value.unkill(); + CAPTURES.clear(); + stop.peephole(); + // Helpers are built with init(), so their complete call graph exists + // before any inlining. Seed those nodes now along with the parsed body. + _code._start.walk(n -> { _code.add(n); return null; }); + } + + /** + * Parses a function body, assuming the header is parsed. + */ + private ReturnNode parseFunctionBody( TypeFunPtr sig, Lexer loc, String... ids) { + return parseFunctionBody(sig, loc, null, ids); + } + + private FunNode _privateFun; + private HashMap _fieldVars; + private record Constructor(TypeFunPtr sig, Lexer loc, int body, String[] ids, java.util.BitSet finals) {} + private Constructor _constructor; + private final HashMap ALLOCS = new HashMap<>(); + private final HashMap INITIALIZERS = new HashMap<>(); + private final HashMap> CAPTURES = new HashMap<>(); + private Ary _captures; + + private ReturnNode parseFunctionBody(TypeFunPtr sig, Lexer loc, java.util.BitSet finals, String... ids) { + return parseFunctionBody(sig,loc,finals,false,ids); + } + + private ReturnNode parseFunctionBody(TypeFunPtr sig, Lexer loc, java.util.BitSet finals, boolean initializer, 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 + FunNode oldfun = _fun; + FunNode oldPrivate = _privateFun; + HashMap oldFields = _fieldVars; + Constructor oldConstructor = _constructor; + _fieldVars = new HashMap<>(); + _constructor = null; + Ary oldCaptures = _captures; + _captures = initializer ? new Ary<>(Node.class) : null; + ScopeNode breakScope = _breakScope; _breakScope = null; + ScopeNode continueScope = _continueScope; _continueScope = null; + + FunNode fun = _fun = (FunNode)peep(new FunNode(loc(),sig,_nestedType,null,_code._start)); + // Once the function header is available, install in linker table - + // allowing recursive functions. Linker matches on declared args and + // exact fidx, and ignores the return (because the fidx will only match + // the exact single function). + _code.link(fun); + _privateFun = sig.ret() instanceof TypeMem ? fun : null; + fun._constructor = _privateFun!=null; + + 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( null,null,null).init(); + assert r.inProgress(); + PhiNode rmem = new BulkMemPhiNode(ScopeNode.MEM0,r,null).init(); + PhiNode rrez = (sig.ret() instanceof TypeMem + ? new BulkMemPhiNode("#selfMem",r,null) : new PhiNode(ScopeNode.ARG0,r,null)).init(); + ReturnNode ret = new ReturnNode(r, rmem, rrez, rpc, fun).init(); + fun.setRet(ret); + 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(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()); + // All args, "as-if" called externally + for( int i=0; i=0; j-- ) + if( _scope.var(j)._name.equals(f._fname) ) { v=_scope.var(j); break; } + assert v!=null; + _fieldVars.put(f._fname,v); + if( !f._one ) { + Node value=missingInit(defaults.in(i),new java.util.BitSet()) ? con(Type.BOTTOM) + : new LoadNode(loc(),f._fname,f._alias,privateMem(),self(),off(shape,f._fname),true).peephole(); + _scope.update(v,value); + } + } + } + Node last=ZERO; + while (!peek('}') && !_lexer.isEOF()) { + last = parseStatement(); + if( initializer ) fields = initFields(fields); + } + if( initializer ) { + finishFields(); + CAPTURES.put(_nestedType,_captures); + if( _constructor!=null ) { + Constructor ctor=_constructor; + int end=pos(ctor.body()); + ReturnNode body=parseFunctionBody(ctor.sig(),ctor.loc(),ctor.finals(),ctor.ids()); + body.fun().setName(shortName(_nestedType)); + require("}"); + pos(end); + } + } + if( _privateFun==fun ) last = storeFields(initializer); + + // Last expression is the return except for the top-level main + if( ctrl()._type==Type.CONTROL ) + if( fun.sig() != _code._main ) + fun.addReturn(ctrl(), _scope.mem(), last); + else { + fun.setDef(1,XCTRL); // Kill default main + _code.addAll(fun._outputs); + } + + // Pop off the inProgress node on the multi-exit Region merge + assert r.inProgress(); + r ._inputs.pop(); + rmem._inputs.pop(); + rrez._inputs.pop(); + r._loc = loc(); // Final position + assert !r.inProgress(); + + // Force peeps, which have been avoided due to inProgress + ret.setDef(1,rmem.peephole()); + ret.setDef(2,rrez.peephole()); + ret.setDef(0,r.peephole()); + ret = (ReturnNode)ret.peephole(); + + // Function scope ends + if( initializer ) _scope.pop(); + _scope.pop(); + _fun = oldfun; + _privateFun = oldPrivate; + _fieldVars = oldFields; + _constructor = oldConstructor; + _captures = oldCaptures; + _breakScope = breakScope; + _continueScope = continueScope; + + // Reset all fields to pre-parse days + for( int i=0; i + * '{' statements '}' + * + * Does not parse the opening or closing '{}' + * @return a {@link Node} or {@code null} + */ + private Node parseBlock(Kind kind) { + // Enter a new scope + _scope.push(kind); + Node last = ZERO; + while (!peek('}') && !_lexer.isEOF()) + last = parseStatement(); + // Exit scope + last.keep(); + _scope.pop(); + return last.unkeep(); + } + + /** + * Parses a statement + * + *

+     *     returnStatement | declStatement | blockStatement | ifStatement | expressionStatement
+     * 
+ * @return a {@link Node} or {@code null} + */ + private Node parseStatement() { + if( matchx("return") ) return parseReturn(); + else if (matchx("if") ) return parseIf(); + else if (matchx("while") ) return parseWhile(); + else if (matchx("for") ) return parseFor(); + else if (matchx("break") ) return parseBreak(); + else if (matchx("continue")) return parseContinue(); + else if (matchx("struct") ) return parseStruct(); + else if (_scope.inConstructor() && matchx("new")) return parseConstructorDeclaration(); + else if (matchx(";") ) return null; // Empty statement + // Break ambiguity around leading function types and starting a block + else if (peek('{') && !isTypeFun() ) { + match("{"); + return require(parseBlock(new Kind.Block()),"}"); + } + // Declaration or normal assignment/expression + else return parseDeclarationStatement(); + } + + // In a struct declaration, new names the constructor. Its body still + // parses as a function; in expressions, new allocates and calls it. + private Node parseConstructorDeclaration() { + Lexer loc=loc(); + String name=requireId(); + String expect=shortName(_nestedType); + if( !name.equals(expect) ) + throw error("Constructor name '"+name+"' must match type name '"+expect+"'"); + require("="); + Node ctor=constructorFunc(); + TypeFunPtr sig=(TypeFunPtr)ctor._type; + Node lift=liftExpr(ctor,sig,true); + if( !_scope.define(name,sig,true,lift,loc) ) + throw error("Redefining constructor '"+name+"'"); + _scope.lookup(name)._init=true; + return require(lift,";"); + } + + /** + * Parses a while statement + * + *
+     *     while ( expression ) statement
+     * 
+ * @return a {@link Node}, never {@code null} + */ + private Node parseWhile() { + require("("); + return parseLooping(false); + } + + + /** + * Parses a for statement + * + *
+     *     for( var x=init; test; incr ) body
+     * 
+ * @return a {@link Node}, never {@code null} + */ + private Node parseFor() { + // { var x=init,y=init,...; + // while( test ) { + // body; + // next; + // } + // } + require("("); + _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 + return rez; + } + + // Shared by `for` and `while` + private Node parseLooping( boolean doFor ) { + + var savedContinueScope = _continueScope; + var savedBreakScope = _breakScope; + + // Loop region has two control inputs, the first is the entry + // point, and second is back edge that is set after loop is parsed + // (see end_loop() call below). Note that the absence of back edge is + // used as an indicator to switch off peepholes of the region and + // associated phis; see {@code inProgress()}. + + ctrl(new LoopNode(loc(),ctrl()).peephole()); // Note we set back edge to null here + + // At loop head, we clone the current Scope (this includes all + // names in every nesting level within the Scope). + // We create phis eagerly for all the names we find, see dup(). + + // Save the current scope as the loop head + ScopeNode head = _scope.keep(); + // Clone the head Scope to create a new Scope for the body. + // Create phis eagerly as part of cloning + _xScopes.push(_scope = _scope.dup(true)); // The true argument triggers creating phis + + // Parse predicate + var pred = peek(';') ? con(1) : parseAsgn(); + require( doFor ? ";" : ")" ); + + // IfNode takes current control and predicate + Node ifNode = new IfNode(ctrl(), pred.keep()).peephole(); + // Setup projection nodes + Node ifT = new CProjNode(ifNode. keep(), 0, "True" ).peephole().keep(); + Node ifF = new CProjNode(ifNode.unkeep(), 1, "False").peephole(); + + // for( ;;next ) body + int nextPos = -1, nextEnd = -1; + if( doFor ) { + // Skip the next expression and parse it later + nextPos = pos(); + skipAsgn(); + nextEnd = pos(); + require(")"); + } + + // Clone the body Scope to create the break/exit Scope which accounts for any + // side effects in the predicate. The break/exit Scope will be the final + // scope after the loop, and its control input is the False branch of + // the loop predicate. Note that body Scope is still our current scope. + ctrl(ifF); + _xScopes.push(_breakScope = _scope.dup()); + _breakScope.addGuards(ifF,pred,true); // Up-cast predicate + + // No continues yet + _continueScope = null; + + // Parse the true side, which corresponds to loop body + // Our current scope is the body Scope + ctrl(ifT.unkeep()); // set ctrl token to ifTrue projection + _scope.addGuards(ifT,pred.unkeep(),false); // Up-cast predicate + parseStatement().isKill(); // Parse loop body + _scope.removeGuards(ifT); + + // Merge the loop bottom into other continue statements + if (_continueScope != null) { + _continueScope = jumpTo(_continueScope); + _scope.kill(); + _scope = _continueScope; + } + + // Now append the next code onto the body code + if( doFor ) { + int old = pos(nextPos); + if( !peek(')') ) + parseAsgn(); + if( pos() != nextEnd ) + throw errorSyntax( "Unexpected code after expression" ); + pos(old); + } + + // The true branch loops back, so whatever is current _scope.ctrl gets + // added to head loop as input. endLoop() updates the head scope, and + // goes through all the phis that were created earlier. For each phi, + // it sets the second input to the corresponding input from the back + // edge. If the phi is redundant, it is replaced by its sole input. + var exit = _breakScope; + head.endLoop(_scope, exit); + head.unkeep().kill(); + + _xScopes.pop(); // Cleanup + _xScopes.pop(); // Cleanup + + _continueScope = savedContinueScope; + _breakScope = savedBreakScope; + + // At exit the false control is the current control, and + // the scope is the exit scope after the exit test. + // During sys parsing, there is no xscope here. + if( !_xScopes.isEmpty() ) { + _xScopes.pop(); + _xScopes.push( exit ); + } + _scope = exit; + return ZERO; + } + + private ScopeNode jumpTo(ScopeNode toScope) { + ScopeNode cur = _scope.dup(); + 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.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 + // "continue" + if( toScope == null ) + return cur; + // toScope is either the break scope, or a scope that was created here + assert toScope.depth() <= _breakScope.depth(); + toScope.ctrl(toScope.mergeScopes(cur,loc()).peephole()); + return toScope; + } + + private void checkLoopActive() { if (_breakScope == null) throw error("No active loop for a break or continue"); } + + private Node parseContinue() { checkLoopActive(); _continueScope = require(jumpTo( _continueScope ),";"); return ZERO; } + private Node parseBreak () { + checkLoopActive(); + // At the time of the break, and loop-exit conditions are only valid if + // they are ALSO valid at the break. It is the intersection of + // conditions here, not the union. + _breakScope.removeGuards(_breakScope.ctrl()); + _breakScope = require(jumpTo(_breakScope ),";"); + _breakScope.addGuards(_breakScope.ctrl(), null, false); + return ZERO; + } + + // Look for an unbalanced `)`, skipping balanced + private void skipAsgn() { + int paren=0; + while( true ) + // Next X char handles skipping complex comments + switch( _lexer.nextXChar() ) { + case Character.MAX_VALUE: + throw Utils.TODO(); + case ')': + if( --paren<0 ) { + posT( pos() - 1 ); + return; // Leave the `)` behind + } + break; + case '(': paren ++; break; + default: break; + } + } + + + /** + * Parses a statement + * + *
+     *     if ( expression ) statement [else statement]
+     * 
+ * @return a {@link Node}, never {@code null} + */ + private Node parseIf() { + // Parse predicate + require("("); + var pred = require(parseAsgn(), ")"); + return parseTrinary(pred,"else"); + } + + // Parse a conditional expression, merging results. + private Node parseTrinary( Node pred, String fside ) { + // IfNode takes current control and predicate + pred.keep(); + Node ifNode = new IfNode(ctrl(), pred).peephole(); + // Setup projection nodes + Node ifT = new CProjNode(ifNode. keep(), 0, "True" ).peephole().keep(); + Node ifF = new CProjNode(ifNode.unkeep(), 1, "False").peephole().keep(); + // In if true branch, the ifT proj node becomes the ctrl + // But first clone the scope and set it as current + ScopeNode fScope = _scope.dup(); // Duplicate current scope + fScope.ctrl(ifF); // The pending arm already starts at False + _xScopes.push(fScope); // For graph visualization we need all scopes + + // Parse the true side + ctrl(ifT.unkeep()); // set ctrl token to ifTrue projection + _scope.addGuards(ifT,pred,false); // Up-cast predicate + // 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. + // See if any forward-refs were made, and copy them to the other side: + // "pred ? n*fact(n-1) : 1" + fScope.balanceIf(_scope); + + ScopeNode tScope = _scope; + + // Parse the false side + _scope = fScope; // Restore scope, then parse else block if any + ifF.unkeep(); // fScope already owns the false control + // 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); + // 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 || fside.equals("||") ) + fScope = _scope; + pred.unkeep(); + + // Check the trinary widening int/flt + if( !fside.equals("else") ) { + rhs = widenInt( rhs.unkeep(), lhs._type ).keep(); + lhs = widenInt( lhs.unkeep(), rhs._type ).keep(); + } + + _scope = tScope; + _xScopes.pop(); // Discard pushed from graph display + // See if a one-sided def was made: "if(pred) int x = 1;" and throw. + // See if any forward-refs were made, and copy them to the other side: + // "pred ? n*fact(n-1) : 1" + tScope.balanceIf(fScope); + + // Merge results + RegionNode r = ctrl(tScope.mergeScopes(fScope,loc())); + Node ret = peep(new PhiNode("",r,lhs.unkeep(),rhs.unkeep())); + // Immediately fail e.g. `arg ? 7 : ptr` + ParseException err; + if( !fside.equals("else") && (err=ret.err()) !=null ) throw err; + r.peephole(); + return ret; + } + + /** + * Parses a return statement; "return" already parsed. + * The $ctrl edge is killed. + * + *
+     *     'return' expr ;
+     * 
+ * @return an expression {@link Node}, never {@code null} + */ + private Node parseReturn() { + var expr = require(parseAsgn(), ";"); + // Force the lazy whole-memory Phi before closing this exit. + if( _privateFun==_fun ) + throw error("A constructor cannot return a value"); + _fun.addReturn(ctrl(), _scope.mem(), expr); + ctrl(XCTRL); // Kill control + return expr; + } + + + + /** Parse: [name '='] expr + */ + 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(); } + + // 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` + Var selfVar = _scope.lookup("self"); + Node self = liftExpr(_scope.in(selfVar),selfVar.type(),true); + 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 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 + checkCaptureWrite(def); + Node expr = parseAsgn(); + + // Lift expression, based on type + Node lift = liftExpr(expr.keep(), def.type(), true); + // Update + _scope.update(name,lift); + // Return un-lifted expr + return expr.unkeep(); + } + + // 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( t==Type.BOTTOM || t==Type.TOP ) return expr; + // Constructor sentinels record missing initialization, not a value + // needing conversion. Keep them visible to the completion check. + if( expr instanceof ConstantNode && !(expr instanceof FRefNode) && + (expr._type==Type.TOP || expr._type==Type.BOTTOM) ) return expr; + if( t instanceof TypeMemPtr tmp && tmp.isFinal() ) + expr = peep(new ReadOnlyNode(expr)); + if( expr instanceof FunPtrNode && t instanceof TypeFunPtr ) return expr; + return peep(new ConvertNode(t,expr)); + } + + private Node widenInt( Node expr, Type t ) { + return (expr._type instanceof TypeInteger || expr._type==Type.NIL) && t instanceof TypeFloat + ? peep(new ToFloatNode(expr)) : expr; + } + + /** + * Parse declaration or expression statement + * declStmt = type var['=' exprAsgn][, var['=' exprAsgn]]* ';' | exprAsgn ';' + *

+ * exprAsgn = var '=' exprAsgn | expr + */ + private Node parseDeclarationStatement() { + int old = pos(); + Type t = type(); + if( peek('.') ) // Ambiguity static vars: "type.var", parse as expression + { pos(old); t=null; } + if( t == null ) + return require(parseAsgn(),";"); + + // now parse var['=' asgnexpr] in a loop + Node n = parseDeclaration(t); + while( match(",") ) + n = parseDeclaration(t); + return require(n,";"); + } + + // Widen a reassignable binding without discarding the initializer's + // 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(); + } + return t.glb(false); + } + + // The binding modifier sits immediately before the name. It never + // changes permissions on the referenced object. + private boolean bindingFinal(Type t) { + if( match("!") ) return false; + if( match("~") ) return true; + return t==Type.TOP || t instanceof TypeNil; + } + + /** Parse final: [!|~]var['=' asgn] + */ + private Node parseDeclaration(Type t) { + assert t!=null; + // Has var/val instead of a user-declared type + boolean inferType = t==Type.TOP || t==Type.BOTTOM; + boolean xfinal = bindingFinal(t); + 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( initialized ) { + expr = isExternDecl() ? externDecl(name,t) + : _scope.inConstructor() && name.equals(shortName(_nestedType)) + ? constructorFunc() : parseAsgn(); + // var/val, then type comes from expression + if( inferType ) { + if( expr._type==Type.NIL ) + throw error("a not-null/non-zero expression"); + t = expr._type; + if( !xfinal ) t = inferredType(t); // Widen if not final + } + // expr is a constant function + if( t instanceof TypeFunPtr && expr._type instanceof TypeFunPtr tfp && tfp.isConstant() ) { + if( expr instanceof ExternNode ) t = expr._type; // Upgrade declared type to exact function + else if( _code._link(tfp)!=null ) _code.link(tfp).setName(name); // Assign debug name to Simple function + } + + } else { + // Need an expression to infer the type. + // Also, if not-null then need an initializing expression. + // Allowed in a class def, because type/init will happen in the constructor. + if( (inferType || (t instanceof TypeNil tn && !tn.nullable() )) && !_scope.inConstructor() ) + throw errorSyntax("=expression"); + // Initial value for uninitialized struct fields. + expr = switch( t ) { + // Nullable pointers get a NIL; not-null get a TOP which + // signals that they *must* be initialized in the constructor. + case TypeNil tn -> tn.nullable() ? NIL : con(Type.TOP); + case TypeInteger ti -> xfinal ? con(Type.TOP) : ZERO; + case TypeFloat tf -> con(xfinal ? Type.TOP : TypeFloat.FZERO); + // Bottom signals type inference: they must be initialized in + // 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 + Node lift = liftExpr(expr, t, true); + + // Lift type to the declaration. This will report as an error later if + // we cannot lift the type. + // Define a new name + if( !_scope.define(name,t,xfinal || fld_final,lift, loc) ) + throw error("Redefining name '" + name + "'", loc); + _scope.lookup(name)._init = initialized; + return lift; + } + + + + /** + * Parse a struct declaration, and return the following statement. + * Structs cannot be redefined, but can be nested. + * + * @return zero + */ + private String _nestedType; + private static String shortName(String name) { return name.substring(name.lastIndexOf('.')+1); } + + private Node parseStruct() { + String old = _nestedType; + String name = requireId(); + if( old!=null ) name=old+"."+name; + _nestedType=name; + TypeMemPtr ptr = TYPES.get(name) instanceof TypeMemPtr p ? p : TypeMemPtr.make(TypeStruct.open(name)); + TYPES.put(name,ptr); + require("{"); + TypeFunPtr sig = _code.makeFun(TypeFunPtr.make(false,false,new Type[]{ptr,TypeMem.BOT},TypeMem.BOT)); + ReturnNode init = parseFunctionBody(sig,loc(),null,true,"self","#selfMem"); + init.fun()._name=name+"."; + INITIALIZERS.put(name,init); + TypeStruct shape = ((TypeMemPtr)TYPES.get(name))._obj; + ALLOCS.put(name,makeAllocator(shape,init)); + _nestedType=old; + require("}"); + require(";"); + return ZERO; + } + + private int initFields(int index) { + TypeStruct shape = ((TypeMemPtr)TYPES.get(_nestedType))._obj; + while( index<_scope.nIns() ) { + Var v = _scope.var(index++); + if( v.isFRef() ) continue; + Node value = _scope.in(v); + boolean constant = v._final && v._init && value._type.isConstant(); + Field field = Field.make(v._name,v.type(),_code.getALIAS(),v._final,constant); + shape=shape.add(field); + TYPES.put(_nestedType,TypeMemPtr.make(shape)); + _fieldVars.put(v._name,v); + } + return index; + } + + private void finishFields() { + TypeStruct shape = ((TypeMemPtr)TYPES.get(_nestedType))._obj.close(); + TYPES.put(_nestedType,TypeMemPtr.make(shape)); + StructNode constants = new StructNode(shape); + int first = _scope._kinds.last()._lexSize; + for( int i=first; i<_scope.nIns(); i++ ) + if( !_scope.var(i).isFRef() ) constants.addDef(_scope.in(i)); + constants.setType(constants.compute()); + INITS.put(_nestedType,constants.keep()); + } + + private Node self() { return _scope.in(_scope.lookup("self")); } + private Node privateMem() { return _scope.in(_scope.lookup("#selfMem")); } + + private boolean privateSelf(Node ptr) { + if( _privateFun!=_fun || _privateFun==null ) return false; + while( ptr instanceof ConvertNode || ptr instanceof ReadOnlyNode || ptr instanceof GuardNode ) ptr=ptr.in(1); + Node self=self(); + while( self instanceof ConvertNode || self instanceof ReadOnlyNode || self instanceof GuardNode ) self=self.in(1); + return ptr==self; + } + private boolean mayBeSelf(Node ptr, java.util.BitSet seen) { + if( _privateFun==null || _privateFun!=_fun || seen.get(ptr._nid) ) return false; + seen.set(ptr._nid); + if( privateSelf(ptr) ) return true; + if( ptr instanceof ConvertNode || ptr instanceof ReadOnlyNode || ptr instanceof GuardNode ) + return mayBeSelf(ptr.in(1),seen); + if( ptr instanceof PhiNode && !(ptr instanceof ParmNode) ) + for( int i=1; i types = new Ary<>(Type.class); + Ary names = new Ary<>(String.class); + types.add(TYPES.get(_nestedType)); names.add("self"); + types.add(TypeMem.BOT); names.add("#selfMem"); + java.util.BitSet finals = new java.util.BitSet(); + finals.set(0); // Rebinding self would detach the pointer from its private memory. + while( true ) { + Type t=type(); + if( t==null ) break; + finals.set(names.size(),bindingFinal(t)); + types.add(t); names.add(requireId()); + match(","); + } + require("->"); + TypeFunPtr sig = _code.makeFun(TypeFunPtr.make(false,false,types.asAry(),TypeMem.BOT)); + // Bind the signature now, but parse the body after the field list is + // closed. Then even a later field has a local SSA slot before branches + // and loops are parsed. Strings/comments must not affect brace depth. + _constructor=new Constructor(sig,loc(),pos(),names.asAry(),finals); + int depth=1; + while( depth>0 ) { + if( peek('"') ) { parseString(); continue; } + if( match("'") ) { parseChar(); continue; } + char ch=_lexer.nextXChar(); + if( ch==Character.MAX_VALUE ) throw errorSyntax("}"); + if( ch=='{' ) depth++; + if( ch=='}' ) depth--; + } + return con(sig); + } + + private CallEndNode rawCall(Node ctrl, Node pub, ReturnNode target, Node... args) { + Node[] inputs = new Node[args.length+3]; + inputs[0]=ctrl; inputs[1]=pub; + System.arraycopy(args,0,inputs,2,args.length); + inputs[inputs.length-1]=new FunPtrNode(target.fun().sig(),target).peephole(); + // Keep the complete declaration-side caller set before any inlining. + CallNode call = new CallNode(loc(),inputs).init(); + CallEndNode end = new CallEndNode(call).init(); + call.link(target.fun()); + return end; + } + + private Node publish(TypeStruct shape, Node ctrl, Node pub, Node priv, Node ptr) { + priv.keep(); ptr.keep(); + for( Field f : shape._fields ) if( !f._one ) { + pub.keep(); + Node esc = new EscapeNode(loc(),f,ctrl,pub,priv,ptr).peephole(); + Node next = new MemMergeNode(pub,f._alias,esc).peephole(); + pub.unkeep(); pub=next; + } + ptr.unkeep(); priv.unkill(); + return pub; + } + + private TypeFunPtr makeAllocator(TypeStruct shape, ReturnNode init) { + Field cf=shape.field(shortName(shape._name)); + TypeFunPtr ctor=cf!=null && cf._t instanceof TypeFunPtr f ? f : null; + int nargs=ctor==null ? 0 : ctor.nargs()-2; + Ary captures=CAPTURES.get(shape._name); + Type[] types=new Type[nargs+captures.size()]; + for( int i=0; i"; fun._constructor=true; + _code.link(fun); + Node rpc=new ParmNode("$rpc",0,TypeRPC.BOT,fun,con(TypeRPC.BOT)).init(); + Node pub=new ParmNode(ScopeNode.MEM0,1,TypeMem.BOT,fun,con(TypeMem.BOT)).init(); + Node[] args=new Node[nargs+2]; + for( int i=0; i")) || id==null ) + return posT(old1); // Reset lexer to reparse + pos(old2); // Reset lexer to reparse + // Yes a forward ref, so declare it + TYPES.put(tname,((TypeMemPtr)t1).withAccess(false)); + // Return the (array, final) type + return t2; + } + + // Make an array type of 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(); + 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 }`. + private Type typeFunPtr() { + int old = pos(); // Record lexer position + match("{"); // Skip already-peeked '{' + 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("?"),false,TypeFunPtr.TEMPTY,t0); + Ary ts = new Ary<>(Type.class); + ts.push(t0); // First argument + while( true ) { + if( match("->") ) { // End of arguments, parse return + Type ret = type(); + if( ret==null || !match("}") ) + return posT(old); // Not a function + return TypeFunPtr.make(match("?"),false,ts.asAry(),ret); + } + Type t1 = type(); + if( t1==null ) return posT(old); // Not a function + ts.push(t1); + } + } + + // True if a TypeFunPtr, without advancing parser + private boolean isTypeFun() { + int old = pos(); + if( typeFunPtr()==null ) return false; + pos(old); + return true; + } + + /** + * Parse an expression of the form: + * + *

+     *     expr : bitwise [? expr [: expr]]
+     * 
+ * @return an expression {@link Node}, never {@code null} + */ + private Node parseExpression() { + Node expr = parseLogical(); + return match("?") ? parseTrinary(expr,":") : expr; + } + + /** + * Parse an bitwise expression + * + *
+     *     bitwise : compareExpr (('&' | '|' | '^') compareExpr)*
+     * 
+ * @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 = parseEquality(); + while( true ) { + if( false ) ; + 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,parseEquality()); + lhs = peep(lhs); + } + return lhs; + } + + + + /** + * Parse an eq/ne expression + */ + private Node parseEquality() { + Node lhs = parseComparison(); + while( true ) { + boolean eq = false; + if( match("==") ) eq = true; + else if( !match("!=") ) break; + lhs.keep(); + Node rhs = parseComparison(); + lhs = peep(new BoolNode.EQ(lhs.unkeep(),rhs)); + if( !eq ) lhs = peep(new NotNode(lhs)); + } + return lhs; + } + + /** + * Parse an expression of the form: + * + *
+     *     expr : shiftExpr < shiftExpr <= shiftExpr...
+     *     expr : shiftExpr > shiftExpr >= shiftExpr...
+     * 
+ * @return an comparator expression {@link Node}, never {@code null} + */ + private Node parseComparison() { + Node lhs = parseShift(); + int dir = 0; + + while( true ) { + int dir0; + if( false ) ; + else if( match("<=" ) ) dir0 = -1; + else if( match(">=" ) ) dir0 = -2; + else if( match("<" ) ) dir0 = 1; + else if( match(">" ) ) dir0 = 2; + else break; + lhs.keep(); + // Check and record direction + if( dir==0 ) { + dir = Math.abs(dir0); + Node rhs = parseShift(); + lhs = makeCompBool(dir0,lhs.unkeep(),rhs); // Convert to a bool + } else if( dir == Math.abs(dir0) ) { + // e0 < lhs < ??? + Node ifNode = new IfNode(ctrl(), lhs).peephole(); + Node ifT = new CProjNode(ifNode. keep(), 0, "True" ).peephole(); + Node ifF = new CProjNode(ifNode.unkeep(), 1, "False").peephole(); + // False side does nothing + ScopeNode fScope = _scope.dup(); + fScope.ctrl(ifF); + // True side executes next part of range + ctrl(ifT); + _scope.addGuards(ifT,lhs,false); + Node rhs = parseShift(); + _scope.removeGuards(ifT); // TODO: I think should remain true as long as possible + Node next = makeCompBool(dir0, lhs, rhs).keep(); + // Merge result + RegionNode r = ctrl(_scope.mergeScopes(fScope, loc())); + assert next._type.meet(lhs._type).isa( TypeInteger.BOOL ); + lhs = peep(new PhiNode("",r,next.unkeep(),lhs.unkeep())); + r.peephole(); + } else { + throw error("Mixing relational directions in a chained relational test"); + } + } + return lhs; + } + + private Node makeCompBool( int dir0, Node lhs, Node rhs ) { + // Convert to a bool + if( Math.abs(dir0)==2 ) // Swap direction + { Node tmp = lhs; lhs = rhs; rhs = tmp; } + lhs = peep(dir0 < 0 ? new BoolNode.LE(lhs,rhs) : new BoolNode.LT(lhs,rhs)); + return peep(lhs); + } + + + /** + * Parse an additive expression + * + *
+     *     shiftExpr : additiveExpr (('<<' | '>>' | '>>>') additiveExpr)*
+     * 
+ * @return a shift expression {@link Node}, never {@code null} + */ + private Node parseShift() { + Node lhs = parseAddition(); + while( true ) { + if( false ) ; + else if( match("<<") ) lhs = new ShlNode(loc(),lhs,null); + else if( match(">>>")) lhs = new ShrNode(loc(),lhs,null); + else if( match(">>") ) lhs = new SarNode(loc(),lhs,null); + else break; + lhs.setDef(2,parseAddition()); + ParseException err; + if( (err=lhs.err()) != null ) + throw err; + lhs = peep(lhs); + } + return lhs; + } + + /** + * Parse an additive expression + * + *
+     *     additiveExpr : multiplicativeExpr (('+' | '-') multiplicativeExpr)*
+     * 
+ * @return an add expression {@link Node}, never {@code null} + */ + private Node parseAddition() { + Node lhs = parseMultiplication(); + while( true ) { + if( false ) ; + else if( match("+") ) lhs = new AddNode(lhs,null); + else if( match("-") ) lhs = new SubNode(lhs,null); + else break; + lhs.setDef(2,parseMultiplication()); + lhs = peep(lhs); + } + return lhs; + } + + /** + * Parse an multiplicativeExpr expression + * + *
+     *     multiplicativeExpr : unaryExpr (('*' | '/') unaryExpr)*
+     * 
+ * @return a multiply expression {@link Node}, never {@code null} + */ + private Node parseMultiplication() { + var lhs = parseUnary(); + while( true ) { + if( false ) ; + else if( match("*") ) lhs = new MulNode(lhs,null); + else if( match("/") ) lhs = new DivNode(lhs,null); + else break; + lhs.setDef(2,parseUnary()); + lhs = peep(lhs); + } + return lhs; + } + + /** + * Parse a unary minus expression. + * + *
+     *     unaryExpr : ('-') unaryExpr | '!') unaryExpr | postfixExpr | primaryExpr | '--' Id | '++' Id
+     * 
+ * @return a unary expression {@link Node}, never {@code null} + */ + private Node parseUnary() { + // Pre-dec/pre-inc + int old = pos(); + if( match("--") || match("++") ) { + int delta = _lexer.peek(-1)=='+' ? 1 : -1; // Pre vs post + String name = _lexer.matchId(); + if( name!=null ) { + Var n = _scope.lookup(name); + if( n != null && !(n.type() instanceof TypeMemPtr) ) { + if( n._final ) + throw error("Cannot reassign final '"+n._name+"'"); + Node expr = liftExpr(peep(new AddNode(_scope.in(n),con(delta))),n.type(),true); + _scope.update(n,expr); + return expr; + } + } + // Reset, try again + pos(old); + } + if (match("-")) return peep(new MinusNode(parseUnary())); + if (match("!")) return peep(new NotNode(parseUnary())); + // Else be a primary expression + return parsePrimary(); + } + + /** + * Parse a primary expression: + * + *
+     *     primaryExpr : integerLiteral | "string" | 'char' | true | false | null |
+     *                   new Type | '(' expression ')' | Id['++','--'] |
+     *                   [static.]* Id
+     * 
+ * @return a primary {@link Node}, never {@code null} + */ + private Node parsePrimary() { + if( _lexer.isNumber(_lexer.peek()) ) return parseLiteral(); + if( _lexer.peek('"') ) return newString(parseString()); + if( matchx("true" ) ) return con(1); + if( matchx("false") ) return ZERO; + if( matchx("null" ) ) return NIL; + if( match ("'" ) ) return parseChar(); + 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 ) { + 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 + + // - 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) ) { + Var selfVar = _scope.lookup("self"); + Node self = liftExpr(_scope.in(selfVar),selfVar.type(),true); + return parsePostfixName(self,id); + } + + // Load local value + Node oldValue = _scope.in(var); + if( oldValue instanceof ConstantNode && !(oldValue instanceof FRefNode) && oldValue._type==Type.BOTTOM ) + throw error("Cannot read uninitialized field '"+id+"'"); + Node rvalue = liftExpr(oldValue,var.type(),true); + if( rvalue!=oldValue ) _scope.update(var,rvalue); + if( rvalue instanceof ConstantNode && rvalue._type == Type.BOTTOM ) + 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 ) // Normal primary, check for postfix updates + return parsePostfix(rvalue); + // Assign-update direct into Scope + if( var._final ) + throw error("Cannot reassign final '"+var._name+"'"); + checkCaptureWrite(var); + 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 ) { + if( var.isFRef() ) return false; // Promote an unresolved lexical name, not self.name. + if( _privateFun==_fun && _privateFun!=null ) return true; + 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); + if( _captures!=null && _scope.kind(var) instanceof Kind.Func && + !(var._final && def._type.isConstant()) && !(def instanceof FRefNode) ) { + if( !(def instanceof ParmNode parm && parm.fun()==_fun) ) { + _captures.add(def.keep()); + Type type=def._type; + int index=_fun.sig().nargs(); + _fun.addParseArg(type); + _scope.update(var,new ParmNode(var._name,index+2,type,_fun,con(type)).init()); + } + return false; + } + 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; + } + + private void checkCaptureWrite(Var var) { + if( _captures==null ) return; + for( int i=_scope.depth()-1; i>=0 && var._idx<_scope._kinds.at(i)._lexSize; i-- ) + if( _scope._kinds.at(i) instanceof Kind.Func ) + throw error("Cannot assign captured variable '"+var._name+"' in an initializer"); + } + + // 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, Type t ) { + // RHS of the update. + lhs.keep(); // Alive across parseAsgn + Node rhs = + (byte)ch == 1 ? con( 1) : // var++ + (byte)ch == -1 ? con(-1) : // var-- + parseAsgn(); // var op= rhs + if( !postfix(ch) ) lhs.unkeep(); // Allow to die in next peep + // 4 cases: + // int + int ==>> narrow int + // int + flt ==>> error, caller must fail assigning flt into int + // flt + int ==>> use float op, wrap toFloat() + // flt + flt ==>> use float op + Node op = switch(ch) { + case 1, (char)-1, + '+' -> new AddNode(lhs,rhs); + case '-' -> new SubNode(lhs,rhs); + case '*' -> new MulNode(lhs,rhs); + case '/' -> new DivNode(lhs,rhs); + case '&' -> new AndNode(loc(),lhs,rhs); + case '|' -> new OrNode(loc(),lhs,rhs); + case '^' -> new XorNode(loc(),lhs,rhs); + case Lexer.SHL -> new ShlNode(loc(),lhs,rhs); + case Lexer.SAR -> new SarNode(loc(),lhs,rhs); + case Lexer.SHR -> new ShrNode(loc(),lhs,rhs); + default -> throw Utils.TODO(); + }; + // Convert to float ops, or narrow int types; error if not declared type. + // Also, if postfix LHS is still keep() + return liftExpr(peep(op),t,true); + } + + + /** + Parse an allocation + */ + private Node alloc() { + Type t = type(); + if( t==null ) throw error("Expected a type"); + // Parse ary[ length_expr ] + if( match("[") ) { + if( !t.makeZero().isa(t) ) + throw error("Cannot allocate a non-nullable, since arrays always zero/null fill"); + Node len = parseAsgn(); + if( !(len._type instanceof TypeInteger) ) + throw error("Cannot allocate an array with length "+len._type); + require("]"); + TypeMemPtr tmp = typeAry(t,false); + return newArray(tmp._obj,len); + } + + if( !(t instanceof TypeMemPtr tmp) ) + throw error("Cannot allocate a "+t.str()); + + TypeStruct shape=tmp._obj; + TypeFunPtr sig=ALLOCS.get(shape._name); + if( sig==null ) throw error("Unknown struct type '"+shape._name+"'"); + if( !peek('{') ) { + // Obvious missing defaults can still be diagnosed here. A user + // constructor or a merge needs the later publication check. + if( shape.field(shortName(shape._name))==null ) { + StructNode defaults=INITS.get(shape._name); + Type initial=INITIALIZERS.get(shape._name).expr()._type; + for( int i=0; i args=new Ary<>(Node.class); + if( match("(") ) { + while( !peek(')') ) { + Node arg=parseAsgn(); + checkPublication(arg); + args.add(arg.keep()); + if( !match(",") ) break; + } + require(")"); + } + for( Node capture : CAPTURES.get(shape._name) ) { + checkPublication(capture); + args.add(capture.keep()); + } + if( args.size()!=sig.nargs() ) throw error("Wrong number of constructor arguments for '"+shape._name+"'"); + for( Node arg : args ) arg.unkeep(); + return callWithArgs(fptr,args.asAry()); + } + require("{"); + Node alloc=new NewNode(TypeMemPtr.make(shape),ctrl(),null,off(shape," len")).init(); + Node ptr=new ProjNode(alloc,0,shape._name).init().keep(); + Node priv=new ProjNode(alloc,1,"#selfMem").init(); + Ary captures=CAPTURES.get(shape._name); + Node[] args=new Node[captures.size()+2]; + args[0]=ptr; args[1]=priv; + for( int i=0; i