From 18fd28edce813d36c491e42d9e64c53c7251d26e Mon Sep 17 00:00:00 2001 From: Cliff Click Date: Fri, 7 Feb 2025 08:58:36 -0800 Subject: Chapter 19: Instruction Selection and Portable Compilation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Tamás Hadházy <85063808+Hels15@users.noreply.github.com> Co-authored-by: Hsiang-Ying Fu <66062782+fuhsnn@users.noreply.github.com> Co-authored-by: hadha Co-authored-by: godalming123 <68993177+godalming123@users.noreply.github.com> Co-authored-by: fuhsnn <66062782+fuhsnn@users.noreply.github.com> Co-authored-by: Symious <14933944+symious@users.noreply.github.com> Co-authored-by: RobertObkircher Co-authored-by: Jun Kawasaki Co-authored-by: Claude Opus 5 --- .../java/com/seaofnodes/simple/ASMPrinter.java | 51 ++ src/main/java/com/seaofnodes/simple/Ary.java | 34 +- src/main/java/com/seaofnodes/simple/CodeGen.java | 234 +++++++-- .../com/seaofnodes/simple/GlobalCodeMotion.java | 85 ++-- src/main/java/com/seaofnodes/simple/IRPrinter.java | 4 +- src/main/java/com/seaofnodes/simple/IterPeeps.java | 50 +- .../java/com/seaofnodes/simple/ListScheduler.java | 245 +++++++--- src/main/java/com/seaofnodes/simple/Machine.java | 21 + src/main/java/com/seaofnodes/simple/Parser.java | 97 ++-- src/main/java/com/seaofnodes/simple/RegMask.java | 47 ++ src/main/java/com/seaofnodes/simple/SB.java | 17 + .../java/com/seaofnodes/simple/node/AddNode.java | 3 +- .../java/com/seaofnodes/simple/node/BoolNode.java | 26 +- .../com/seaofnodes/simple/node/BulkMemPhiNode.java | 16 +- .../java/com/seaofnodes/simple/node/CFGNode.java | 19 +- .../java/com/seaofnodes/simple/node/CProjNode.java | 4 +- .../com/seaofnodes/simple/node/CallEndNode.java | 15 +- .../java/com/seaofnodes/simple/node/CallNode.java | 24 +- .../com/seaofnodes/simple/node/ConstantNode.java | 17 +- .../java/com/seaofnodes/simple/node/CtrlNode.java | 3 +- .../java/com/seaofnodes/simple/node/FunNode.java | 38 +- .../java/com/seaofnodes/simple/node/IfNode.java | 6 +- .../java/com/seaofnodes/simple/node/LoadNode.java | 71 +-- .../java/com/seaofnodes/simple/node/LoopNode.java | 1 + .../seaofnodes/simple/node/MachConcreteNode.java | 27 ++ .../java/com/seaofnodes/simple/node/MachNode.java | 49 ++ .../com/seaofnodes/simple/node/MemMergeNode.java | 13 +- .../java/com/seaofnodes/simple/node/MemOpNode.java | 41 ++ .../com/seaofnodes/simple/node/MemPhiNode.java | 6 +- .../java/com/seaofnodes/simple/node/MulNode.java | 28 +- .../java/com/seaofnodes/simple/node/MultiUse.java | 5 - .../java/com/seaofnodes/simple/node/NewNode.java | 17 +- src/main/java/com/seaofnodes/simple/node/Node.java | 103 ++-- .../java/com/seaofnodes/simple/node/ParmNode.java | 3 +- .../java/com/seaofnodes/simple/node/PhiNode.java | 4 +- .../java/com/seaofnodes/simple/node/ProjNode.java | 6 +- .../com/seaofnodes/simple/node/ReadOnlyNode.java | 1 + .../com/seaofnodes/simple/node/RegionNode.java | 66 +-- .../com/seaofnodes/simple/node/ReturnNode.java | 3 +- .../java/com/seaofnodes/simple/node/ScopeNode.java | 6 +- .../java/com/seaofnodes/simple/node/StartNode.java | 7 +- .../java/com/seaofnodes/simple/node/StopNode.java | 1 + .../java/com/seaofnodes/simple/node/StoreNode.java | 49 +- .../java/com/seaofnodes/simple/node/XCtrlNode.java | 3 +- .../simple/node/cpus/riscv/AddFRISC.java | 30 ++ .../simple/node/cpus/riscv/AddIRISC.java | 39 ++ .../seaofnodes/simple/node/cpus/riscv/AddRISC.java | 36 ++ .../simple/node/cpus/riscv/AndIRISC.java | 40 ++ .../seaofnodes/simple/node/cpus/riscv/AndRISC.java | 41 ++ .../simple/node/cpus/riscv/CBranchRISC.java | 49 ++ .../simple/node/cpus/riscv/CallRISC.java | 37 ++ .../simple/node/cpus/riscv/CallRRISC.java | 31 ++ .../simple/node/cpus/riscv/CmpIRISC.java | 44 ++ .../seaofnodes/simple/node/cpus/riscv/CmpRISC.java | 28 ++ .../simple/node/cpus/riscv/DivFRISC.java | 30 ++ .../seaofnodes/simple/node/cpus/riscv/DivRISC.java | 33 ++ .../simple/node/cpus/riscv/F8ToIRISC.java | 27 ++ .../seaofnodes/simple/node/cpus/riscv/FltRISC.java | 39 ++ .../seaofnodes/simple/node/cpus/riscv/FunRISC.java | 22 + .../simple/node/cpus/riscv/I2F8RISC.java | 29 ++ .../seaofnodes/simple/node/cpus/riscv/IntRISC.java | 43 ++ .../simple/node/cpus/riscv/LoadRISC.java | 34 ++ .../simple/node/cpus/riscv/MemOpRISC.java | 39 ++ .../simple/node/cpus/riscv/MulFRISC.java | 30 ++ .../simple/node/cpus/riscv/MulIRISC.java | 35 ++ .../seaofnodes/simple/node/cpus/riscv/MulRISC.java | 33 ++ .../seaofnodes/simple/node/cpus/riscv/NewRISC.java | 44 ++ .../seaofnodes/simple/node/cpus/riscv/OrIRISC.java | 43 ++ .../seaofnodes/simple/node/cpus/riscv/OrRISC.java | 43 ++ .../simple/node/cpus/riscv/ParmRISC.java | 25 + .../simple/node/cpus/riscv/ProjRISC.java | 23 + .../seaofnodes/simple/node/cpus/riscv/RetRISC.java | 48 ++ .../seaofnodes/simple/node/cpus/riscv/SetRISC.java | 35 ++ .../simple/node/cpus/riscv/SllIRISC.java | 40 ++ .../seaofnodes/simple/node/cpus/riscv/SllRISC.java | 36 ++ .../simple/node/cpus/riscv/SraIRISC.java | 43 ++ .../seaofnodes/simple/node/cpus/riscv/SraRISC.java | 42 ++ .../simple/node/cpus/riscv/SrlIRISC.java | 43 ++ .../seaofnodes/simple/node/cpus/riscv/SrlRISC.java | 38 ++ .../simple/node/cpus/riscv/StoreRISC.java | 40 ++ .../simple/node/cpus/riscv/SubFRISC.java | 30 ++ .../seaofnodes/simple/node/cpus/riscv/SubRISC.java | 29 ++ .../seaofnodes/simple/node/cpus/riscv/TFPRISC.java | 43 ++ .../simple/node/cpus/riscv/XorIRISC.java | 45 ++ .../seaofnodes/simple/node/cpus/riscv/XorRISC.java | 43 ++ .../seaofnodes/simple/node/cpus/riscv/riscv.java | 332 +++++++++++++ .../simple/node/cpus/x86_64_v2/AddFMemX86.java | 33 ++ .../simple/node/cpus/x86_64_v2/AddFX86.java | 29 ++ .../simple/node/cpus/x86_64_v2/AddIX86.java | 39 ++ .../simple/node/cpus/x86_64_v2/AddMemX86.java | 25 + .../simple/node/cpus/x86_64_v2/AddX86.java | 29 ++ .../simple/node/cpus/x86_64_v2/AndIX86.java | 42 ++ .../simple/node/cpus/x86_64_v2/AndX86.java | 33 ++ .../simple/node/cpus/x86_64_v2/CallEndX86.java | 37 ++ .../simple/node/cpus/x86_64_v2/CallRX86.java | 31 ++ .../simple/node/cpus/x86_64_v2/CallX86.java | 41 ++ .../simple/node/cpus/x86_64_v2/CmpIX86.java | 43 ++ .../simple/node/cpus/x86_64_v2/CmpMemX86.java | 33 ++ .../simple/node/cpus/x86_64_v2/CmpX86.java | 28 ++ .../simple/node/cpus/x86_64_v2/DivFX86.java | 33 ++ .../simple/node/cpus/x86_64_v2/DivX86.java | 31 ++ .../simple/node/cpus/x86_64_v2/F8ToIX86.java | 27 ++ .../simple/node/cpus/x86_64_v2/FltX86.java | 36 ++ .../simple/node/cpus/x86_64_v2/FunX86.java | 21 + .../simple/node/cpus/x86_64_v2/I2f8X86.java | 28 ++ .../simple/node/cpus/x86_64_v2/IntX86.java | 45 ++ .../simple/node/cpus/x86_64_v2/JmpX86.java | 46 ++ .../simple/node/cpus/x86_64_v2/LeaX86.java | 42 ++ .../simple/node/cpus/x86_64_v2/LoadX86.java | 25 + .../simple/node/cpus/x86_64_v2/MemAddX86.java | 28 ++ .../simple/node/cpus/x86_64_v2/MemOpX86.java | 90 ++++ .../simple/node/cpus/x86_64_v2/MulFX86.java | 30 ++ .../simple/node/cpus/x86_64_v2/MulIX86.java | 36 ++ .../simple/node/cpus/x86_64_v2/MulX86.java | 30 ++ .../simple/node/cpus/x86_64_v2/NewX86.java | 40 ++ .../simple/node/cpus/x86_64_v2/OrIX86.java | 44 ++ .../simple/node/cpus/x86_64_v2/OrX86.java | 33 ++ .../simple/node/cpus/x86_64_v2/ParmX86.java | 23 + .../simple/node/cpus/x86_64_v2/ProjX86.java | 25 + .../simple/node/cpus/x86_64_v2/RetX86.java | 48 ++ .../simple/node/cpus/x86_64_v2/SarIX86.java | 37 ++ .../simple/node/cpus/x86_64_v2/SarX86.java | 42 ++ .../simple/node/cpus/x86_64_v2/SetX86.java | 39 ++ .../simple/node/cpus/x86_64_v2/ShlIX86.java | 38 ++ .../simple/node/cpus/x86_64_v2/ShlX86.java | 39 ++ .../simple/node/cpus/x86_64_v2/ShrIX86.java | 40 ++ .../simple/node/cpus/x86_64_v2/ShrX86.java | 38 ++ .../simple/node/cpus/x86_64_v2/StoreX86.java | 31 ++ .../simple/node/cpus/x86_64_v2/SubFX86.java | 29 ++ .../simple/node/cpus/x86_64_v2/SubX86.java | 29 ++ .../simple/node/cpus/x86_64_v2/TFPX86.java | 41 ++ .../simple/node/cpus/x86_64_v2/XorIX86.java | 42 ++ .../simple/node/cpus/x86_64_v2/x86_64_v2.java | 443 +++++++++++++++++ .../simple/print/SimpleGraphAdapter.java | 2 +- .../simple/print/SimpleGraphObserver.java | 9 +- src/main/java/com/seaofnodes/simple/type/Type.java | 4 - .../com/seaofnodes/simple/type/TypeFunPtr.java | 68 +-- .../com/seaofnodes/simple/type/TypeStruct.java | 8 +- .../java/com/seaofnodes/simple/BrainFuckTest.java | 2 +- .../java/com/seaofnodes/simple/Chapter03Test.java | 2 +- .../java/com/seaofnodes/simple/Chapter04Test.java | 21 +- .../java/com/seaofnodes/simple/Chapter05Test.java | 64 +-- .../java/com/seaofnodes/simple/Chapter06Test.java | 30 +- .../java/com/seaofnodes/simple/Chapter08Test.java | 4 +- .../java/com/seaofnodes/simple/Chapter09Test.java | 4 +- .../java/com/seaofnodes/simple/Chapter11Test.java | 10 +- .../java/com/seaofnodes/simple/Chapter12Test.java | 2 +- .../java/com/seaofnodes/simple/Chapter15Test.java | 12 +- .../java/com/seaofnodes/simple/Chapter17bTest.java | 8 +- .../java/com/seaofnodes/simple/Chapter18Test.java | 58 +-- .../java/com/seaofnodes/simple/Chapter19Test.java | 529 +++++++++++++++++++++ src/test/java/com/seaofnodes/simple/Eval2.java | 6 +- .../java/com/seaofnodes/simple/FuzzerWrap.java | 7 +- src/test/java/com/seaofnodes/simple/Main.java | 17 + .../com/seaofnodes/simple/evaluator/Evaluator.java | 13 +- .../com/seaofnodes/simple/evaluator/Scheduler.java | 2 +- .../java/com/seaofnodes/simple/fuzzer/Fuzzer.java | 15 +- .../com/seaofnodes/simple/fuzzer/FuzzerUtils.java | 36 +- .../seaofnodes/simple/fuzzer/ScriptGenerator.java | 16 +- src/test/java/com/seaofnodes/simple/progs/fcn.smp | 15 + src/test/java/com/seaofnodes/simple/progs/fcn2.smp | 5 + src/test/java/com/seaofnodes/simple/progs/fcn3.smp | 42 ++ .../java/com/seaofnodes/simple/progs/sieve.smp | 32 ++ 163 files changed, 5802 insertions(+), 733 deletions(-) create mode 100644 src/main/java/com/seaofnodes/simple/ASMPrinter.java create mode 100644 src/main/java/com/seaofnodes/simple/Machine.java create mode 100644 src/main/java/com/seaofnodes/simple/RegMask.java create mode 100644 src/main/java/com/seaofnodes/simple/node/MachConcreteNode.java create mode 100644 src/main/java/com/seaofnodes/simple/node/MachNode.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/MultiUse.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/riscv/AddFRISC.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/riscv/AddIRISC.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/riscv/AddRISC.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/riscv/AndIRISC.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/riscv/AndRISC.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/riscv/CBranchRISC.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/riscv/CallRISC.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/riscv/CallRRISC.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/riscv/CmpIRISC.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/riscv/CmpRISC.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/riscv/DivFRISC.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/riscv/DivRISC.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/riscv/F8ToIRISC.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/riscv/FltRISC.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/riscv/FunRISC.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/riscv/I2F8RISC.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/riscv/IntRISC.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/riscv/LoadRISC.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/riscv/MemOpRISC.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/riscv/MulFRISC.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/riscv/MulIRISC.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/riscv/MulRISC.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/riscv/NewRISC.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/riscv/OrIRISC.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/riscv/OrRISC.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/riscv/ParmRISC.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/riscv/ProjRISC.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/riscv/RetRISC.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/riscv/SetRISC.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/riscv/SllIRISC.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/riscv/SllRISC.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/riscv/SraIRISC.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/riscv/SraRISC.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/riscv/SrlIRISC.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/riscv/SrlRISC.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/riscv/StoreRISC.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/riscv/SubFRISC.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/riscv/SubRISC.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/riscv/TFPRISC.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/riscv/XorIRISC.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/riscv/XorRISC.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/riscv/riscv.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddFMemX86.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddFX86.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddIX86.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddMemX86.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddX86.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AndIX86.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AndX86.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CallEndX86.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CallRX86.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CallX86.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CmpIX86.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CmpMemX86.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CmpX86.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/DivFX86.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/DivX86.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/F8ToIX86.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/FltX86.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/FunX86.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/I2f8X86.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/IntX86.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/JmpX86.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/LeaX86.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/LoadX86.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MemAddX86.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MemOpX86.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MulFX86.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MulIX86.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MulX86.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/NewX86.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/OrIX86.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/OrX86.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ParmX86.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ProjX86.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/RetX86.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SarIX86.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SarX86.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SetX86.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShlIX86.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShlX86.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShrIX86.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShrX86.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/StoreX86.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SubFX86.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SubX86.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/TFPX86.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/XorIX86.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/x86_64_v2.java create mode 100644 src/test/java/com/seaofnodes/simple/Chapter19Test.java create mode 100644 src/test/java/com/seaofnodes/simple/Main.java create mode 100644 src/test/java/com/seaofnodes/simple/progs/fcn.smp create mode 100644 src/test/java/com/seaofnodes/simple/progs/fcn2.smp create mode 100644 src/test/java/com/seaofnodes/simple/progs/fcn3.smp create mode 100644 src/test/java/com/seaofnodes/simple/progs/sieve.smp (limited to 'src') diff --git a/src/main/java/com/seaofnodes/simple/ASMPrinter.java b/src/main/java/com/seaofnodes/simple/ASMPrinter.java new file mode 100644 index 00000000..3c4162ea --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/ASMPrinter.java @@ -0,0 +1,51 @@ +package com.seaofnodes.simple; + +import com.seaofnodes.print.AssemblyAdapter; +import com.seaofnodes.print.ASMPrinter.Data; +import com.seaofnodes.print.ASMPrinter.Pool; +import com.seaofnodes.simple.CodeGen; +import com.seaofnodes.simple.SB; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.type.*; +import java.util.ArrayList; + +/** Machine-specific facts; traversal, columns and byte display live in print/. */ +public final class ASMPrinter extends AssemblyAdapter { + private final CodeGen _code; + private ASMPrinter(CodeGen code) { + super(new IRPrinter()); + _code=code; + } + public static SB print(SB sb, CodeGen code) { + return sb.p(new com.seaofnodes.print.ASMPrinter<>(new ASMPrinter(code)).print()); + } + @Override public ArrayList blocks() { + var blocks=new ArrayList(); + var cfg=_code._cfg; + if( cfg!=null ) for( Node n : cfg ) blocks.add(n); + return blocks; + } + @Override public String op(Node n) { return n instanceof MachNode m ? m.op() : n.label(); } + @Override public String register(Node n) { return _code.reg(n); } + @Override public String comment(Node n) { return n.comment(); } + @Override public String phiName(Node n) { return ((PhiNode)n)._label; } + @Override public String operands(Node n) { + SB sb=new SB(); + if( n instanceof MachNode m ) m.asm(_code,sb); + else if( !(n._type instanceof TypeMem) ) { + sb.p(n._nid).p(": "); + for( Node def : n._inputs ) sb.p(def==null ? "___" : Integer.toString(def._nid)).p(' '); + } + return sb.toString(); + } + @Override public boolean labelBlock(Node n) { + return !(n instanceof FunNode) && !(n instanceof IfNode) && !(n instanceof CallNode) && + !(n instanceof CallEndNode) && !(n instanceof CProjNode && ir.input0(n) instanceof CallEndNode); + } + @Override public boolean hidden(Node n) { + if( n instanceof PhiNode || n instanceof ProjNode ) + if( n._type instanceof TypeMem || n._type instanceof TypeRPC ) return true; + if( n instanceof MemMergeNode ) return true; + return n.getClass()==ConstantNode.class; + } +} diff --git a/src/main/java/com/seaofnodes/simple/Ary.java b/src/main/java/com/seaofnodes/simple/Ary.java index b5184948..73ada8e0 100644 --- a/src/main/java/com/seaofnodes/simple/Ary.java +++ b/src/main/java/com/seaofnodes/simple/Ary.java @@ -42,8 +42,7 @@ public class Ary extends AbstractList implements List { } /** @return remove and return last element */ public E pop( ) { - range_check(0); - return _es[--_len]; + return _len==0 ? null : _es[--_len]; } @Override public E removeLast() { return pop(); } @@ -93,6 +92,22 @@ public class Ary extends AbstractList implements List { return old; } + /** Set existing element + * @param i element to set + * @param e value to set + * @return old value + * @exception AIOOBE if !(0 <= i < _len) + */ + public E setX( int i, E e ) { + if( i >= _len ) { + while( i >= _es.length ) _es = Arrays.copyOf( _es, _es.length << 1 ); + _len = i+1; + } + E old = _es[i]; + _es[i] = e; + return old; + } + public Ary setLen( int len ) { if( len > _len ) while( len>= _es.length ) _es = Arrays.copyOf(_es,_es.length<<1); @@ -103,13 +118,14 @@ public class Ary extends AbstractList implements List { return this; } - public void swap( int i, int j ) { + public E swap( int i, int j ) { range_check(i); range_check(j); - if( i==j ) return; E tmp = _es[i]; + if( i==j ) return tmp; _es[i] = _es[j]; _es[j] = tmp; + return tmp; } @@ -139,12 +155,12 @@ public class Ary extends AbstractList implements List { return this; } - /** @return compact array version */ - public E[] asAry() { return Arrays.copyOf(_es,_len); } + /** @return compact array version */ + public E[] asAry() { return Arrays.copyOf(_es,_len); } - /** Sorts in-place - * @param c Comparator to sort by */ - public void sort_update(Comparator c ) { Arrays.sort(_es, 0, _len, c); } + /** Sorts in-place + * @param c Comparator to sort by */ + public void sort_update(Comparator c ) { Arrays.sort(_es, 0, _len, c); } /** Find the first matching element using ==, or -1 if none. Note that * most del calls shuffle the list, so the first element might be random. diff --git a/src/main/java/com/seaofnodes/simple/CodeGen.java b/src/main/java/com/seaofnodes/simple/CodeGen.java index 864b3a1a..0a3a60e8 100644 --- a/src/main/java/com/seaofnodes/simple/CodeGen.java +++ b/src/main/java/com/seaofnodes/simple/CodeGen.java @@ -3,8 +3,11 @@ import com.seaofnodes.graph.GraphObserver; import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.node.cpus.x86_64_v2.MemOpX86; import com.seaofnodes.simple.type.*; +import java.util.BitSet; import java.util.HashMap; +import java.util.IdentityHashMap; public class CodeGen { public GraphObserver _obs; @@ -18,66 +21,154 @@ public class CodeGen { } + // Last created CodeGen as a global; used all over to avoid passing about a + // "context". + public static CodeGen CODE; public enum Phase { - Parse, Opto, TypeCheck, Schedule, LocalSched, RegAlloc; + Parse, // Parse ASCII text into Sea-of-Nodes IR + Opto, // Run ideal optimizations + TypeCheck, // Last check for bad programs + InstructionSelection, // Convert to target hardware nodes + Schedule, // Global schedule (code motion) nodes + LocalSched, // Local schedule + RegAlloc; // Register allocation + } + public enum CallingConv { + Win64, + SystemV } public Phase _phase; + // --------------------------- + // Compilation source code public final String _src; // Compile-time known initial argument type public final TypeInteger _arg; + public CallingConv _callingConv; + + // --------------------------- + public CodeGen( String src ) { this(src, TypeInteger.BOT, 123L ); } + public CodeGen( String src, TypeInteger arg, long workListSeed ) { + CODE = this; + _phase = null; + _callingConv = null; + _start = new StartNode(arg); + _stop = new StopNode(src); + _src = src; + _arg = arg; + _iter = new IterPeeps(workListSeed); + P = new Parser(this,arg); + } + + + // --------------------------- + /** + * A counter, for unique node id generation. Starting with value 1, to + * avoid bugs confusing node ID 0 with uninitialized values. + */ + private int _uid = 1; + public int UID() { return _uid; } + public int getUID() { return _uid++; } + + // Next available memory alias number + private int _alias = 2; // 0 is for control, 1 for memory + public int getALIAS() { return _alias++; } - public Parser P; + + // Popular visit bitset, declared here, so it gets reused all over + public final BitSet _visit = new BitSet(); + public BitSet visit() { assert _visit.isEmpty(); return _visit; } + + // Start and stop; end points of the generated IR public StartNode _start; - public StopNode _stop ; + public StopNode _stop; + + // Global Value Numbering. Hash over opcode and inputs; hits in this table + // are structurally equal. + public final HashMap _gvn = new HashMap<>(); + + // Compute "function indices": FIDX. + // Each new request at the same signature gets a new FIDX. + private final HashMap FIDXS = new HashMap<>(); + public TypeFunPtr makeFun( TypeTuple sig, Type ret ) { + Integer i = FIDXS.get(sig); + int fidx = i==null ? 0 : i; + FIDXS.put(sig,fidx+1); // Track count per sig + assert fidx<64; // TODO: need a larger FIDX space + return TypeFunPtr.make((byte)2,sig,ret, 1L< _linker = new HashMap<>(); + // Parser object + public final Parser P; - // Last created CodeGen as a global, for easier debugging prints - public static CodeGen CODE; - - CodeGen( String src, TypeInteger arg ) { _phase = null; _src = src; _arg=arg; CODE=this; } - public CodeGen( String src ) { this(src,TypeInteger.BOT); } - + // Parse ASCII text into Sea-of-Nodes IR public CodeGen parse() { assert _phase == null; _phase = Phase.Parse; - _linker.clear(); - P = new Parser(_src,_arg); - _stop = P.parse(); - _start = Parser.START; // Capture global start + P.parse(); if( _obs != null ) _obs.phase("Parse"); - P = null; // No longer parsing return this; } + + // --------------------------- + // Iterator peepholes. + public final IterPeeps _iter; + // Stop tracking deps while assertin + public boolean _midAssert; + // Statistics on peepholes + public int _iter_cnt, _iter_nop_cnt; + // Stat gathering + public void iterCnt() { if( !_midAssert ) _iter_cnt++; } + public void iterNop() { if( !_midAssert ) _iter_nop_cnt++; } + + // Run ideal optimizations public CodeGen opto() { assert _phase == Phase.Parse; _phase = Phase.Opto; - // Optimization - IterPeeps.iterate(_stop); + // Pessimistic peephole optimization on a worklist + _iter.iterate(this); // TODO: // Optimistic + // TODO: + // loop unroll, peel, RCE, etc if( _obs != null ) _obs.phase("Opto"); return this; } + public N add( N n ) { return (N)_iter.add(n); } + public void addAll( Ary ary ) { _iter.addAll(ary); } + + // --------------------------- + // Last check for bad programs public CodeGen typeCheck() { // Demand phase Opto for cleaning up dead control flow at least, // required for the following GCM. - assert _phase == Phase.Opto; + assert _phase.ordinal() <= Phase.Opto.ordinal(); _phase = Phase.TypeCheck; - // Type check Parser.ParseException err = _stop.walk(n -> { Parser.ParseException msg = n.err(); if( msg != null && _obs != null ) _obs.error(n, msg.getMessage()); @@ -90,20 +181,96 @@ public class CodeGen { } + // --------------------------- + // Code generation CPU target + public Machine _mach; + + // Convert to target hardware nodes + public CodeGen instSelect( String cpu, String callingConv ) { + assert _phase.ordinal() <= Phase.TypeCheck.ordinal(); + _phase = Phase.InstructionSelection; + + _callingConv = switch(callingConv) { + case "SystemV" -> CallingConv.SystemV; + case "Win64" -> CallingConv.Win64; + default -> throw Utils.TODO(); // Unknown calling convention + }; + + // Look for CPU in fixed named place: + // com.seaofnodes.simple.node.cpus."cpu"."cpu.class" + String clzFile = "com.seaofnodes.simple.node.cpus."+cpu+"."+cpu; + try { _mach = ((Class) Class.forName( clzFile )).getDeclaredConstructor().newInstance(); } + catch( Exception e ) { throw new RuntimeException(e); } + + // Convert to machine ops + _uid = 1; // All new machine nodes reset numbering + var map = new IdentityHashMap(); + _instSelect( _stop, map ); + _stop = ( StopNode)map.get(_stop ); + _start = (StartNode)map.get(_start); + _instOuts(_stop,visit()); + _visit.clear(); + return this; + } + + // Walk all ideal nodes, recursively mapping ideal to machine nodes, then + // make a machine node for "this". + private Node _instSelect( Node n, IdentityHashMap map ) { + if( n==null ) return null; + Node x = map.get(n); + if( x !=null ) return x; // Been there, done that + + // If n is a MachConcrete, then its part of a multi-node expansion. + // It does not need instruction selection (already selected!) + // but it does need its inputs walked. + if( n instanceof MachConcreteNode || n instanceof MemOpX86) { + for( int i=0; i < n.nIns(); i++ ) + n._inputs.set(i, _instSelect(n.in(i),map) ); + return n; + } + + // Produce a machine node from n; map it to flag as done so stops cycles. + map.put(n, x=_mach.instSelect(n) ); + // Walk machine op and replace inputs with mapped inputs + for( int i=0; i < x.nIns(); i++ ) + x._inputs.set(i, _instSelect(x.in(i),map) ); + if( x instanceof MachNode mach ) + mach.postSelect(); // Post selection action + + // Updates forward edges only. + n._outputs.clear(); + return x; + } + + // Walk all machine Nodes, and set their output edges + private void _instOuts( Node n, BitSet visit ) { + if( visit.get(n._nid) ) return; + visit.set(n._nid); + for( Node in : n._inputs ) + if( in!=null ) { + in._outputs.push(n); + _instOuts(in,visit); + } + } + + + // --------------------------- + // Control Flow Graph in RPO order. + Ary _cfg = new Ary<>(CFGNode.class); + + // Global schedule (code motion) nodes public CodeGen GCM() { - assert _phase == Phase.TypeCheck; + assert _phase.ordinal() <= Phase.InstructionSelection.ordinal(); _phase = Phase.Schedule; // Build the loop tree, fix never-exit loops _start.buildLoopTree(_stop); + GlobalCodeMotion.buildCFG(this); - - // TODO: - // loop unroll, peel, RCE, etc - GlobalCodeMotion.buildCFG(_start,_stop); return this; } + // --------------------------- // Local (basic block) scheduler phase, a classic list scheduler public CodeGen localSched() { assert _phase == Phase.Schedule; @@ -112,25 +279,26 @@ public class CodeGen { return this; } - // Reverse from a constant function pointer to the IR function being called - public FunNode link( TypeFunPtr tfp ) { - assert tfp.isConstant(); - return _linker.get(tfp.makeFrom(Type.BOTTOM)); - } - public void link(FunNode fun) { - _linker.put(fun.sig().makeFrom(Type.BOTTOM),fun); + // --------------------------- + public String reg(Node n) { + if( _phase == Phase.RegAlloc ) throw Utils.TODO(); + return "N"+ n._nid; } + + // --------------------------- + SB asm(SB sb) { return ASMPrinter.print(sb,this); } + String asm() { return asm(new SB()).toString(); } + + // Testing shortcuts Node ctrl() { return _stop.ret().ctrl(); } Node expr() { return _stop.ret().expr(); } String print() { return _stop.print(); } // Debugging helper - @Override public String toString() { - return IRPrinter.prettyPrint(this); - } + @Override public String toString() { return IRPrinter.prettyPrint(this); } // Debugging helper public Node f(int idx) { return _stop.find(idx); } diff --git a/src/main/java/com/seaofnodes/simple/GlobalCodeMotion.java b/src/main/java/com/seaofnodes/simple/GlobalCodeMotion.java index 8221ede1..95ada83d 100644 --- a/src/main/java/com/seaofnodes/simple/GlobalCodeMotion.java +++ b/src/main/java/com/seaofnodes/simple/GlobalCodeMotion.java @@ -10,12 +10,36 @@ public abstract class GlobalCodeMotion { // Arrange that the existing isCFG() Nodes form a valid CFG. The // Node.use(0) is always a block tail (either IfNode or head of the // following block). There are no unreachable infinite loops. - public static void buildCFG( StartNode start, StopNode stop ) { - schedEarly(start); - breakUpGlobalConstants(start); - schedLate(stop); + public static void buildCFG( CodeGen code ) { + Ary rpo = new Ary<>(CFGNode.class); + _rpo_cfg(null, code._start, code.visit(), rpo); + // Reverse in-place + for( int i=0; i< rpo.size()>>1; i++ ) { + int j = rpo.size()-1-i; + CFGNode tmp = rpo.get(i); + rpo.set(i, rpo.get(j)); + rpo.set(j, tmp); + } + // Set global CFG + code._cfg = rpo; + + schedEarly(code); + breakUpGlobalConstants(code._start); + code._visit.clear(); + schedLate (code); } + // Post-Order of CFG + private static void _rpo_cfg(CFGNode def, Node use, BitSet visit, Ary rpo) { + if( !(use instanceof CFGNode cfg) || visit.get(cfg._nid) ) + return; // Been there, done that + if( def instanceof CallNode && cfg instanceof FunNode ) + return; // Ignore linked function calls + visit.set(cfg._nid); + for( Node useuse : cfg._outputs ) + _rpo_cfg(cfg,useuse,visit,rpo); + rpo.add(cfg); + } // After early scheduling, every global chain member has Start in slot 0. // Give each function one private copy of the entire dependency graph. @@ -24,7 +48,7 @@ public abstract class GlobalCodeMotion { var cons = new ArrayList(); for( Node con : start._outputs ) if( con!=null && !(con instanceof CFGNode) && - con.isConst() ) { + (con.isConst() || (con instanceof MachNode mach && mach.outregmap()!=null)) ) { globals.put(con,true); cons.add(con.keep()); // Preserve original inputs while rewiring users. } @@ -72,38 +96,22 @@ public abstract class GlobalCodeMotion { // (except at loops). Since defs are visited first - and hoisted as early // as possible, when we come to a use we place it just after its deepest // input. - private static void schedEarly(StartNode start) { - ArrayList rpo = new ArrayList<>(); - BitSet visit = new BitSet(); - _rpo_cfg(null, start, visit, rpo); + private static void schedEarly(CodeGen code) { // Reverse Post-Order on CFG - for( int j=rpo.size()-1; j>=0; j-- ) { - CFGNode cfg = rpo.get(j); + for( CFGNode cfg : code._cfg ) { cfg.loopDepth(); for( Node n : cfg._inputs ) - _schedEarly(n,visit); + _schedEarly(n,code._visit ); // In dead infinite loops, entire code blocks may be unreachable // from below. Reach down from the CFG to their Phis so their // inputs are scheduled too. if( cfg instanceof RegionNode ) for( Node phi : cfg._outputs ) if( phi instanceof PhiNode ) - _schedEarly(phi,visit); + _schedEarly(phi,code._visit ); } } - // Post-Order of CFG - private static void _rpo_cfg(CFGNode def, Node use, BitSet visit, ArrayList rpo) { - if( !(use instanceof CFGNode cfg) || visit.get(cfg._nid) ) - return; // Been there, done that - if( def instanceof CallNode && cfg instanceof FunNode ) - return; // Ignore linked function calls - visit.set(cfg._nid); - for( Node useuse : cfg._outputs ) - _rpo_cfg(cfg,useuse,visit,rpo); - rpo.add(cfg); - } - private static void _schedEarly(Node n, BitSet visit) { if( n==null || visit.get(n._nid) ) return; // Been there, done that assert !(n instanceof CFGNode); @@ -113,26 +121,27 @@ public abstract class GlobalCodeMotion { for( Node def : n._inputs ) if( def!=null && !(def instanceof PhiNode) ) _schedEarly(def,visit); + // An existing edge 0 already supplies control (or a Phi/Proj binding). if( n.in(0)==null ) { // Schedule at deepest input - CFGNode early = Parser.START; // Maximally early, lowest idepth + CFGNode early = CodeGen.CODE._start; // Maximally early, lowest idepth for( int i=1; i early.idepth() ) early = n.in(i).cfg0(); // Latest/deepest input - n.setDef(0,early); // First place this can go + n.setDef(0,early); } } // ------------------------------------------------------------------------ - private static void schedLate( StopNode stop) { - CFGNode[] late = new CFGNode[Node.UID()]; - Node[] ns = new Node[Node.UID()]; + private static void schedLate( CodeGen code ) { + CFGNode[] late = new CFGNode[code.UID()]; + Node[] ns = new Node[code.UID()]; // Record Load NIDs at all their CFG block choices, then check against // Store block choices to force a Load above an anti-dependent Store. - int[] anti = new int[Node.UID()]; + int[] anti = new int[code.UID()]; // Breadth-first scheduling - breadth(stop,ns,late,anti); + breadth(code._stop,ns,late,anti); // Copy the best placement choice into the control slot for( int i=0; iearly, flagging Load's block location choices for( CFGNode cfg=lca; early!=null && cfg!=early.idom(); cfg = cfg.idom() ) @@ -251,7 +260,7 @@ public abstract class GlobalCodeMotion { // Walk load->mem uses, looking for Stores causing an anti-dep for( Node mem : load.antiDeps() ) { switch( mem ) { - case StoreNode st: + case MemOpNode st: assert late[st._nid]!=null; lca = anti_dep(load,late[st._nid],st.cfg0(),lca,st,anti); break; @@ -277,7 +286,7 @@ public abstract class GlobalCodeMotion { } // - private static CFGNode anti_dep( LoadNode load, CFGNode stblk, CFGNode defblk, CFGNode lca, Node st, int[] anti ) { + private static CFGNode anti_dep( MemOpNode load, CFGNode stblk, CFGNode defblk, CFGNode lca, Node st, int[] anti ) { for( ; stblk != defblk.idom(); stblk = stblk.idom() ) { // Store and Load overlap, need anti-dependence if( anti[stblk._nid]==load._nid ) { diff --git a/src/main/java/com/seaofnodes/simple/IRPrinter.java b/src/main/java/com/seaofnodes/simple/IRPrinter.java index c7ca8cf4..f0ac717a 100644 --- a/src/main/java/com/seaofnodes/simple/IRPrinter.java +++ b/src/main/java/com/seaofnodes/simple/IRPrinter.java @@ -35,10 +35,10 @@ public final class IRPrinter extends IRAdapter { if( n instanceof CProjNode p ) return p._idx; return n._nid; } - @Override public String functionName(Node n) { return n.label(); } + @Override public String functionName(Node n) { return ((FunNode)n)._name==null ? "" : ((FunNode)n)._name; } @Override public String signature(Node n) { FunNode fun=(FunNode)n; - return fun.sig().str(); + return (fun._name==null ? "" : fun._name)+" "+fun.sig().str(); } public static String prettyPrint(Node n, int depth) { return PRINT.prettyPrint(n,depth); } diff --git a/src/main/java/com/seaofnodes/simple/IterPeeps.java b/src/main/java/com/seaofnodes/simple/IterPeeps.java index 1792dada..087a990b 100644 --- a/src/main/java/com/seaofnodes/simple/IterPeeps.java +++ b/src/main/java/com/seaofnodes/simple/IterPeeps.java @@ -42,30 +42,28 @@ import java.util.Random; * progressOnList(stop);` * */ -public abstract class IterPeeps { +public class IterPeeps { - private static final WorkList WORK = new WorkList<>(); + private final WorkList _work; - public static N add( N n ) { - return (N)WORK.push(n); - } + IterPeeps( long seed ) { _work = new WorkList<>(seed); } - public static void addAll( Ary ary ) { - WORK.addAll(ary); - } + 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 static void iterate( StopNode stop) { - assert progressOnList(stop); + public void iterate( CodeGen code ) { + assert progressOnList(code); int cnt=0; Node n; - while( (n=WORK.pop()) != null ) { + while( (n=_work.pop()) != null ) { if( n.isDead() ) continue; cnt++; // Useful for debugging, searching which peephole broke things - var obs = CodeGen.CODE == null ? null : CodeGen.CODE._obs; + var obs = CodeGen.CODE._obs; if( obs != null ) obs.before(n); Node x = n.peepholeOpt(); if( x != null ) { @@ -78,20 +76,20 @@ public abstract class IterPeeps { // Changes require neighbors onto the worklist if( x != n || !(x instanceof ConstantNode) ) { // All outputs of n (changing node) not x (prior existing node). - for( Node z : n._outputs ) WORK.push(z); + for( Node z : n._outputs ) _work.push(z); // Everybody gets a free "go again" in case they didn't get // made in their final form. - WORK.push(x); + _work.push(x); // If the result is not self, revisit all inputs (because // there's a new user), and replace in the graph. if( x != n ) { - for( Node z : n. _inputs ) WORK.push(z); + for( Node z : n. _inputs ) _work.push(z); n.subsume(x); } } // If there are distant neighbors, move to worklist n.moveDepsToWorklist(); - assert progressOnList(stop); // Very expensive assert + assert progressOnList(code); // Very expensive assert } if( n.isUnused() && !(n instanceof StopNode) ) n.kill(); // Just plain dead @@ -101,7 +99,7 @@ public abstract class IterPeeps { } // Visit ALL nodes and confirm the invariant: - // Either you are on the WORK worklist OR running `iter()` makes no progress. + // Either you are on the _work worklist OR running `iter()` makes no progress. // This invariant ensures that no progress is missed, i.e., when the // worklist is empty we have indeed done all that can be done. To help @@ -112,30 +110,22 @@ public abstract class IterPeeps { // neighbors and these should fail, but will then try to add dependencies // {@link #Node.addDep} which is a side effect in an assert. The {@link // #midAssert} is used to stop this side effect. - private static boolean MID_ASSERT; - public static boolean midAssert() { return MID_ASSERT; } - private static boolean progressOnList(Node stop) { - MID_ASSERT = true; - int old_cnt = Node.ITER_CNT, old_nop = Node.ITER_NOP_CNT; - Node changed = stop.walk( n -> { + private boolean progressOnList(CodeGen code) { + code._midAssert = true; + Node changed = code._stop.walk( n -> { Node m = n; if( n.compute().isa(n._type) && (!n.iskeep() || n._nid<=6) ) { // Types must be forwards, even if on worklist - if( WORK.on(n) ) return null; + if( _work.on(n) ) return null; m = n.peepholeOpt(); if( m==null ) return null; } System.err.println("BREAK HERE FOR BUG"); return m; }); - Node.ITER_CNT = old_cnt; Node.ITER_NOP_CNT = old_nop; - MID_ASSERT = false; + code._midAssert = false; return changed==null; } - public static void reset() { - WORK.clear(); - } - /** * Classic WorkList, with a fast add/remove, dup removal, random pull. * The Node's nid is used to check membership in the worklist. diff --git a/src/main/java/com/seaofnodes/simple/ListScheduler.java b/src/main/java/com/seaofnodes/simple/ListScheduler.java index 252a8f8e..efca945a 100644 --- a/src/main/java/com/seaofnodes/simple/ListScheduler.java +++ b/src/main/java/com/seaofnodes/simple/ListScheduler.java @@ -7,91 +7,157 @@ import java.util.*; public abstract class ListScheduler { - private static final BitSet VISIT = new BitSet(); - private static int[] BCNTS; - public static void sched( CodeGen code ) { - assert VISIT.isEmpty(); - BCNTS = new int[Node.UID()]; - walk(code._start,VISIT); - VISIT.clear(); - } + // eXtra per-node stuff for scheduling. + private static final IdentityHashMap XS = new IdentityHashMap<>(); + private static class XSched { + static final Ary FREE = new Ary<>(XSched.class); + static void alloc(CFGNode bb, Node n) { + XSched x = FREE.pop(); + XS.put(n, (x==null ? new XSched() : x).init(bb,n)); + } + void free() { _n = null; FREE.push(this); } - // Visit all CFG nodes - static void walk( CFGNode cfg, BitSet visit ) { - if( visit.get(cfg._nid) ) return; - visit.set(cfg._nid); - local(cfg); - for( Node n : cfg._outputs ) - if( n instanceof CFGNode c ) - walk(c,visit); - } + static XSched get(Node n) { return n==null ? null : XS.get(n); } - // Local schedule this one CFG - static void local( CFGNode bb ) { - if( !bb.blockHead() || bb instanceof StopNode ) return; - Node[] uses = bb._outputs._es; - int ulen = bb._outputs._len; - - // Count block-locals - for( int i=0; i 0 || _rcnt > 0 ) return false; + if( _n instanceof MultiNode ) + for( Node use : _n._outputs ) + if( use instanceof ProjNode ) readyUp(use); + // anti-dep from a multireg directly + else _readyUp(use); + else + readyUp(_n); + return true; + } + private static void readyUp(Node n) { + for( Node use : n._outputs ) + _readyUp(use); + } + private static void _readyUp(Node use) { + XSched xs = get(use); + if( xs!=null && !(use instanceof PhiNode) ) + { assert xs._bcnt>0; xs._bcnt--; xs._rcnt++; } + } + + boolean decIsReady() { + assert _rcnt > 0; + _rcnt--; + return isReady(); } + Node _n; // Node this extra is for + int _bcnt; // Not-ready not-scheduled inputs + int _rcnt; // Ready not-scheduled inputs + boolean _ruse; // Node IS a "remote use", uses a other-block value + boolean _rdef; // Node IS a "remote def" of a value used in other block + boolean _single; // Defines a single register, likely to conflict + } + + + // List schedule every block + public static void sched( CodeGen code ) { + XS.clear(); + for( CFGNode bb : code._cfg ) + if( bb.blockHead() ) + local(bb); + } + + // Classic list scheduler + private static void local( CFGNode bb ) { + assert XS.isEmpty(); + int len = bb.nOuts(); + + // Count block-locals, remote inputs and outputs + for( int i=0; i 0; + for( int i=0; i 0; // Classic list scheduler. Behind sched is scheduled. Between sched // and ready have zero counts and are ready to schedule. Ahead of // ready is undefined. // - // As nodes are scheduled, the nodes they use decrement their _bcnt. Once + // As nodes are scheduled, the nodes they use decrement their bcnts[]. Once // that gets to 0, all the nodes using it are scheduled, so it's ready. int sched = 0; - while( sched < ulen ) { - assert sched < ready : "Scheduling nodes that are not ready. Probably due to duplicate nodes in the user list."; - int pick = best(uses, sched,ready); // Pick best - Node best = uses[pick]; // Swap best to head of ready - uses[pick] = uses[sched]; - uses[sched++] = best; // And move it into the scheduled set - // Lower ready count of users + while( sched < len ) { + assert sched < ready; + int pick = best(bb._outputs,sched,ready); // Pick best + Node best = bb._outputs.swap(pick,sched++); // Swap best to head of ready and move into the scheduled set + // Lower ready count of users, and since projections are ready as + // soon as their multi is ready, lower those now too. for( Node use : best._outputs ) - if( use instanceof ProjNode ) { + if( use instanceof ProjNode ) for( Node useuse : use._outputs ) ready = ready(bb,useuse,ready); - } else - ready = ready(bb, use,ready); + else + ready = ready(bb,use,ready); } + + // Reset for next block + for( XSched x : XS.values() ) + x.free(); + XS.clear(); } private static int ready(CFGNode bb, Node use, int ready) { - if( use != null && use.in(0)==bb && !(use instanceof PhiNode) ) { - assert BCNTS[use._nid] >= 1; - if( --BCNTS[use._nid] == 0 ) - bb._outputs._es[ready++] = use; // Became ready, move into ready set - } + if( use!=null && !(use instanceof PhiNode) && use.in(0)==bb && XSched.get(use).decIsReady() ) + bb._outputs.set(ready++, use); // Became ready, move into ready set + //bb._outputs.swap(ready++,use); // Became ready, move into ready set return ready; } // Pick best between sched and ready. - private static int best( Node[] uses, int sched, int ready ) { + private static int best( Ary blk, int sched, int ready ) { int pick = sched; - int score = score(uses[pick]); + int score = score(blk.at(pick)); for( int i=sched+1; i score ) { score = nscore; pick = i; } } @@ -99,12 +165,67 @@ public abstract class ListScheduler { } // Highest score wins. Max at 1000, min at 10, except specials. - private static int score( Node n ) { - if( n.isMultiTail() ) return 1001; // Pinned just behind the multi-head - if( n instanceof CFGNode ) return n instanceof ReturnNode ? 2 : 1; // Pinned at block exit + private static final int[] CNT = new int[3]; + static int score( Node n ) { + if( n instanceof ProjNode ) return 1001; // Pinned at block entry + if( n instanceof CProjNode ) return 1001; // Pinned at block entry + if( n instanceof PhiNode ) return 1000; + if( n instanceof CFGNode ) return 1; // Pinned at block exit + + int score = 500; + // Ideal nodes ignore register pressure scheduling + if( !(n instanceof MachNode) ) + return score; + + // Register pressure local scheduling: avoid overlapping specialized + // registers usages. + + // Subtract 10 if this op forces a live range to exist that cannot be + // resolved immediately; i.e., live count goes up. Scale to 20 if + // multi-def. + + // Subtract 100 if this op forces a single-def live range to exist + // which might conflict on the same register with other live ranges. + // Defines a single register based on def & uses, and the output is not + // ready. Scale to 200 for multi-def. + CNT[1]=CNT[2]=0; + XSched xn = XSched.get(n); + if( xn._rdef ) score = 200; // If defining a remote value, just generically stall alot + if( n instanceof MultiNode ) { + for( Node use : n._outputs ) + singleUseNotReady( use, xn._single ); + } else if( n.nOuts() == 1 ) + singleUseNotReady( n, xn._single ); + score += -10 * Math.min( CNT[1], 2 ); + score += -100 * Math.min( CNT[2], 2 ); + + // Add 10 if this op ends a single-def live range, add 20 for 2 or more. + // Scale to 100,200 if the single-def is also single-register. + CNT[1]=CNT[2]=0; + for( int i=1; i - * To make the compiler multithreaded, this field will have to move into a TLS. - */ - public static StartNode START; - public static ConstantNode ZERO; // Very common node, cached here public static ConstantNode NIL; // Very common node, cached here public static XCtrlNode XCTRL; // Very common node, cached here - // Next available memory alias number - static int ALIAS; - - public StopNode STOP; + // Compile driver + public final CodeGen _code; // The Lexer. Thin wrapper over a byte[] buffer with a cursor. private final Lexer _lexer; @@ -51,7 +41,7 @@ public class Parser { /** * List of keywords disallowed as identifiers */ - private static final HashSet KEYWORDS = new HashSet<>(){{ + public static final HashSet KEYWORDS = new HashSet<>(){{ add("bool"); add("break"); add("byte"); @@ -98,21 +88,14 @@ public class Parser { public final HashMap INITS; - public Parser(String source) { this(source, TypeInteger.BOT); } - - public Parser(String source, TypeInteger arg) { - Node.reset(); - IterPeeps.reset(); - Type.reset(); - _lexer = new Lexer(source); + public Parser(CodeGen code, TypeInteger arg) { + _code = code; + _lexer = new Lexer(code._src); _scope = new ScopeNode(); _continueScope = _breakScope = null; - START = new StartNode(arg); - STOP = new StopNode(source); ZERO = con(TypeInteger.ZERO).keep(); NIL = con(Type.NIL).keep(); XCTRL= new XCtrlNode().peephole().keep(); - ALIAS = 2; // alias 0 for the control, 1 for memory TYPES = defaultTypes(); INITS = new HashMap<>(); } @@ -142,12 +125,12 @@ public class Parser { } // Debugging utility to find a Node by index - public static Node find(int nid) { return START.find(nid); } + public Node f(int nid) { return _code.f(nid); } private Node ctrl() { return _scope.ctrl(); } private N ctrl(N n) { return _scope.ctrl(n); } - public StopNode parse() { + public void parse() { _xScopes.push(_scope); _scope.define(ScopeNode.CTRL, Type.CONTROL , false, null, _lexer); _scope.define(ScopeNode.MEM0, TypeMem.BOT , false, null, _lexer); @@ -157,7 +140,8 @@ public class Parser { _scope.mem(con(TypeMem.BOT)); // Parse whole program, as-if function header "{ int arg -> body }" - parseFunctionBody(TypeFunPtr.MAIN,loc(),"arg"); + parseFunctionBody(_code._main,loc(),"arg"); + _code.link(_code._main)._name = "main"; if( !_lexer.isEOF() ) throw _errorSyntax("unexpected"); @@ -167,8 +151,7 @@ public class Parser { for( StructNode init : INITS.values() ) init.unkeep().kill(); INITS.clear(); - STOP.peephole(); - return STOP; + _code._stop.peephole(); } /** @@ -186,24 +169,24 @@ public class Parser { ScopeNode breakScope = _breakScope; _breakScope = null; ScopeNode continueScope = _continueScope; _continueScope = null; - FunNode fun = _fun = (FunNode)peep(new FunNode(loc(),START,sig)); + FunNode fun = _fun = (FunNode)peep(new FunNode(loc(),sig,null,_code._start)); // Once the function header is available, install in linker table - // allowing recursive functions. Linker matches on declared args and // exact fidx, and ignores the return (because the fidx will only match // the exact single function). - CodeGen.CODE.link(fun); + _code.link(fun); Node rpc = new ParmNode("$rpc",0,TypeRPC.BOT,fun,con(TypeRPC.BOT)).peephole(); // Build a multi-exit return point for all function returns - RegionNode r = new RegionNode((Node)null,null).init(); + RegionNode r = new RegionNode((Lexer)null,null,null).init(); assert r.inProgress(); PhiNode rmem = new BulkMemPhiNode(ScopeNode.MEM0,r,null).init(); PhiNode rrez = new PhiNode(ScopeNode.ARG0,Type.BOTTOM,r,null).init(); ReturnNode ret = new ReturnNode(r, rmem, rrez, rpc, fun).init(); fun.setRet(ret); assert ret.inProgress(); - STOP.addDef(ret); + _code._stop.addDef(ret); // Pre-call the function from Start, with worse-case arguments. This // represents all the future, yet-to-be-parsed functions calls and @@ -398,7 +381,7 @@ public class Parser { // Our current scope is the body Scope ctrl(ifT.unkeep()); // set ctrl token to ifTrue projection _scope.addGuards(ifT,pred.unkeep(),false); // Up-cast predicate - Node expr = parseStatement().keep(); // Parse loop body + parseStatement(); // Parse loop body _scope.removeGuards(ifT); // Merge the loop bottom into other continue statements @@ -438,7 +421,7 @@ public class Parser { _xScopes.pop(); _xScopes.push(exit); _scope = exit; - return peep(expr.unkeep()); + return ZERO; } private ScopeNode jumpTo(ScopeNode toScope) { @@ -696,7 +679,6 @@ public class Parser { boolean xfinal = bindingFinal(t); Lexer loc = loc(); String name = requireId(); - // Optional initializing expression follows Node expr; if( match("=") ) { @@ -709,7 +691,7 @@ public class Parser { if( !xfinal ) t = inferredType(t); // Widen if not final } if( t instanceof TypeFunPtr && expr._type instanceof TypeFunPtr tfp && tfp.isConstant() ) - tfp.setName(name); + _code.link(tfp)._name = name; } else { // Need an expression to infer the type. @@ -774,7 +756,7 @@ public class Parser { s.addDef(_scope.in(i)); Var v = _scope._vars.at(i); if( !v.isFRef() ) // Promote to outer scope, not defined here - fs.push(Field.make(v._name,v.type(),ALIAS++,v._final)); + fs.push(Field.make(v._name,v.type(),_code.getALIAS(),v._final)); } TypeStruct ts = s._ts = TypeStruct.make(typeName, fs.asAry()); TYPES.put(typeName, TypeMemPtr.make(ts)); @@ -842,7 +824,7 @@ public class Parser { int old2 = pos(); if( !match("!") ) match("~"); String id = _lexer.matchId(); - if( !(peek(',') || peek(';')) ) + if( !(peek(',') || peek(';') || match("->")) ) return posT(old1); pos(old2); // Reset lexer to reparse if( id==null ) @@ -865,10 +847,10 @@ public class Parser { if( t instanceof TypeMemPtr ptr && (ptr._obj._fields==null || !ptr._obj.isAry()) && ary._fields[1]._type instanceof TypeMemPtr old ) t = old.withAccess(ptr._ro); - return TypeMemPtr.make(TypeStruct.makeAry(TypeInteger.BOT,ary._fields[0]._alias,t,ary._fields[1]._alias)); + return TypeMemPtr.make(TypeStruct.makeAry(TypeInteger.U32,ary._fields[0]._alias,t,ary._fields[1]._alias)); } // Need make an array type. - TypeStruct ts = TypeStruct.makeAry(TypeInteger.BOT,ALIAS++,t,ALIAS++); + TypeStruct ts = TypeStruct.makeAry(TypeInteger.U32,_code.getALIAS(),t,_code.getALIAS()); assert ts.str().equals(tname); TypeMemPtr tary = TypeMemPtr.make(ts); TYPES.put(tname,tary); @@ -1225,14 +1207,13 @@ public class Parser { if( fs==null ) throw error("Unknown struct type '" + obj._name + "'"); Node prior = mem().keep(); - MemMergeNode input = new MemMergeNode(null); + MemMergeNode input = new MemMergeNode((Node)null); for( Field f : fs ) input.alias(f._alias,prior); - // Constructor values follow control, memory, and allocation size. - Node[] ns = new Node[3+fs.length]; + // Allocation zeroes its fields; nonzero initializers become explicit Stores. + Node[] ns = new Node[3]; ns[0] = ctrl(); ns[1] = input.peephole(); ns[2] = size; - for( int i=0; i"); // Make a concrete function type, with a fidx - TypeFunPtr tfp = TypeFunPtr.makeFun(TypeTuple.make(ts.asAry()),Type.BOTTOM); + TypeFunPtr tfp = _code.makeFun(TypeTuple.make(ts.asAry()),Type.BOTTOM); ReturnNode ret = parseFunctionBody(tfp,loc,finals,ids.asAry()); return con(ret._fun.sig()); } @@ -1427,6 +1417,14 @@ public class Parser { // Unkeep them all for( Node arg : args ) arg.unkeep(); + // Dead into the call? Skip all the node gen + if( ctrl()._type == Type.XCONTROL ) { + for( Node arg : args ) + if( arg.isUnused() ) + arg.kill(); + return con(Type.TOP); + } + // Into the call CallNode call = (CallNode)new CallNode(loc(), args.asAry()).peephole(); @@ -1680,7 +1678,6 @@ public class Parser { private String parseNumberString() { int old = _position; int len = Math.abs(isLongOrDouble()); - _position += len; return new String(_input,old,len); } diff --git a/src/main/java/com/seaofnodes/simple/RegMask.java b/src/main/java/com/seaofnodes/simple/RegMask.java new file mode 100644 index 00000000..073b472b --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/RegMask.java @@ -0,0 +1,47 @@ +package com.seaofnodes.simple; + +import java.util.BitSet; + +// A "register mask" - 1 bit set for each allowed register. In addition "stack +// slot" registers may be allowed, effectively making the set infinite. +public class RegMask { + + public static final RegMask EMPTY = new RegMask(); + + final BitSet _bits; + + public RegMask(long x ) { _bits = BitSet.valueOf(new long[]{x}); } + public RegMask(long[] xs) { _bits = BitSet.valueOf( xs); } + + public RegMask or(RegMask other) { + BitSet resultBits = (BitSet)this._bits.clone(); + resultBits.or(other._bits); + return new RegMask(resultBits); + } + + // Internal constructor + RegMask() { _bits = new BitSet(); } + RegMask(BitSet bs) { _bits = bs; } + + public boolean get(int r) { return _bits.get(r); } + + // Defensive writable copy + public RegMaskRW copy() { return new RegMaskRW( (BitSet)_bits.clone() ); } + + boolean size1() { return _bits.cardinality()==1; } +} + +class RegMaskRW extends RegMask { + public RegMaskRW() { super(); } + public RegMaskRW(long x) { super(new long[]{x}); } + public RegMaskRW(long[] xs) { super(xs); } + public RegMaskRW(BitSet bs) { super(bs); } + public void set(int r) { _bits.set(r); } + public void clr(int r) { _bits.clear(r); } + public void set(int r, boolean b) { _bits.set(r,b); } + public RegMaskRW and( RegMask r ) { + if( r==null ) return this; + _bits.and(r._bits); + return this; + } +} diff --git a/src/main/java/com/seaofnodes/simple/SB.java b/src/main/java/com/seaofnodes/simple/SB.java index 6f5a29ee..7d580c21 100644 --- a/src/main/java/com/seaofnodes/simple/SB.java +++ b/src/main/java/com/seaofnodes/simple/SB.java @@ -29,6 +29,23 @@ public final class SB { public SB p( int s ) { _sb.append(s); return this; } public SB p( long s ) { _sb.append(s); return this; } public SB p( boolean s) { _sb.append(s); return this; } + // 4 hex digits + public SB hex4(int s) { + assert (s>>4*4)==0; // Fits in 16 bits + for( int i=0; i<4; i++ ) { + int digit = (s>>((3-i)*4)) & 0xf; + _sb.append((char)((digit <= 9 ? '0' : ('A'-10))+digit)); + } + return this; + } + // Fixed width field + public SB fix( int sz, String s ) { + assert sz >=0; + for( int i=0; i i2._max ) return FALSE; @@ -87,7 +86,7 @@ abstract public class BoolNode extends Node { } public static class LT extends BoolNode { public LT(Node lhs, Node rhs) { super(lhs,rhs); } - String op() { return "<" ; } + public String op() { return "<" ; } TypeInteger doOp(TypeInteger i1, TypeInteger i2) { if( i1._max < i2._min ) return TRUE; if( i1._min >= i2._max ) return FALSE; @@ -98,7 +97,7 @@ abstract public class BoolNode extends Node { } public static class LE extends BoolNode { public LE(Node lhs, Node rhs) { super(lhs,rhs); } - String op() { return "<="; } + public String op() { return "<="; } TypeInteger doOp(TypeInteger i1, TypeInteger i2) { if( i1._max <= i2._min ) return TRUE; if( i1._min > i2._max ) return FALSE; @@ -112,4 +111,15 @@ abstract public class BoolNode extends Node { public static class LTF extends LT { public LTF(Node lhs, Node rhs) { super(lhs,rhs); } boolean doOp(double lhs, double rhs) { return lhs < rhs; } } public static class LEF extends LE { public LEF(Node lhs, Node rhs) { super(lhs,rhs); } boolean doOp(double lhs, double rhs) { return lhs <= rhs; } } + // Unsigned less that, for range checks. Not directly user writable. + public static class ULT extends BoolNode { + public ULT(Node lhs, Node rhs) { super(lhs,rhs); } + public String op() { return "u<" ; } + TypeInteger doOp(TypeInteger i1, TypeInteger i2) { + if( Long.compareUnsigned(i1._max,i2._min) < 0 ) return TRUE; + if( Long.compareUnsigned(i1._min,i2._max) >= 0 ) return FALSE; + return BOOL; + } + Node copy(Node lhs, Node rhs) { return new ULT(lhs,rhs); } + } } diff --git a/src/main/java/com/seaofnodes/simple/node/BulkMemPhiNode.java b/src/main/java/com/seaofnodes/simple/node/BulkMemPhiNode.java index de5f8b4c..5d8a4aba 100644 --- a/src/main/java/com/seaofnodes/simple/node/BulkMemPhiNode.java +++ b/src/main/java/com/seaofnodes/simple/node/BulkMemPhiNode.java @@ -1,6 +1,6 @@ package com.seaofnodes.simple.node; -import com.seaofnodes.simple.IterPeeps; +import com.seaofnodes.simple.CodeGen; import com.seaofnodes.simple.type.Type; import com.seaofnodes.simple.type.TypeMem; import java.util.BitSet; @@ -29,6 +29,8 @@ public class BulkMemPhiNode extends PhiNode { public boolean isSplit(int alias) { return _aliases.get(alias); } + public BulkMemPhiNode(BulkMemPhiNode phi) { super(phi); _aliases = phi._aliases; } + @Override public String label() { return "BulkPhi"+_aliases; } @Override @@ -49,7 +51,7 @@ public class BulkMemPhiNode extends PhiNode { // "Peek through" a MemMerge that covers this alias set on its default for( int i=1; i @@ -22,7 +22,14 @@ import java.util.HashMap; */ public abstract class CFGNode extends Node { - public CFGNode(Node... nodes) { super(nodes); } + public CFGNode(Node... nodes) { super(nodes); } + public CFGNode(CFGNode cfg) { + super(cfg); + _idepth = cfg._idepth; + _idepthVersion = cfg._idepthVersion; + _ltree = cfg._ltree; + _pre = cfg._pre; + } public CFGNode cfg(int idx) { return (CFGNode)in(idx); } @@ -118,8 +125,8 @@ public abstract class CFGNode extends Node { } // Pre-walk for( Node use : _outputs ) - if( use instanceof CFGNode usecfg && !skip(usecfg) ) - pre = usecfg._bltWalk(pre,use instanceof FunNode fuse ? fuse : fun,stop,post); + if( use instanceof CFGNode usecfg && !skip( usecfg ) ) + pre = usecfg._bltWalk( pre, use instanceof FunNode fuse ? fuse : fun, stop, post ); // Post-order work: find innermost loop LoopTree inner = null, ltree; diff --git a/src/main/java/com/seaofnodes/simple/node/CProjNode.java b/src/main/java/com/seaofnodes/simple/node/CProjNode.java index 70238f8c..c338ff4c 100644 --- a/src/main/java/com/seaofnodes/simple/node/CProjNode.java +++ b/src/main/java/com/seaofnodes/simple/node/CProjNode.java @@ -7,7 +7,7 @@ import com.seaofnodes.simple.type.Type; import com.seaofnodes.simple.type.TypeMem; import com.seaofnodes.simple.type.TypeTuple; -public class CProjNode extends CFGNode implements MultiUse { +public class CProjNode extends CFGNode { // Which slice of the incoming multipart value public final int _idx; @@ -20,9 +20,9 @@ public class CProjNode extends CFGNode implements MultiUse { _idx = idx; _label = label; } + public CProjNode(CProjNode c) { super(c); _idx = c._idx; _label = c._label; } @Override public String label() { return _label; } - @Override public int idx() { return _idx; } @Override protected ExprPrinter _print1(ExprPrinter p) { return p.p(_label); } diff --git a/src/main/java/com/seaofnodes/simple/node/CallEndNode.java b/src/main/java/com/seaofnodes/simple/node/CallEndNode.java index 2da09cfc..83c98b97 100644 --- a/src/main/java/com/seaofnodes/simple/node/CallEndNode.java +++ b/src/main/java/com/seaofnodes/simple/node/CallEndNode.java @@ -2,10 +2,7 @@ package com.seaofnodes.simple.node; import com.seaofnodes.print.ExprPrinter; -import com.seaofnodes.simple.CodeGen; -import com.seaofnodes.simple.IterPeeps; -import com.seaofnodes.simple.Parser; -import com.seaofnodes.simple.Utils; +import com.seaofnodes.simple.*; import com.seaofnodes.simple.type.*; /** @@ -17,14 +14,15 @@ public class CallEndNode extends CFGNode implements MultiNode { private boolean _folding; public boolean folding() { return _folding; } - public CallEndNode(CallNode call) { super(call); } + public CallEndNode(CallNode call) { super(new Node[]{call}); } + public CallEndNode(CallEndNode cend) { super(cend); } @Override public String label() { return "CallEnd"; } @Override public boolean isMultiHead() { return true; } @Override public boolean blockHead() { return true; } - CallNode call() { return (CallNode)in(0); } + public CallNode call() { return (CallNode)in(0); } @Override protected ExprPrinter _print1(ExprPrinter p) { @@ -48,8 +46,7 @@ public class CallEndNode extends CFGNode implements MultiNode { // Trivial inlining: call site calls a single function; single function // is only called by this call site. - if( !_folding && nIns()==2 ) { - CallNode call = call(); + if( !_folding && nIns()==2 && in(0) instanceof CallNode call ) { Node fptr = call.fptr(); if( fptr.nOuts() == 1 && // Only user is this call fptr instanceof ConstantNode && // We have an immediate call @@ -74,7 +71,7 @@ public class CallEndNode extends CFGNode implements MultiNode { fun.setDef(1,Parser.XCTRL); // No default/unknown StartNode caller fun.setDef(2,call.ctrl()); // Bypass the Call; fun.ret().setDef(3,null); // Return is folding also - IterPeeps.addAll(fun._outputs); + CodeGen.CODE.addAll(fun._outputs); CodeGen.CODE.invalidateIDepthCaches(); return this; } diff --git a/src/main/java/com/seaofnodes/simple/node/CallNode.java b/src/main/java/com/seaofnodes/simple/node/CallNode.java index 10b04b38..22cef22d 100644 --- a/src/main/java/com/seaofnodes/simple/node/CallNode.java +++ b/src/main/java/com/seaofnodes/simple/node/CallNode.java @@ -18,23 +18,29 @@ public class CallNode extends CFGNode { public final Parser.Lexer _loc; public CallNode(Parser.Lexer loc, Node... nodes) { super(nodes); _loc = loc; } + public CallNode(CallNode call) { super(call); _loc = call._loc; } @Override public String label() { return "Call"; } - @Override - protected ExprPrinter _print1(ExprPrinter p) { - String fname = null; - Node fptr = fptr(); - if( fptr._type instanceof TypeFunPtr tfp && tfp.isConstant() ) - fname = tfp._name; - if( fname==null ) p.n(fptr); + @Override protected ExprPrinter _print1(ExprPrinter p) { + String fname = name(); + if( fname == null ) p.n(fptr()); else p.p(fname); p.p("( "); for( int i=2; i * The Constant's value is the value stored in it. */ + public class ConstantNode extends Node { public final Type _con; public ConstantNode( Type type ) { - super(Parser.START); - _con = type; + super(new Node[]{CodeGen.CODE._start}); + _con = _type = type; } + public ConstantNode( ConstantNode con ) { super(con); _con = con._type; } public static Node make( Type type ) { if( type==Type. CONTROL ) return new CtrlNode(); @@ -38,7 +41,13 @@ public class ConstantNode extends Node { @Override public String label() { return "Con"; } - @Override protected ExprPrinter _print1(ExprPrinter p) { + @Override + protected ExprPrinter _print1(ExprPrinter p) { + if( _con instanceof TypeFunPtr tfp && tfp.isConstant() && tfp.notNull() ) { + FunNode fun = CodeGen.CODE.link(tfp); + if( fun!=null && fun._name != null ) + return p.p("{ ").p(fun._name).p("}"); + } return p.p(_con.print(new SB())); } diff --git a/src/main/java/com/seaofnodes/simple/node/CtrlNode.java b/src/main/java/com/seaofnodes/simple/node/CtrlNode.java index 9268381a..e647a68a 100644 --- a/src/main/java/com/seaofnodes/simple/node/CtrlNode.java +++ b/src/main/java/com/seaofnodes/simple/node/CtrlNode.java @@ -2,11 +2,12 @@ package com.seaofnodes.simple.node; import com.seaofnodes.print.ExprPrinter; +import com.seaofnodes.simple.CodeGen; import com.seaofnodes.simple.Parser; import com.seaofnodes.simple.type.Type; public class CtrlNode extends CFGNode { - public CtrlNode() { super(Parser.START); } + public CtrlNode() { super(CodeGen.CODE._start); } @Override public String label() { return "Ctrl"; } @Override protected ExprPrinter _print1(ExprPrinter p) { return p.p("Cctrl"); } @Override public boolean isConst() { return true; } diff --git a/src/main/java/com/seaofnodes/simple/node/FunNode.java b/src/main/java/com/seaofnodes/simple/node/FunNode.java index ec60f215..d24f4829 100644 --- a/src/main/java/com/seaofnodes/simple/node/FunNode.java +++ b/src/main/java/com/seaofnodes/simple/node/FunNode.java @@ -4,6 +4,7 @@ import com.seaofnodes.simple.*; import com.seaofnodes.simple.type.Type; import com.seaofnodes.simple.type.TypeFunPtr; import java.util.BitSet; +import static com.seaofnodes.simple.CodeGen.CODE; public class FunNode extends RegionNode { @@ -14,10 +15,13 @@ public class FunNode extends RegionNode { private TypeFunPtr _sig; // Initial signature private ReturnNode _ret; // Return pointer - public FunNode( Parser.Lexer loc, StartNode start, TypeFunPtr sig ) { super(loc,null,start); _sig=sig; } + public String _name; // Debug name + + public FunNode( Parser.Lexer loc, TypeFunPtr sig, Node... nodes ) { super(loc,nodes); _sig = sig; } + public FunNode( FunNode fun ) { super( fun, fun._loc ); _sig = fun.sig(); _name = fun._name; } @Override - public String label() { return _sig._name == null ? "$fun" : _sig._name; } + public String label() { return _name == null ? "$fun" : _name; } // Find the one CFG user from Fun. It's not always the Return, but always // the Return *is* a CFG user of Fun. @@ -46,9 +50,10 @@ public class FunNode extends RegionNode { public TypeFunPtr sig() { return _sig; } void setSig( TypeFunPtr sig ) { assert sig.isa(_sig); - if( _sig != sig ) - IterPeeps.add(this); - _sig = sig; + if( _sig != sig ) { + CODE.add(this); + _sig = sig; + } } @Override @@ -59,6 +64,15 @@ public class FunNode extends RegionNode { @Override public Node idealize() { + + // Some linked path dies + Node progress = deadPath(); + if( progress!=null ) { + if( nIns()==3 && in(2) instanceof CallNode call ) + CODE.add(call.cend()); // If Start and one call, check for inline + return progress; + } + // When can we assume no callers? Or no other callers (except main)? // In a partial compilation, we assume Start gets access to any/all // top-level public structures and recursively what they point to. @@ -67,16 +81,18 @@ public class FunNode extends RegionNode { // In a total compilation, we can start from Start and keep things // more contained. - // If no default/unknown caller, use the normal RegionNode ideal rules // to collapse if( unknownCallers() ) return null; - Node progress = super.idealize(); - if( progress!=null ) { - IterPeeps.add( CodeGen.CODE._stop); - IterPeeps.add( _ret ); + + // If down to a single input, become that input + if( nIns()==2 && !hasPhi() ) { + CODE.add( CODE._stop ); // Stop will remove dead path + CODE.add( _ret ); // Return will compute to TOP control + return in(1); // Collapse if no Phis; 1-input Phis will collapse on their own } - return progress; + + return null; } // Unfolded functions are roots; inlining reconnects their depth to the caller. diff --git a/src/main/java/com/seaofnodes/simple/node/IfNode.java b/src/main/java/com/seaofnodes/simple/node/IfNode.java index a3061b63..a01eb8bd 100644 --- a/src/main/java/com/seaofnodes/simple/node/IfNode.java +++ b/src/main/java/com/seaofnodes/simple/node/IfNode.java @@ -1,6 +1,7 @@ package com.seaofnodes.simple.node; +import com.seaofnodes.simple.CodeGen; import com.seaofnodes.simple.IterPeeps; import com.seaofnodes.simple.type.*; import java.util.HashSet; @@ -9,8 +10,9 @@ public class IfNode extends CFGNode implements MultiNode { public IfNode(Node ctrl, Node pred) { super(ctrl, pred); - IterPeeps.add(this); // Because idoms are complex, just add it + CodeGen.CODE.add(this); // Because idoms are complex, just add it } + public IfNode(IfNode iff) { super(iff); } @Override public String label() { return "If"; } @@ -54,7 +56,7 @@ public class IfNode extends CFGNode implements MultiNode { if( !pred()._type.isHighOrConst() ) for( CFGNode dom = idom(), prior=this; dom!=null; prior = dom, dom = dom.idom() ) if( dom.addDep(this) instanceof IfNode iff && iff.pred().addDep(this)==pred() && prior instanceof CProjNode prj ) { - setDef(1,new ConstantNode(TypeInteger.constant(prj._idx==0?1:0)).peephole()); + setDef(1,con( prj._idx==0 ? 1 : 0 )); return this; } return null; diff --git a/src/main/java/com/seaofnodes/simple/node/LoadNode.java b/src/main/java/com/seaofnodes/simple/node/LoadNode.java index 5190bf08..a06d1291 100644 --- a/src/main/java/com/seaofnodes/simple/node/LoadNode.java +++ b/src/main/java/com/seaofnodes/simple/node/LoadNode.java @@ -2,12 +2,10 @@ package com.seaofnodes.simple.node; import com.seaofnodes.print.ExprPrinter; +import com.seaofnodes.simple.CodeGen; import com.seaofnodes.simple.Parser; import com.seaofnodes.simple.Utils; -import com.seaofnodes.simple.IterPeeps; import com.seaofnodes.simple.type.*; -import java.util.BitSet; -import java.util.ArrayList; /** * Load represents extracting a value from inside a memory object, @@ -65,29 +63,36 @@ public class LoadNode extends MemOpNode { @Override public Node idealize() { if( mem() instanceof MemMergeNode merge ) { - setDef(1,IterPeeps.add(merge.alias(_alias))); + setDef(1,CodeGen.CODE.add(merge.alias(_alias))); return this; } Node ptr = ptr(); + Node mem = mem(); // Simple Load-after-Store on same address. if( mem() instanceof StoreNode st && _alias==st._alias && ptr == st.ptr() && off() == st.off() ) { // Must check same object assert _name.equals(st._name); // Equiv class aliasing is perfect - return castRO(st.val()); + return extend(st.val()); } // Simple Load-after-New on same address. - if( mem() instanceof ProjNode p && p.in(0) instanceof NewNode nnn && + if( mem instanceof ProjNode p && p.in(0) instanceof NewNode nnn && ptr == nnn.proj(0) ) // Must check same object - return castRO(nnn.in(nnn.findAlias(_alias))); // Load from New init + return zero(nnn); // Load zero from new + + // Uplift control to a prior dominating load. + for( Node memuse : mem._outputs ) + // Find a prior load, has same mem,ptr,off but higher ctrl + if( memuse != this && memuse instanceof LoadNode ld && ptr==ld.ptr() && off()==ld.off() && + cfg0()!=null && cfg0().domLCA(ld.cfg0(),this) == ld.cfg0() ) // Higher control means load is legal earlier + return ld; // Load-after-Store on same address, but bypassing provably unrelated // stores. This is a more complex superset of the above two peeps. // "Provably unrelated" is really weak. if( ptr instanceof ReadOnlyNode ro ) ptr = ro.in(1); - Node mem = mem(); outer: while( true ) { mem.addDep(this); @@ -97,7 +102,7 @@ public class LoadNode extends MemOpNode { break; case StoreNode st: if( _alias==st._alias && ptr == st.ptr().addDep(this) && off() == st.off() ) - return castRO(st.val()); // Proved equal + return extend(castRO(st.val())); // Proved equal // Can we prove unequal? Offsets do not overlap? if( _alias==st._alias && !off()._type.join(st.off()._type).isHigh() && // Offsets overlap !neverAlias(ptr,st.ptr()) ) // And might alias @@ -114,8 +119,8 @@ public class LoadNode extends MemOpNode { switch( mproj.in(0) ) { case NewNode nnn1: if( ptr instanceof ProjNode pproj && pproj.in(0) == mproj.in(0) ) - return castRO(nnn1.in(nnn1.findAlias(_alias))); // Load from New init - if( !(ptr instanceof ProjNode pproj && pproj.in(0) instanceof NewNode nnn2) ) + return zero(nnn1); + if( !(ptr instanceof ProjNode pproj && pproj.in(0) instanceof NewNode) ) break outer; // Cannot tell, ptr not related to New mem = nnn1.mem();// Bypass unrelated New break; @@ -156,6 +161,14 @@ public class LoadNode extends MemOpNode { return null; } + // Load a flavored zero from a New + private Node zero(NewNode nnn) { + TypeStruct ts = nnn._ptr._obj; + Type zero = ts._fields[ts.findAlias(_alias)]._type.makeZero(); + assert zero.isa(_type); // Catch an uninitialized non-null field + return castRO(new ConstantNode(zero).peephole()); + } + private Node ld( int idx ) { Node mem = mem(), ptr = ptr(); LoadNode ld = new LoadNode(_loc,_name,_alias,_declaredType,mem.in(idx),ptr instanceof PhiNode && ptr.in(0)==mem.in(0) ? ptr.in(idx) : ptr,off()); @@ -206,29 +219,19 @@ public class LoadNode extends MemOpNode { return rez; } - // Memory writers which must follow this read. A packaging node is not a - // write, but can hide New's input, so follow this alias through aggregates. - public ArrayList antiDeps() { - ArrayList deps = new ArrayList<>(); - antiDeps(mem(),deps,new BitSet()); - return deps; + // When a load bypasses a store, the store might truncate bits - and the + // load will need to zero/sign-extend. + private Node extend(Node val) { + if( !(_declaredType instanceof TypeInteger ti) ) return val; + if( ti._min==0 ) // Unsigned + return new AndNode(null,val,con(ti._max)); + // Signed extension + int shift = Long.numberOfLeadingZeros(ti._max)-1; + if( shift==0 ) + return val; + Node shf = con(shift); + Node shl = new ShlNode(null,val,shf.keep()).peephole(); + return new SarNode(null,shl,shf.unkeep()); } - private void antiDeps(Node mem, ArrayList deps, BitSet visit) { - if( visit.get(mem._nid) ) return; - visit.set(mem._nid); - for( Node use : mem._outputs ) { - if( use instanceof MemMergeNode merge ) { - Node slice = _alias _print1(ExprPrinter p) { + p.open().p(op()).p(","); + for( int i=1; i=nIns() || in(i)==null) ) alias(i,mem.in(i)); - IterPeeps.add(mem.in(1)); + CodeGen.CODE.add(mem.in(1)); setDef(1,mem.in(1)); return this; } diff --git a/src/main/java/com/seaofnodes/simple/node/MemOpNode.java b/src/main/java/com/seaofnodes/simple/node/MemOpNode.java index c09f2725..e9fccc74 100644 --- a/src/main/java/com/seaofnodes/simple/node/MemOpNode.java +++ b/src/main/java/com/seaofnodes/simple/node/MemOpNode.java @@ -1,9 +1,14 @@ package com.seaofnodes.simple.node; +import com.seaofnodes.print.ExprPrinter; + import com.seaofnodes.simple.Parser; import com.seaofnodes.simple.Utils; import com.seaofnodes.simple.type.Type; import com.seaofnodes.simple.type.TypeMemPtr; +import java.lang.StringBuilder; +import java.util.BitSet; +import java.util.ArrayList; /** * Convenience common base for Load and Store. @@ -29,6 +34,13 @@ public abstract class MemOpNode extends Node { this(loc,name, alias, glb, mem, ptr, off); addDef(value); } + public MemOpNode( Node mach, MemOpNode mop ) { + super(mach); + _name = mop._name; + _alias = mop._alias; + _declaredType = mop._declaredType; + _loc = mop._loc; + } // static String mlabel(String name) { return name.equals("[]") ? "ary" : (name.equals("#") ? "len" : name); } @@ -38,6 +50,10 @@ public abstract class MemOpNode extends Node { public Node ptr() { return in(2); } public Node off() { return in(3); } + @Override protected ExprPrinter _print1(ExprPrinter p) { return _printMach(p); } + protected ExprPrinter _printMach(ExprPrinter p) { throw Utils.TODO(); } + + // Extra conditions for factoring matching memory operations through a Phi. // The caller has already checked the opcode, input shape and control. boolean canDrop(MemOpNode other, Node dep) { @@ -65,4 +81,29 @@ public abstract class MemOpNode extends Node { return null; return Parser.error( "Might be null accessing '" + _name + "'",_loc); } + // Memory writers which must follow this read. A packaging node is not a + // write, but can hide New's input, so follow this alias through aggregates. + public ArrayList antiDeps() { + ArrayList deps = new ArrayList<>(); + antiDeps(mem(),deps,new BitSet()); + return deps; + } + + private void antiDeps(Node mem, ArrayList deps, BitSet visit) { + if( visit.get(mem._nid) ) return; + visit.set(mem._nid); + for( Node use : mem._outputs ) { + if( use instanceof MemMergeNode merge ) { + Node slice = _alias _print1(ExprPrinter p) { return p.p("new ").p(_ptr._obj.str()); } - // Find matching alias input - int findAlias(int alias) { - return 3+_ptr._obj.findAlias(alias); - } - @Override public TypeTuple compute() { diff --git a/src/main/java/com/seaofnodes/simple/node/Node.java b/src/main/java/com/seaofnodes/simple/node/Node.java index 114a652a..dd37b051 100644 --- a/src/main/java/com/seaofnodes/simple/node/Node.java +++ b/src/main/java/com/seaofnodes/simple/node/Node.java @@ -3,12 +3,13 @@ package com.seaofnodes.simple.node; import com.seaofnodes.print.BaseNode; import com.seaofnodes.simple.CodeGen; - import com.seaofnodes.simple.*; import com.seaofnodes.simple.type.Type; import com.seaofnodes.simple.type.TypeFloat; +import com.seaofnodes.simple.type.TypeInteger; import java.util.*; import java.util.function.Function; +import static com.seaofnodes.simple.CodeGen.CODE; /** * All Nodes in the Sea of Nodes IR inherit from the Node class. @@ -43,16 +44,8 @@ public abstract class Node extends BaseNode implements Cloneable { */ public Type _type; - /** - * A private Global Static mutable counter, for unique node id generation. - * To make the compiler multithreaded, this field will have to move into a TLS. - * Starting with value 1, to avoid bugs confusing node ID 0 with uninitialized values. - * */ - private static int UNIQUE_ID = 1; - public static int UID() { return UNIQUE_ID; } - - protected Node(Node... inputs) { - super(UNIQUE_ID++); // allocate unique dense ID + Node(Node... inputs) { + super(CODE.getUID()); // allocate unique dense ID _inputs = new Ary<>(Node.class); Collections.addAll(_inputs,inputs); _outputs = new Ary<>(Node.class); @@ -61,6 +54,18 @@ public abstract class Node extends BaseNode implements Cloneable { n.addUse( this ); } + // Make a Node using the existing arrays of nodes. + // Used by any pass rewriting all Node classes but not the edges. + Node( Node n ) { + super(CODE.getUID()); // allocate unique dense ID + assert CodeGen.CODE._phase == CodeGen.Phase.InstructionSelection; + _inputs = new Ary<>(n._inputs.asAry()); + _outputs = new Ary<>(Node.class); + _type = n._type; + _deps = null; + _hash = 0; + } + // Easy reading label for debugger, e.g. "Add" or "Region" or "EQ" public abstract String label(); @@ -119,10 +124,10 @@ public abstract class Node extends BaseNode implements Cloneable { new_def.addUse(this); // Set the new_def over the old (killed) edge _inputs.set(idx,new_def); - if( old_def != null ) { // If the old def exists, remove a def->use edge - if( old_def.delUse(this) ) // If we removed the last use, the old def is now dead - old_def.kill(); // Kill old def - else IterPeeps.add(old_def);// Else old lost a use, so onto worklist + if( old_def != null ) { // If the old def exists, remove a def->use edge + if( old_def.delUse(this) ) // If we removed the last use, the old def is now dead + old_def.kill(); // Kill old def + else CODE.add(old_def); // Else old lost a use, so onto worklist } moveDepsToWorklist(); // Return new_def for easy flow-coding @@ -202,11 +207,9 @@ public abstract class Node extends BaseNode implements Cloneable { _type=null; // Flag as dead while( nIns()>0 ) { // Set all inputs to null, recursively killing unused Nodes Node old_def = _inputs.removeLast(); - if( old_def != null ) { - IterPeeps.add(old_def);// Revisit neighbor because removed use - if( old_def.delUse(this) ) // If we removed the last use, the old def is now dead - old_def.kill(); // Kill old def - } + // Revisit neighbor because removed use + if( old_def != null && CODE.add(old_def).delUse(this) ) + old_def.kill(); // If we removed the last use, the old def is now dead } assert isDead(); // Really dead now } @@ -236,7 +239,7 @@ public abstract class Node extends BaseNode implements Cloneable { n._inputs.set(idx,nnn); n.moveDepsToWorklist(); // Rewiring can change a dependent query without changing type. nnn.addUse(n); - IterPeeps.addAll(n._outputs); + CODE.addAll(n._outputs); } kill(); } @@ -249,7 +252,7 @@ public abstract class Node extends BaseNode implements Cloneable { * Always returns some not-null Node (often this). */ public final Node peephole() { - var obs = CodeGen.CODE == null || IterPeeps.midAssert() || CodeGen.CODE._phase == CodeGen.Phase.Parse && CodeGen.CODE.P == null ? null : CodeGen.CODE._obs; + var obs = CodeGen.CODE._midAssert ? null : CodeGen.CODE._obs; if( obs != null ) obs.before(this); Node n = peepholeOpt(); Node rez = n == null ? this : deadCodeElim(n._nid >= _nid ? n.peephole() : n); @@ -276,19 +279,22 @@ public abstract class Node extends BaseNode implements Cloneable { * */ public final Node peepholeOpt( ) { - ITER_CNT++; + CODE.iterCnt(); // Compute initial or improved Type Type old = setType(compute()); - // Replace constant computations from non-constants with a constant node. + // Replace constant computations from non-constants with a constant + // node. If peeps are disabled, still allow high Phis to collapse; + // they typically come from dead Regions, and we want the Region to + // collapse, which requires the Phis to die first. if( _type.isHighOrConst() && !isConst() ) - return ConstantNode.make(_type).peepholeOpt(); + return ConstantNode.make(_type).peephole(); // Global Value Numbering if( _hash==0 ) { - Node n = GVN.get(this); // Will set _hash as a side effect + Node n = CODE._gvn.get(this); // Will set _hash as a side effect if( n==null ) - GVN.put(this,this); // Put in table now + CODE._gvn.put(this,this); // Put in table now else { // Because of random worklist ordering, the two equal nodes // might have different types. Because of monotonicity, both @@ -306,10 +312,9 @@ public abstract class Node extends BaseNode implements Cloneable { return n; // Report progress if( old!=_type ) return this; // Report progress; - ITER_NOP_CNT++; + CODE.iterNop(); return null; // No progress } - public static int ITER_CNT, ITER_NOP_CNT; // m is the new Node, self is the old. // Return 'm', which may have zero uses but is alive nonetheless. @@ -353,7 +358,7 @@ public abstract class Node extends BaseNode implements Cloneable { assert old==null || type.isa(old); // Since _type not set, can just re-run this in assert in the debugger if( old == type ) return old; _type = type; // Set _type late for easier assert debugging - IterPeeps.addAll(_outputs); + CODE.addAll(_outputs); moveDepsToWorklist(); return old; } @@ -432,9 +437,10 @@ public abstract class Node extends BaseNode implements Cloneable { private Node addDep(Node dep, boolean forwards) { // Running peepholes during the big assert cannot have side effects // like adding dependencies. - if( IterPeeps.midAssert() ) return this; - var obs = CodeGen.CODE == null || IterPeeps.midAssert() || CodeGen.CODE._phase == CodeGen.Phase.Parse && CodeGen.CODE.P == null ? null : CodeGen.CODE._obs; + if( CODE._midAssert ) return this; + var obs = CodeGen.CODE._midAssert ? null : CodeGen.CODE._obs; if( obs != null ) obs.dep(this, dep); + if( dep == null ) return this; if( _deps==null ) _deps = new Ary<>(Node.class); if( _deps .find(dep) != -1 ) return this; // Already on list if( !forwards && _inputs .find(dep) != -1 ) return this; // No need for deps on immediate neighbors @@ -446,14 +452,10 @@ public abstract class Node extends BaseNode implements Cloneable { // Move the dependents onto a worklist, and clear for future dependents. public void moveDepsToWorklist( ) { if( _deps==null ) return; - IterPeeps.addAll(_deps); + CODE.addAll(_deps); _deps.clear(); } - // Global Value Numbering. Hash over opcode and inputs; hits in this table - // are structurally equal. - public static final HashMap GVN = new HashMap<>(); - // Two nodes are equal if they have the same inputs and the same "opcode" // which means the same Java class, plus same internal parts. @Override public final boolean equals( Object o ) { @@ -481,7 +483,7 @@ public abstract class Node extends BaseNode implements Cloneable { // If the _hash is set, then the Node is in the GVN table; remove it. void unlock() { if( _hash==0 ) return; - Node old = GVN.remove(this); // Pull from table + Node old = CODE._gvn.remove(this); // Pull from table assert old==this; _hash=0; // Out of table now } @@ -562,7 +564,7 @@ public abstract class Node extends BaseNode implements Cloneable { Node n; try { n = (Node)clone(); } catch( Exception e ) { throw new RuntimeException(e); } - n._nid = UNIQUE_ID++; // allocate unique dense ID + n._nid = CODE.getUID(); // allocate unique dense ID n._inputs = new Ary<>(Node.class); n._outputs = new Ary<>(Node.class); n._deps = null; @@ -573,28 +575,21 @@ public abstract class Node extends BaseNode implements Cloneable { // Report any post-optimize errors public Parser.ParseException err() { return null; } - /** - * Used to allow repeating tests in the same JVM. This just resets the - * Node unique id generator, and is done as part of making a new Parser. - */ - public static void reset() { - UNIQUE_ID = 1; - GVN.clear(); - ITER_CNT = ITER_NOP_CNT = 0; - } + // Common integer constants + public static ConstantNode con(long x) { return (ConstantNode)(new ConstantNode(TypeInteger.constant(x)).peephole()); } // Utility to walk the entire graph applying a function; return the first // not-null result. - private static final BitSet WVISIT = new BitSet(); final public E walk( Function pred ) { - assert WVISIT.isEmpty(); - try { return _walk(pred); } - finally { WVISIT.clear(); } + assert CODE._visit.isEmpty(); + E rez = _walk(pred); + CODE._visit.clear(); + return rez; } private E _walk( Function pred ) { - if( WVISIT.get(_nid) ) return null; // Been there, done that - WVISIT.set(_nid); + if( CODE._visit.get(_nid) ) return null; // Been there, done that + CODE._visit.set(_nid); E x = pred.apply(this); if( x != null ) return x; for( Node def : _inputs ) if( def != null && (x = def._walk(pred)) != null ) return x; diff --git a/src/main/java/com/seaofnodes/simple/node/ParmNode.java b/src/main/java/com/seaofnodes/simple/node/ParmNode.java index 7f5924cc..c3a0cf21 100644 --- a/src/main/java/com/seaofnodes/simple/node/ParmNode.java +++ b/src/main/java/com/seaofnodes/simple/node/ParmNode.java @@ -14,6 +14,7 @@ public class ParmNode extends PhiNode { super(label,declaredType,inputs); _idx = idx; } + public ParmNode(ParmNode parm) { super(parm, parm._label, parm._declaredType); _idx = parm._idx; } @Override public String label() { return MemOpNode.mlabel(_label); } @@ -21,7 +22,7 @@ public class ParmNode extends PhiNode { @Override protected ExprPrinter _print1(ExprPrinter p) { - if( "main".equals(fun().sig()._name) && _label.equals("arg") ) + if( "main".equals(fun()._name) && _label.equals("arg") ) return p.p("arg"); p.p("Parm_").p(_label).open(); for( Node in : _inputs ) p.n(in).p(","); diff --git a/src/main/java/com/seaofnodes/simple/node/PhiNode.java b/src/main/java/com/seaofnodes/simple/node/PhiNode.java index 9b5e1c26..6fb315b4 100644 --- a/src/main/java/com/seaofnodes/simple/node/PhiNode.java +++ b/src/main/java/com/seaofnodes/simple/node/PhiNode.java @@ -14,9 +14,11 @@ public class PhiNode extends Node { final Type _declaredType; public PhiNode(String label, Type declaredType, Node... inputs) { super(inputs); _label = label; assert declaredType!=null; _declaredType = declaredType; } + public PhiNode(PhiNode phi, String label, Type declaredType) { super(phi); _label = label; _declaredType = declaredType; } + public PhiNode(PhiNode phi) { super(phi); _label = phi._label; _declaredType = phi._declaredType; } public PhiNode(RegionNode r, Node sample) { - super(r); + super(new Node[]{r}); _label = ""; _declaredType = sample._type; while( nIns() < r.nIns() ) diff --git a/src/main/java/com/seaofnodes/simple/node/ProjNode.java b/src/main/java/com/seaofnodes/simple/node/ProjNode.java index 1e192676..6ae4a60d 100644 --- a/src/main/java/com/seaofnodes/simple/node/ProjNode.java +++ b/src/main/java/com/seaofnodes/simple/node/ProjNode.java @@ -7,7 +7,7 @@ import com.seaofnodes.simple.type.TypeMem; import com.seaofnodes.simple.type.TypeTuple; -public class ProjNode extends Node implements MultiUse { +public class ProjNode extends Node { // Which slice of the incoming multipart value public final int _idx; @@ -16,13 +16,13 @@ public class ProjNode extends Node implements MultiUse { public final String _label; public ProjNode(Node ctrl, int idx, String label) { - super(ctrl); + super(new Node[]{ctrl}); _idx = idx; _label = label; } + public ProjNode(ProjNode p) { super(p); _idx = p._idx; _label = p._label; } @Override public String label() { return _label; } - @Override public int idx() { return _idx; } @Override protected ExprPrinter _print1(ExprPrinter p) { diff --git a/src/main/java/com/seaofnodes/simple/node/ReadOnlyNode.java b/src/main/java/com/seaofnodes/simple/node/ReadOnlyNode.java index fa4e0242..d79f2815 100644 --- a/src/main/java/com/seaofnodes/simple/node/ReadOnlyNode.java +++ b/src/main/java/com/seaofnodes/simple/node/ReadOnlyNode.java @@ -10,6 +10,7 @@ import com.seaofnodes.simple.type.TypeMemPtr; */ public class ReadOnlyNode extends Node { public ReadOnlyNode( Node n ) { super(null,n); } + public ReadOnlyNode( ReadOnlyNode n ) { super(n); } @Override public String label() { return "ReadOnly"; } @Override protected String format() { return "(const)%1"; } diff --git a/src/main/java/com/seaofnodes/simple/node/RegionNode.java b/src/main/java/com/seaofnodes/simple/node/RegionNode.java index 945398e9..e1f095a9 100644 --- a/src/main/java/com/seaofnodes/simple/node/RegionNode.java +++ b/src/main/java/com/seaofnodes/simple/node/RegionNode.java @@ -2,20 +2,17 @@ package com.seaofnodes.simple.node; import com.seaofnodes.print.ExprPrinter; -import com.seaofnodes.simple.IterPeeps; -import com.seaofnodes.simple.Parser; -import com.seaofnodes.simple.Utils; +import com.seaofnodes.simple.*; import com.seaofnodes.simple.type.Type; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashSet; +import java.util.*; public class RegionNode extends CFGNode { // Source location for late discovered errors public Parser.Lexer _loc; - public RegionNode(Node... nodes) { this(null,nodes); } - public RegionNode(Parser.Lexer loc, Node... nodes) { super(nodes); _loc = loc; } + public RegionNode(Parser.Lexer loc, Node... nodes) { super(nodes); _loc = loc; } + public RegionNode(RegionNode r, Parser.Lexer loc) { super(r); _loc = loc; } + public RegionNode(RegionNode r) { super(r); _loc = r._loc; } @Override public String label() { return "Region"; } @@ -39,30 +36,10 @@ public class RegionNode extends CFGNode { @Override public Node idealize() { if( inProgress() ) return null; - // Delete dead paths into a Region - int path = findDeadInput(); - if( path != 0 && - // Do not delete the entry path of a loop (ok to remove the back - // edge and make the loop a single-entry Region which folds away - // the Loop). Folding the entry path confused the loop structure, - // moving the backedge to the entry point. - !(this instanceof LoopNode loop && loop.entry()==in(path)) ) { - // Cannot use the obvious output iterator here, because a Phi - // deleting an input might recursively delete *itself*. This - // shuffles the output array, and we might miss iterating an - // unrelated Phi. So on rare occasions we repeat the loop to get - // all the Phis. - int nouts = 0; - while( nouts != nOuts() ) { - nouts = nOuts(); - for( int i=0; i _print1(ExprPrinter p) { return p.p("Xctrl"); } @Override public boolean isConst() { return true; } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AddFRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AddFRISC.java new file mode 100644 index 00000000..bc959977 --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AddFRISC.java @@ -0,0 +1,30 @@ +package com.seaofnodes.simple.node.cpus.riscv; + +import com.seaofnodes.simple.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.type.TypeInteger; +import java.io.ByteArrayOutputStream; + +public class AddFRISC extends MachConcreteNode implements MachNode{ + AddFRISC(Node addf) {super(addf);} + + // Register mask allowed on input i. + @Override public RegMask regmap(int i) { assert i==1 || i==2; return riscv.FMASK; } + // Register mask allowed as a result. 0 for no register. + @Override public RegMask outregmap() { return riscv.FMASK; } + // Output is same register as input#1 + @Override public int twoAddress() { return 1; } + + // Encoding is appended into the byte array; size is returned + @Override public int encoding(ByteArrayOutputStream bytes) { + throw Utils.TODO(); + } + + // Default on double precision for now(64 bits) + // General form: "fadd.d rd = src1 + src2 + @Override public void asm(CodeGen code, SB sb) { + sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" + ").p(code.reg(in(2))); + } + + @Override public String op() { return "addf"; } +} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AddIRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AddIRISC.java new file mode 100644 index 00000000..af4bf08d --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AddIRISC.java @@ -0,0 +1,39 @@ +package com.seaofnodes.simple.node.cpus.riscv; + +import com.seaofnodes.simple.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.type.TypeInteger; +import java.io.ByteArrayOutputStream; + +public class AddIRISC extends MachConcreteNode implements MachNode { + final TypeInteger _ti; + AddIRISC( Node add, TypeInteger ti ) { + super(add); + _inputs.pop(); + _ti = ti; + } + + // Register mask allowed on input i. + @Override public RegMask regmap(int i) { + // assert i== i; + return riscv.RMASK; } + // Register mask allowed as a result. 0 for no register. + @Override public RegMask outregmap() { return riscv.RMASK; } + // Output is same register as input#1 + @Override public int twoAddress() { return 0; } + + // Encoding is appended into the byte array; size is returned + @Override public int encoding(ByteArrayOutputStream bytes) { + throw Utils.TODO(); + } + + // General form: "addi rd = rs1 + imm" + @Override public void asm(CodeGen code, SB sb) { + sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" + #"); + _ti.print(sb); + } + + @Override public String op() { + return _ti.value() == 1 ? "inc" : (_ti.value() == -1 ? "dec" : "addi"); + } +} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AddRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AddRISC.java new file mode 100644 index 00000000..482b7f11 --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AddRISC.java @@ -0,0 +1,36 @@ +package com.seaofnodes.simple.node.cpus.riscv; + + +import com.seaofnodes.simple.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.type.TypeInteger; +import java.io.ByteArrayOutputStream; + + +public class AddRISC extends MachConcreteNode implements MachNode { + + AddRISC( Node add) {super(add); } + + // Register mask allowed on input i. + @Override public RegMask regmap(int i) { + assert i== 1 || i == 2 || i == 3; + return riscv.RMASK; + } + // Register mask allowed as a result. 0 for no register. + @Override public RegMask outregmap() { return riscv.RMASK; } + // Output is same register as input#1 + @Override public int twoAddress() { return 0; } + + // Encoding is appended into the byte array; size is returned + @Override public int encoding(ByteArrayOutputStream bytes) { + throw Utils.TODO(); + } + + // General form: "rd = rs1 + rs2" + @Override public void asm(CodeGen code, SB sb) { + sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" + ").p(code.reg(in(2))); + } + + @Override public String op() { + return "add";} +} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AndIRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AndIRISC.java new file mode 100644 index 00000000..29c2a686 --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AndIRISC.java @@ -0,0 +1,40 @@ +package com.seaofnodes.simple.node.cpus.riscv; + +import com.seaofnodes.simple.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.type.Type; +import com.seaofnodes.simple.type.TypeInteger; +import java.io.ByteArrayOutputStream; +import java.util.BitSet; +import java.lang.StringBuilder; + +public class AndIRISC extends MachConcreteNode implements MachNode{ + final TypeInteger _ti; + AndIRISC(AndNode and, TypeInteger ti) { + super(and); + _inputs.pop(); + _ti = ti; + } + // Register mask allowed on input i. + // This is the normal calling convention + @Override public RegMask regmap(int i) { assert i==1 || i == 2; return riscv.RMASK; } + // Register mask allowed as a result. 0 for no register. + @Override public RegMask outregmap() { return riscv.RMASK; } + + // Output is same register as input#1 + @Override public int twoAddress() { return 0; } + + // Encoding is appended into the byte array; size is returned + @Override public int encoding(ByteArrayOutputStream bytes) { + throw Utils.TODO(); + } + + // General form + // General form: "andi rd = rs1 & imm" + @Override public void asm(CodeGen code, SB sb) { + sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" & #"); + _ti.print(sb); + } + + @Override public String op() { return "andi"; } +} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AndRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AndRISC.java new file mode 100644 index 00000000..e697de36 --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AndRISC.java @@ -0,0 +1,41 @@ +package com.seaofnodes.simple.node.cpus.riscv; + + +import com.seaofnodes.simple.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.node.cpus.x86_64_v2.x86_64_v2; +import com.seaofnodes.simple.type.Type; +import com.seaofnodes.simple.type.TypeInteger; +import java.io.ByteArrayOutputStream; +import java.util.BitSet; +import java.lang.StringBuilder; + +public class AndRISC extends MachConcreteNode implements MachNode{ + AndRISC(Node and) { + super(and); + } + + // Register mask allowed on input i. + // This is the normal calling convention + @Override public RegMask regmap(int i) { + assert i==1 || i==2; + return riscv.RMASK; + } + + // Register mask allowed as a result. 0 for no register. + @Override public RegMask outregmap() { return riscv.RMASK; } + + // Output is same register as input#1 + @Override public int twoAddress() { return 0; } + + // Encoding is appended into the byte array; size is returned + @Override public int encoding(ByteArrayOutputStream bytes) { + throw Utils.TODO(); + } + + // General form + // General form: #rd = rs1 & rs2 + @Override public void asm(CodeGen code, SB sb) { + sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))); + } +} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CBranchRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CBranchRISC.java new file mode 100644 index 00000000..d7e23c6d --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CBranchRISC.java @@ -0,0 +1,49 @@ +package com.seaofnodes.simple.node.cpus.riscv; + + +import com.seaofnodes.simple.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.type.Type; +import com.seaofnodes.simple.type.TypeInteger; +import java.io.ByteArrayOutputStream; +// Conditional branch such as: BEQ +public class CBranchRISC extends IfNode implements MachNode{ + final String _bop; + // label is obtained implicitly + CBranchRISC( IfNode iff, String bop ) { + super(iff); + _bop = bop; + } + + @Override public String label() { return op(); } + + @Override public void postSelect() { + Node set = in(1); + Node cmp = set.in(1); + // Bypass an expected Set and just reference the cmp directly + if( set instanceof SetRISC) + _inputs.set(1,cmp); + else + throw Utils.TODO(); + } + + @Override public RegMask regmap(int i) { assert i==1; return riscv.RMASK; } + @Override public RegMask outregmap() { return null; } + + // Encoding is appended into the byte array; size is returned + + @Override public int encoding(ByteArrayOutputStream bytes) { + throw Utils.TODO(); + } + + @Override public void asm(CodeGen code, SB sb) { + String src = code.reg(in(1)); + if( src!="FLAGS" ) sb.p(src); + } + + @Override public String op() { return "b"+_bop; } + + @Override public String comment() { + return "L"+cproj(1)._nid+", L"+cproj(0)._nid; + } +} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CallRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CallRISC.java new file mode 100644 index 00000000..7f062104 --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CallRISC.java @@ -0,0 +1,37 @@ +package com.seaofnodes.simple.node.cpus.riscv; + +import com.seaofnodes.simple.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.type.TypeFunPtr; +import java.io.ByteArrayOutputStream; + +public class CallRISC extends CallNode implements MachNode{ + final TypeFunPtr _tfp; + final String _name; + CallRISC( CallNode call, TypeFunPtr tfp ) { + super(call); + _inputs.pop(); // Pop constant target + assert tfp.isConstant(); + _tfp = tfp; + _name = CodeGen.CODE.link(tfp)._name; + } + + @Override public String label() { return op(); } + @Override public RegMask regmap(int i) { + return riscv.callInMaskInt(i); // Normal argument + } + @Override public RegMask outregmap() { return riscv.RET_MASK; } + + @Override public String name() { return _name; } + + // Encoding is appended into the byte array; size is returned + @Override public int encoding(ByteArrayOutputStream bytes) { + throw Utils.TODO(); + } + + @Override public void asm(CodeGen code, SB sb) { + sb.p(_name); + } + + @Override public String op() { return "call"; } +} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CallRRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CallRRISC.java new file mode 100644 index 00000000..02850582 --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CallRRISC.java @@ -0,0 +1,31 @@ +package com.seaofnodes.simple.node.cpus.riscv; + +import com.seaofnodes.simple.*; +import com.seaofnodes.simple.node.*; + +import java.io.ByteArrayOutputStream; + +public class CallRRISC extends CallNode implements MachNode{ + CallRRISC( CallNode call ) { super(call); } + + @Override public String label() { return op(); } + @Override public RegMask regmap(int i) { + // Todo: float or int? + return i==_inputs._len + ? riscv.RMASK // Function call target + : riscv.callInMaskInt(i); // Normal argument + } + @Override public RegMask outregmap() { return riscv.RET_MASK; } + + // Encoding is appended into the byte array; size is returned + @Override public int encoding(ByteArrayOutputStream bytes) { + throw Utils.TODO(); + } + + @Override public void asm(CodeGen code, SB sb) { + sb.p(code.reg(fptr())); + } + + @Override public String op() { return "callr"; } + +} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CmpIRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CmpIRISC.java new file mode 100644 index 00000000..39fa3a29 --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CmpIRISC.java @@ -0,0 +1,44 @@ +package com.seaofnodes.simple.node.cpus.riscv; + + +import com.seaofnodes.simple.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.type.Type; +import com.seaofnodes.simple.type.TypeInteger; +import java.io.ByteArrayOutputStream; + +// Compare immediate. Sets flags.(RFLAGS) +public class CmpIRISC extends MachConcreteNode implements MachNode{ + final int _imm; + final String _bop; + CmpIRISC( BoolNode bool, TypeInteger ti ) { + super(bool); + _inputs.pop(); // Toss ideal ConstantNode away, embedded into machine op + _bop = bool.op(); // One of <,<=,== + _imm = (int)ti.value(); + assert _imm == ti.value(); + } + CmpIRISC( Node cmp, double ignore ) { + super(cmp); + _bop = "=="; + _imm = 0; + } + + @Override public RegMask regmap(int i) { assert i==1; return riscv.RMASK; } + @Override public RegMask outregmap() { return riscv.FLAGS_MASK; } + + // Encoding is appended into the byte array; size is returned + @Override public int encoding(ByteArrayOutputStream bytes) { + throw Utils.TODO(); + } + + // General form: "cmp rs1, 1" + @Override public void asm(CodeGen code, SB sb) { + String dst = code.reg(this); + if( dst!="FLAGS" ) sb.p(dst).p(" = "); + sb.p(code.reg(in(1))); + if( _imm != 0 ) sb.p(", #").p(_imm); + } + + @Override public String op() { return _imm==0 ? "test" : "cmp"; } +} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CmpRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CmpRISC.java new file mode 100644 index 00000000..3bd4008e --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CmpRISC.java @@ -0,0 +1,28 @@ +package com.seaofnodes.simple.node.cpus.riscv; + +import com.seaofnodes.simple.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.type.Type; +import com.seaofnodes.simple.type.TypeFunPtr; +import java.io.ByteArrayOutputStream; + +// Compare. Sets flags.(RFLAGS) +public class CmpRISC extends MachConcreteNode implements MachNode{ + CmpRISC( Node cmp ) { super(cmp); } + + // Encoding is appended into the byte array; size is returned + @Override public int encoding(ByteArrayOutputStream bytes) { + throw Utils.TODO(); + } + + + // General form: "cmp rs1, rs2" + @Override public void asm(CodeGen code, SB sb) { + String dst = code.reg(this); + if( dst!="FLAGS" ) sb.p(dst).p(" = "); + sb.p(code.reg(in(1))).p(", ").p(code.reg(in(2))); + } + + @Override public RegMask regmap(int i) { assert i==1 || i==2; return riscv.RMASK; } + @Override public RegMask outregmap() { return riscv.FLAGS_MASK; } +} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/DivFRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/DivFRISC.java new file mode 100644 index 00000000..deb2628d --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/DivFRISC.java @@ -0,0 +1,30 @@ +package com.seaofnodes.simple.node.cpus.riscv; + +import com.seaofnodes.simple.*; +import com.seaofnodes.simple.node.*; +import java.io.ByteArrayOutputStream; + +public class DivFRISC extends MachConcreteNode implements MachNode{ + DivFRISC(Node divf) {super(divf);} + + // Register mask allowed on input i. + @Override public RegMask regmap(int i) { assert i==1 || i==2; return riscv.FMASK; } + // Register mask allowed as a result. 0 for no register. + @Override public RegMask outregmap() { return riscv.FMASK; } + // Output is same register as input#1 + @Override public int twoAddress() { return 1; } + + // Encoding is appended into the byte array; size is returned + @Override public int encoding(ByteArrayOutputStream bytes) { + throw Utils.TODO(); + } + + // Default on double precision for now(64 bits) + // General form: "fdiv.d rd = src1 / src2 + @Override public void asm(CodeGen code, SB sb) { + sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" + ").p(code.reg(in(2))); + } + + @Override public String op() { return "divf"; } + +} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/DivRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/DivRISC.java new file mode 100644 index 00000000..98a8406f --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/DivRISC.java @@ -0,0 +1,33 @@ +package com.seaofnodes.simple.node.cpus.riscv; + +import com.seaofnodes.simple.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.node.cpus.x86_64_v2.x86_64_v2; +import com.seaofnodes.simple.type.TypeInteger; +import java.io.ByteArrayOutputStream; + +public class DivRISC extends MachConcreteNode implements MachNode{ + DivRISC(Node div) {super(div);} + + // Register mask allowed on input i. + @Override public RegMask regmap(int i) { + assert i==1 || i==2; + return riscv.FMASK; } + // Register mask allowed as a result. 0 for no register. + @Override public RegMask outregmap() { return x86_64_v2.RMASK; } + // Output is same register as input#1 + @Override public int twoAddress() { return 1; } + + // Encoding is appended into the byte array; size is returned + @Override public int encoding(ByteArrayOutputStream bytes) { + throw Utils.TODO(); + } + + // Signed division + // General form: "rd = rs1 / rs" + @Override public void asm(CodeGen code, SB sb) { + sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" / ").p(code.reg(in(2))); + } + + @Override public String op() { return "div"; } +} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/F8ToIRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/F8ToIRISC.java new file mode 100644 index 00000000..261a1dcd --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/F8ToIRISC.java @@ -0,0 +1,27 @@ +package com.seaofnodes.simple.node.cpus.riscv; + +import com.seaofnodes.simple.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.node.cpus.x86_64_v2.x86_64_v2; +import com.seaofnodes.simple.type.TypeInteger; +import java.io.ByteArrayOutputStream; + +public class F8ToIRISC extends MachConcreteNode implements MachNode { + F8ToIRISC(Node f8toi) {super(f8toi);} + + // Register mask allowed on input i. + @Override public RegMask regmap(int i) { assert i==1; return riscv.FMASK; } + // Register mask allowed as a result. 0 for no register. + @Override public RegMask outregmap() { return riscv.RMASK; } + + // Encoding is appended into the byte array; size is returned + @Override public int encoding(ByteArrayOutputStream bytes) { + throw Utils.TODO(); + } + + @Override public void asm(CodeGen code, SB sb) { + sb.p(code.reg(this)).p(" = ").p("(int)").p(code.reg(in(1))); + } + + @Override public String op() { return "f8toi"; } +} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/FltRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/FltRISC.java new file mode 100644 index 00000000..e1637eec --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/FltRISC.java @@ -0,0 +1,39 @@ +package com.seaofnodes.simple.node.cpus.riscv; + + +import com.seaofnodes.simple.CodeGen; +import com.seaofnodes.simple.RegMask; +import com.seaofnodes.simple.SB; +import com.seaofnodes.simple.Utils; +import com.seaofnodes.simple.node.ConstantNode; +import com.seaofnodes.simple.node.MachNode; +import com.seaofnodes.simple.node.cpus.x86_64_v2.x86_64_v2; + +import java.io.ByteArrayOutputStream; + +public class FltRISC extends ConstantNode implements MachNode{ + FltRISC(ConstantNode con) { + super(con); + } + // Register mask allowed on input i. 0 for no register. + @Override public RegMask regmap(int i) { return null; } + // General int registers + @Override public RegMask outregmap() { return riscv.FMASK; } + + // Encoding is appended into the byte array; size is returned + @Override public int encoding(ByteArrayOutputStream bytes) { + throw Utils.TODO(); + } + + // Human-readable form appended to the SB. Things like the encoding, + // indentation, leading address or block labels not printed here. + // Just something like "ld4\tR17=[R18+12] // Load array base". + // General form: "op\tdst=src+src" + @Override public void asm(CodeGen code, SB sb) { + _con.print(sb.p(code.reg(this)).p(" #")); + } + + @Override public String op() { + return "flw"; // Some fancier encoding + } +} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/FunRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/FunRISC.java new file mode 100644 index 00000000..66e32e29 --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/FunRISC.java @@ -0,0 +1,22 @@ +package com.seaofnodes.simple.node.cpus.riscv; + +import com.seaofnodes.simple.RegMask; +import com.seaofnodes.simple.Utils; +import com.seaofnodes.simple.node.FunNode; +import com.seaofnodes.simple.node.MachNode; +import com.seaofnodes.simple.node.cpus.x86_64_v2.FunX86; + +import java.io.ByteArrayOutputStream; + +public class FunRISC extends FunNode implements MachNode{ + FunRISC(FunNode fun) {super(fun);} + // Register mask allowed on input i. 0 for no register. + @Override public RegMask regmap(int i) { return null; } + // Register mask allowed as a result. 0 for no register. + @Override public RegMask outregmap() { return null; } + + // Encoding is appended into the byte array; size is returned + @Override public int encoding(ByteArrayOutputStream bytes) { + throw Utils.TODO(); + } +} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/I2F8RISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/I2F8RISC.java new file mode 100644 index 00000000..df005e86 --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/I2F8RISC.java @@ -0,0 +1,29 @@ +package com.seaofnodes.simple.node.cpus.riscv; + +import com.seaofnodes.simple.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.node.cpus.x86_64_v2.x86_64_v2; +import com.seaofnodes.simple.type.TypeInteger; +import java.io.ByteArrayOutputStream; + +// fcvt.d.w +public class I2F8RISC extends MachConcreteNode implements MachNode { + I2F8RISC(Node i2f8) {super(i2f8);} + + // Register mask allowed on input i. + @Override public RegMask regmap(int i) { assert i==1; return riscv.RMASK; } + // Register mask allowed as a result. 0 for no register. + @Override public RegMask outregmap() { return riscv.FMASK; } + + // Encoding is appended into the byte array; size is returned + @Override public int encoding(ByteArrayOutputStream bytes) { + throw Utils.TODO(); + } + + // General form: "i2f8 (flt)int_value" + @Override public void asm(CodeGen code, SB sb) { + sb.p(code.reg(this)).p(" = ").p("(flt)").p(code.reg(in(1))); + } + + @Override public String op() { return "i2f8"; } +} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/IntRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/IntRISC.java new file mode 100644 index 00000000..bf170b22 --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/IntRISC.java @@ -0,0 +1,43 @@ +package com.seaofnodes.simple.node.cpus.riscv; + +import com.seaofnodes.simple.*; +import com.seaofnodes.simple.node.ConstantNode; +import com.seaofnodes.simple.node.MachNode; +import com.seaofnodes.simple.type.Type; +import com.seaofnodes.simple.type.TypeInteger; + +import java.io.ByteArrayOutputStream; +// Integer constants +public class IntRISC extends ConstantNode implements MachNode { + IntRISC(ConstantNode con) { + super(con); + } + + // Register mask allowed on input i. 0 for no register. + @Override public RegMask regmap(int i) { return null; } + // General int registers + @Override public RegMask outregmap() { return riscv.RMASK; } + + // Encoding is appended into the byte array; size is returned + @Override public int encoding(ByteArrayOutputStream bytes) { + throw Utils.TODO(); + } + + // Human-readable form appended to the SB. Things like the encoding, + // indentation, leading address or block labels not printed here. + // Just something like "ld4\tR17=[R18+12] // Load array base". + // General form: "op\tdst=src+src" + @Override public void asm(CodeGen code, SB sb) { + String reg = code.reg(this); + if( _con == Type.NIL || _con == TypeInteger.ZERO ) + sb.p(reg).p(",").p(reg); + else + _con.print(sb.p(reg).p(" #")); + } + + @Override public String op() { + if( _con == Type.NIL || _con == TypeInteger.ZERO ) + return "xor"; + return "ldi"; // Some fancier encoding + } +} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/LoadRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/LoadRISC.java new file mode 100644 index 00000000..846e313c --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/LoadRISC.java @@ -0,0 +1,34 @@ +package com.seaofnodes.simple.node.cpus.riscv; + + +import com.seaofnodes.simple.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.type.TypeInteger; +import java.io.ByteArrayOutputStream; + +public class LoadRISC extends MemOpRISC{ + LoadRISC(LoadNode ld) { + super(ld, ld); + } + + @Override public RegMask regmap(int i) { + // wider mask, encode it differently, immediate handled with hard split(in RA) + return riscv.RMASK.or(riscv.FMASK); + } + // Register mask allowed as a result. 0 for no register. + @Override public RegMask outregmap() { return riscv.RMASK; } + + + // Encoding is appended into the byte array; size is returned + @Override public int encoding(ByteArrayOutputStream bytes) { + throw Utils.TODO(); + } + + @Override public void asm(CodeGen code, SB sb) { + } + + @Override public String op() { + return "lw" +_sz; + } +} + diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/MemOpRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/MemOpRISC.java new file mode 100644 index 00000000..3bf1cf09 --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/MemOpRISC.java @@ -0,0 +1,39 @@ +package com.seaofnodes.simple.node.cpus.riscv; + +import com.seaofnodes.print.ExprPrinter; + +import com.seaofnodes.simple.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.node.cpus.x86_64_v2.x86_64_v2; +import com.seaofnodes.simple.type.*; +import java.io.ByteArrayOutputStream; +import java.lang.StringBuilder; + + +public class MemOpRISC extends MemOpNode implements MachNode{ + final char _sz = (char)('0'+(1<<_declaredType.log_size())); + MemOpRISC(Node op, MemOpNode mop) { + super(op, mop); + } + @Override protected ExprPrinter _printMach(ExprPrinter p) { return p.p(".").p(_name); } + + @Override public boolean isMem() { return _type instanceof TypeMem; } + + @Override public String label() { return op(); } + @Override public Type compute() { throw Utils.TODO(); } + @Override public Node idealize() { throw Utils.TODO(); } + + // Register mask allowed on input i. + @Override public RegMask regmap(int i) { + throw Utils.TODO(); + } + // Register mask allowed as a result. 0 for no register. + @Override public RegMask outregmap() { throw Utils.TODO(); } + + @Override public int encoding(ByteArrayOutputStream bytes) { throw Utils.TODO(); } + + + SB asm_address(CodeGen code, SB sb) { + return sb; + } +} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/MulFRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/MulFRISC.java new file mode 100644 index 00000000..43197b50 --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/MulFRISC.java @@ -0,0 +1,30 @@ +package com.seaofnodes.simple.node.cpus.riscv; + +import com.seaofnodes.simple.*; +import com.seaofnodes.simple.node.*; +import java.io.ByteArrayOutputStream; + +public class MulFRISC extends MachConcreteNode implements MachNode{ + MulFRISC(Node mulf) {super(mulf);} + + // Register mask allowed on input i. + @Override public RegMask regmap(int i) { assert i==1 || i==2; return riscv.FMASK; } + // Register mask allowed as a result. 0 for no register. + @Override public RegMask outregmap() { return riscv.FMASK; } + // Output is same register as input#1 + @Override public int twoAddress() { return 1; } + + // Encoding is appended into the byte array; size is returned + @Override public int encoding(ByteArrayOutputStream bytes) { + throw Utils.TODO(); + } + + // Default on double precision for now(64 bits) + // General form: "fmul.d rd = src1 * src2 + @Override public void asm(CodeGen code, SB sb) { + sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" + ").p(code.reg(in(2))); + } + + @Override public String op() { return "mulf"; } + +} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/MulIRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/MulIRISC.java new file mode 100644 index 00000000..8735068e --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/MulIRISC.java @@ -0,0 +1,35 @@ +package com.seaofnodes.simple.node.cpus.riscv; + +import com.seaofnodes.simple.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.type.TypeInteger; +import java.io.ByteArrayOutputStream; + +public class MulIRISC extends MachConcreteNode implements MachNode { + final TypeInteger _ti; + MulIRISC(Node mul, TypeInteger ti) {super(mul); _inputs.pop(); _ti = ti;} + + // Register mask allowed on input i. + // This is the normal calling convention + @Override public RegMask regmap(int i) { + return riscv.RMASK; } + // Register mask allowed as a result. 0 for no register. + @Override public RegMask outregmap() { return riscv.RMASK; } + + + // Output is same register as input#1 + @Override public int twoAddress() { return 0; } + + // Encoding is appended into the byte array; size is returned + @Override public int encoding(ByteArrayOutputStream bytes) { + throw Utils.TODO(); + } + // General form + // General form: "muli dst * #imm" + @Override public void asm(CodeGen code, SB sb) { + sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" * #"); + _ti.print(sb); + } + + @Override public String op() { return "muli"; } +} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/MulRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/MulRISC.java new file mode 100644 index 00000000..8713486e --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/MulRISC.java @@ -0,0 +1,33 @@ +package com.seaofnodes.simple.node.cpus.riscv; + +import com.seaofnodes.simple.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.node.cpus.x86_64_v2.x86_64_v2; +import com.seaofnodes.simple.type.TypeInteger; +import java.io.ByteArrayOutputStream; + + +// mulh signed multiply instruction(no-imm form) +public class MulRISC extends MachConcreteNode implements MachNode{ + MulRISC(Node mul) {super(mul);} + + // Register mask allowed on input i. + @Override public RegMask regmap(int i) { assert i==1 || i==2; return riscv.RMASK; } + // Register mask allowed as a result. 0 for no register. + @Override public RegMask outregmap() { return riscv.RMASK; } + // Output is same register as input#1 + @Override public int twoAddress() { return 1; } + + // Encoding is appended into the byte array; size is returned + @Override public int encoding(ByteArrayOutputStream bytes) { + throw Utils.TODO(); + } + + // General form: "rd = rs1 * rs2" + @Override public void asm(CodeGen code, SB sb) { + sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" * ").p(code.reg(in(2))); + } + + @Override public String op() { return "mul"; } + +} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/NewRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/NewRISC.java new file mode 100644 index 00000000..2b8ec4f2 --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/NewRISC.java @@ -0,0 +1,44 @@ +package com.seaofnodes.simple.node.cpus.riscv; + +import com.seaofnodes.simple.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.node.cpus.x86_64_v2.x86_64_v2; + +import java.io.ByteArrayOutputStream; + + +public class NewRISC extends NewNode implements MachNode{ + + // A pre-zeroed chunk of memory. + NewRISC( NewNode nnn ) { super(nnn); } + + // Register mask allowed on input i, the size + @Override public RegMask regmap(int i) { + // Size + if( i==2 ) return riscv.RET_MASK; + // Memory has no register + return null; + } + + @Override public RegMask outregmap() { throw Utils.TODO(); } + + // Register mask allowed as a result. Pointer result in standard calling + // convention. + @Override public RegMask outregmap(int i) { + if( i == 0 ) return riscv.RET_MASK; + // Memory has no register + return null; + } + + // Encoding is appended into the byte array; size is returned + @Override public int encoding(ByteArrayOutputStream bytes) { + throw Utils.TODO(); + } + + // General form: "alloc #bytes" + @Override public void asm(CodeGen code, SB sb) { + sb.p(code.reg(size())); + } + + @Override public String op() { return "alloc"; } +} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/OrIRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/OrIRISC.java new file mode 100644 index 00000000..f3f79ed1 --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/OrIRISC.java @@ -0,0 +1,43 @@ +package com.seaofnodes.simple.node.cpus.riscv; + +import com.seaofnodes.simple.CodeGen; +import com.seaofnodes.simple.RegMask; +import com.seaofnodes.simple.SB; +import com.seaofnodes.simple.Utils; +import com.seaofnodes.simple.node.MachConcreteNode; +import com.seaofnodes.simple.node.MachNode; +import com.seaofnodes.simple.node.Node; +import com.seaofnodes.simple.node.cpus.x86_64_v2.x86_64_v2; +import com.seaofnodes.simple.type.Type; +import com.seaofnodes.simple.type.TypeInteger; + +import java.io.ByteArrayOutputStream; + +public class OrIRISC extends MachConcreteNode implements MachNode{ + final TypeInteger _ti; + OrIRISC(Node or, TypeInteger ti) {super(or); _inputs.pop(); _ti = ti;} + // Register mask allowed on input i. + // This is the normal calling convention + @Override public RegMask regmap(int i) { + assert i==1 || i==2; + return riscv.RMASK; + } + // Register mask allowed as a result. 0 for no register. + @Override public RegMask outregmap() { return riscv.RMASK; } + // Output is same register as input#1 + @Override public int twoAddress() { return 0;} + + // Encoding is appended into the byte array; size is returned + @Override public int encoding(ByteArrayOutputStream bytes) { + throw Utils.TODO(); + } + + // General form + // General form: "rd = rs1 | imm" + @Override public void asm(CodeGen code, SB sb) { + sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" | #"); + _ti.print(sb); + } + + @Override public String op() { return "ori"; } +} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/OrRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/OrRISC.java new file mode 100644 index 00000000..8f1e9305 --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/OrRISC.java @@ -0,0 +1,43 @@ +package com.seaofnodes.simple.node.cpus.riscv; + +import com.seaofnodes.simple.node.MachConcreteNode; +import com.seaofnodes.simple.node.MachNode; + +import com.seaofnodes.simple.CodeGen; +import com.seaofnodes.simple.RegMask; +import com.seaofnodes.simple.SB; +import com.seaofnodes.simple.Utils; +import com.seaofnodes.simple.node.Node; +import com.seaofnodes.simple.node.cpus.x86_64_v2.x86_64_v2; +import java.io.ByteArrayOutputStream; + +public class OrRISC extends MachConcreteNode implements MachNode { + OrRISC(Node or) { + super(or); + } + + // Register mask allowed on input i. + // This is the normal calling convention + @Override public RegMask regmap(int i) { + assert i==1 || i==2; + return riscv.RMASK; + } + + // Register mask allowed as a result. 0 for no register. + @Override public RegMask outregmap() { return riscv.RMASK; } + + // Output is same register as input#1 + @Override public int twoAddress() { return 0; } + + // Encoding is appended into the byte array; size is returned + @Override public int encoding(ByteArrayOutputStream bytes) { + throw Utils.TODO(); + } + + // General form + // General form: # rd = rs1 | rs2 + @Override public void asm(CodeGen code, SB sb) { + sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))); + } + +} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/ParmRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/ParmRISC.java new file mode 100644 index 00000000..2e20f96f --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/ParmRISC.java @@ -0,0 +1,25 @@ +package com.seaofnodes.simple.node.cpus.riscv; + +import com.seaofnodes.simple.RegMask; +import com.seaofnodes.simple.SB; +import com.seaofnodes.simple.Utils; +import com.seaofnodes.simple.node.ParmNode; +import com.seaofnodes.simple.node.MachNode; +import java.io.ByteArrayOutputStream; + +public class ParmRISC extends ParmNode implements MachNode{ + final RegMask _rmask; + ParmRISC(ParmNode parm) { + super(parm); + // Assume int + _rmask = riscv.callInMaskInt(_idx); + } + + // Register mask allowed on input i. 0 for no register. + @Override public RegMask regmap(int i) { return null; } + // Register mask allowed as a result. Calling convention register + @Override public RegMask outregmap() { return _rmask; } + + // Encoding is appended into the byte array. Returns size + @Override public int encoding(ByteArrayOutputStream bytes) { return 0; } +} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/ProjRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/ProjRISC.java new file mode 100644 index 00000000..a1312c4c --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/ProjRISC.java @@ -0,0 +1,23 @@ +package com.seaofnodes.simple.node.cpus.riscv; + + +import com.seaofnodes.simple.RegMask; +import com.seaofnodes.simple.SB; +import com.seaofnodes.simple.Utils; +import com.seaofnodes.simple.node.ProjNode; +import com.seaofnodes.simple.node.MachNode; +import java.io.ByteArrayOutputStream; + +public class ProjRISC extends ProjNode implements MachNode{ + ProjRISC(ProjNode p) {super(p);} + + // Register mask allowed on input i. 0 for no register. + @Override public RegMask regmap(int i) { return null; } + // Register mask allowed as a result. 0 for no register. + @Override public RegMask outregmap() { return null; } + + // Encoding is appended into the byte array; size is returned + @Override public int encoding(ByteArrayOutputStream bytes) { + throw Utils.TODO(); + } +} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/RetRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/RetRISC.java new file mode 100644 index 00000000..c92e6989 --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/RetRISC.java @@ -0,0 +1,48 @@ +package com.seaofnodes.simple.node.cpus.riscv; + +import com.seaofnodes.simple.*; +import com.seaofnodes.simple.node.FunNode; +import com.seaofnodes.simple.node.ReturnNode; +import com.seaofnodes.simple.node.MachNode; + +import java.io.ByteArrayOutputStream; + +public class RetRISC extends ReturnNode implements MachNode{ + RetRISC(ReturnNode ret, FunNode fun) { super(ret, fun); fun.setRet(this); } + // Todo: postSelect + // Register mask allowed on input i. + // This is the normal calling convention + @Override public RegMask regmap(int i) { + return switch( i ) { + case 0 -> null; + case 1 -> null; + case 2 -> riscv.RET_MASK; + case 3 -> null; // RPC is always on stack + default -> throw Utils.TODO(); + }; + } + // Register mask allowed as a result. 0 for no register. + @Override public RegMask outregmap() { return null; } + + // Encoding is appended into the byte array; size is returned + @Override public int encoding(ByteArrayOutputStream bytes) { + throw Utils.TODO(); + } + + // Human-readable form appended to the SB. Things like the encoding, + // indentation, leading address or block labels not printed here. + // Just something like "ld4\tR17=[R18+12] // Load array base". + // General form: "op\tdst=src+src" + @Override public void asm(CodeGen code, SB sb) { + sb.p(code.reg(in(2))); + } + + // Correct Nodes outside the normal edges + @Override public void postSelect() { + FunNode fun = (FunNode)rpc().in(0); + _fun = fun; + fun.setRet(this); + } + + @Override public String op() { return "ret"; } +} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SetRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SetRISC.java new file mode 100644 index 00000000..1d8c9de8 --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SetRISC.java @@ -0,0 +1,35 @@ +package com.seaofnodes.simple.node.cpus.riscv; + +import com.seaofnodes.simple.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.type.Type; +import com.seaofnodes.simple.type.TypeInteger; +import java.io.ByteArrayOutputStream; + +// corresponds to slt,sltu,slti,sltiu, seqz +// RISCV doesn't have rflags. +public class SetRISC extends MachConcreteNode implements MachNode{ + final String _bop; // One of <,<=,== + SetRISC( Node cmp, String bop ) { + super(cmp); + _inputs.setLen(1); // Pop the cmp inputs + // Replace with the matched cmp + _inputs.push(cmp); + _bop = bop; + } + @Override public RegMask regmap(int i) { assert i==1; return riscv.FLAGS_MASK; } + @Override public RegMask outregmap() { return riscv.RMASK; } + + // Encoding is appended into the byte array; size is returned + @Override public int encoding(ByteArrayOutputStream bytes) { + throw Utils.TODO(); + } + + @Override public void asm(CodeGen code, SB sb) { + sb.p(code.reg(this)); + String src = code.reg(in(1)); + if( src!="FLAGS" ) sb.p(" = ").p(src); + } + + @Override public String op() { return "set"+_bop; } +} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SllIRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SllIRISC.java new file mode 100644 index 00000000..8927c086 --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SllIRISC.java @@ -0,0 +1,40 @@ +package com.seaofnodes.simple.node.cpus.riscv; + +import com.seaofnodes.simple.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.node.cpus.x86_64_v2.x86_64_v2; +import com.seaofnodes.simple.type.Type; +import com.seaofnodes.simple.type.TypeInteger; +import java.io.ByteArrayOutputStream; +import java.util.BitSet; + +public class SllIRISC extends MachConcreteNode implements MachNode{ + final TypeInteger _ti; + SllIRISC(Node sll, TypeInteger ti) {super(sll); _inputs.pop(); _ti = ti;} + + // Register mask allowed on input i. + // This is the normal calling convention + @Override public RegMask regmap(int i) { + //assert i==1; + return riscv.RMASK; } + + // Register mask allowed as a result. 0 for no register. + @Override public RegMask outregmap() { return riscv.RMASK; } + + // Output is same register as input#1 + @Override public int twoAddress() { return 0; } + + // Encoding is appended into the byte array; size is returned + @Override public int encoding(ByteArrayOutputStream bytes) { + throw Utils.TODO(); + } + + // General form + // General form: "slli rd, rs1, imm" + @Override public void asm(CodeGen code, SB sb) { + sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" << #"); + _ti.print(sb); + } + + @Override public String op() { return "slli"; } +} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SllRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SllRISC.java new file mode 100644 index 00000000..69538433 --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SllRISC.java @@ -0,0 +1,36 @@ +package com.seaofnodes.simple.node.cpus.riscv; + +import com.seaofnodes.simple.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.type.Type; +import com.seaofnodes.simple.type.TypeInteger; +import java.io.ByteArrayOutputStream; +import java.util.BitSet; + +public class SllRISC extends MachConcreteNode implements MachNode{ + SllRISC(Node sll) {super(sll);} + + // Register mask allowed on input i. + // This is the normal calling convention + @Override public RegMask regmap(int i) { + //assert i==1; + return riscv.RMASK; } + // Register mask allowed as a result. 0 for no register. + @Override public RegMask outregmap() { return riscv.RMASK; } + + // Output is same register as input#1 + @Override public int twoAddress() { return 1; } + + // Encoding is appended into the byte array; size is returned + @Override public int encoding(ByteArrayOutputStream bytes) { + throw Utils.TODO(); + } + + // General form + // General form: "sll rd, rs1, rs2" + @Override public void asm(CodeGen code, SB sb) { + sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))); + } + + @Override public String op() { return "sll"; } +} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SraIRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SraIRISC.java new file mode 100644 index 00000000..654dd2ae --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SraIRISC.java @@ -0,0 +1,43 @@ +package com.seaofnodes.simple.node.cpus.riscv; + +import com.seaofnodes.simple.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.node.cpus.x86_64_v2.x86_64_v2; +import com.seaofnodes.simple.type.Type; +import com.seaofnodes.simple.type.TypeInteger; +import java.io.ByteArrayOutputStream; +import java.util.BitSet; +import java.lang.StringBuilder; + + +// Right Shift Arithmetic(immediate) +public class SraIRISC extends MachConcreteNode implements MachNode{ + final TypeInteger _ti; + SraIRISC(Node sra, TypeInteger ti) {super(sra); _inputs.pop(); _ti = ti;} + + // Register mask allowed on input i. + // This is the normal calling convention + @Override public RegMask regmap(int i) { + // assert i==1; + return riscv.RMASK; } + + // Register mask allowed as a result. 0 for no register. + @Override public RegMask outregmap() { return riscv.RMASK; } + + // Output is same register as input#1 + @Override public int twoAddress() { return 0; } + + // Encoding is appended into the byte array; size is returned + @Override public int encoding(ByteArrayOutputStream bytes) { + throw Utils.TODO(); + } + + // General form + // General form: "srai rd, rs1, imm" + @Override public void asm(CodeGen code, SB sb) { + sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" >> #"); + _ti.print(sb); + } + + @Override public String op() { return "srai"; } +} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SraRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SraRISC.java new file mode 100644 index 00000000..2d50ceaa --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SraRISC.java @@ -0,0 +1,42 @@ +package com.seaofnodes.simple.node.cpus.riscv; + +import com.seaofnodes.simple.CodeGen; +import com.seaofnodes.simple.RegMask; +import com.seaofnodes.simple.SB; +import com.seaofnodes.simple.Utils; +import com.seaofnodes.simple.node.MachConcreteNode; +import com.seaofnodes.simple.node.MachNode; +import com.seaofnodes.simple.node.Node; +import com.seaofnodes.simple.type.TypeInteger; + +import java.io.ByteArrayOutputStream; + +// Right Shift Arithmetic +public class SraRISC extends MachConcreteNode implements MachNode { + + SraRISC(Node sra) {super(sra);} + + // Register mask allowed on input i. + // This is the normal calling convention + @Override public RegMask regmap(int i) { + // assert i==1; + return riscv.RMASK; } + // Register mask allowed as a result. 0 for no register. + @Override public RegMask outregmap() { return riscv.RMASK; } + + // Output is same register as input#1 + @Override public int twoAddress() { return 1; } + + // Encoding is appended into the byte array; size is returned + @Override public int encoding(ByteArrayOutputStream bytes) { + throw Utils.TODO(); + } + + // General form + // General form: "sra rd, rs1, rs2" + @Override public void asm(CodeGen code, SB sb) { + sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))); + } + + @Override public String op() { return "sra"; } +} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SrlIRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SrlIRISC.java new file mode 100644 index 00000000..f7ad6c25 --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SrlIRISC.java @@ -0,0 +1,43 @@ +package com.seaofnodes.simple.node.cpus.riscv; + +import com.seaofnodes.simple.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.node.cpus.x86_64_v2.x86_64_v2; +import com.seaofnodes.simple.type.Type; +import com.seaofnodes.simple.type.TypeInteger; +import java.io.ByteArrayOutputStream; +import java.util.BitSet; +import java.lang.StringBuilder; + + +// Right Shift Logical Immediate +public class SrlIRISC extends MachConcreteNode implements MachNode { + final TypeInteger _ti; + SrlIRISC(Node srl, TypeInteger ti) {super(srl); _inputs.pop(); _ti = ti;} + + + // Register mask allowed on input i. + // This is the normal calling convention + @Override public RegMask regmap(int i) { + // assert i==1; + return riscv.RMASK; } + // Register mask allowed as a result. 0 for no register. + @Override public RegMask outregmap() { return riscv.RMASK; } + + // Output is same register as input#1 + @Override public int twoAddress() { return 1; } + + // Encoding is appended into the byte array; size is returned + @Override public int encoding(ByteArrayOutputStream bytes) { + throw Utils.TODO(); + } + + // General form + // General form: "srli dst << #imm" + @Override public void asm(CodeGen code, SB sb) { + sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" >> #"); + _ti.print(sb); + } + + @Override public String op() { return "srli"; } +} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SrlRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SrlRISC.java new file mode 100644 index 00000000..25d62688 --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SrlRISC.java @@ -0,0 +1,38 @@ +package com.seaofnodes.simple.node.cpus.riscv; + +import com.seaofnodes.simple.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.type.Type; +import com.seaofnodes.simple.type.TypeInteger; +import java.io.ByteArrayOutputStream; +import java.util.BitSet; +import java.lang.StringBuilder; + +// Right Shift Logical +public class SrlRISC extends MachConcreteNode implements MachNode { + SrlRISC(Node srli) {super(srli);} + + // Register mask allowed on input i. + // This is the normal calling convention + @Override public RegMask regmap(int i) { + // assert i==1; + return riscv.RMASK; } + // Register mask allowed as a result. 0 for no register. + @Override public RegMask outregmap() { return riscv.RMASK; } + + // Output is same register as input#1 + @Override public int twoAddress() { return 1; } + + // Encoding is appended into the byte array; size is returned + @Override public int encoding(ByteArrayOutputStream bytes) { + throw Utils.TODO(); + } + + // General form + // General form: "srl rd, rs1, rs2" + @Override public void asm(CodeGen code, SB sb) { + sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))); + } + + @Override public String op() { return "srl"; } +} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/StoreRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/StoreRISC.java new file mode 100644 index 00000000..1bd40d0c --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/StoreRISC.java @@ -0,0 +1,40 @@ +package com.seaofnodes.simple.node.cpus.riscv; + +import com.seaofnodes.simple.CodeGen; +import com.seaofnodes.simple.RegMask; +import com.seaofnodes.simple.SB; +import com.seaofnodes.simple.Utils; +import com.seaofnodes.simple.node.MachConcreteNode; +import com.seaofnodes.simple.node.MachNode; +import com.seaofnodes.simple.node.Node; +import com.seaofnodes.simple.node.StoreNode; + +import java.io.ByteArrayOutputStream; +import java.util.BitSet; + + +// sw rs2,offset(rs1) +public class StoreRISC extends MemOpRISC { + StoreRISC( StoreNode st) { + super(st, st); + } + + @Override public RegMask regmap(int i) { + return i>=2 && i<=4 ? riscv.RMASK : null; + } + + + // Register mask allowed as a result. 0 for no register. + @Override public RegMask outregmap() { return null; } + + // Encoding is appended into the byte array; size is returned + @Override public int encoding(ByteArrayOutputStream bytes) { + throw Utils.TODO(); + } + + @Override public void asm(CodeGen code, SB sb) { + + } + + @Override public String op() { return "st"+_sz; } +} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SubFRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SubFRISC.java new file mode 100644 index 00000000..e6a0b8e4 --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SubFRISC.java @@ -0,0 +1,30 @@ +package com.seaofnodes.simple.node.cpus.riscv; + +import com.seaofnodes.simple.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.type.TypeInteger; +import java.io.ByteArrayOutputStream; + +public class SubFRISC extends MachConcreteNode implements MachNode{ + SubFRISC(Node subf) {super(subf);} + + // Register mask allowed on input i. + @Override public RegMask regmap(int i) { assert i==1 || i==2; return riscv.FMASK; } + // Register mask allowed as a result. 0 for no register. + @Override public RegMask outregmap() { return riscv.FMASK; } + // Output is same register as input#1 + @Override public int twoAddress() { return 0; } + + // Encoding is appended into the byte array; size is returned + @Override public int encoding(ByteArrayOutputStream bytes) { + throw Utils.TODO(); + } + + // Default on double precision for now(64 bits) + // General form: "fsub.d rd = src1 - src2 + @Override public void asm(CodeGen code, SB sb) { + sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" + ").p(code.reg(in(2))); + } + + @Override public String op() { return "subf"; } +} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SubRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SubRISC.java new file mode 100644 index 00000000..6fb0e0ef --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SubRISC.java @@ -0,0 +1,29 @@ +package com.seaofnodes.simple.node.cpus.riscv; + +import com.seaofnodes.simple.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.type.TypeInteger; +import java.io.ByteArrayOutputStream; + +public class SubRISC extends MachConcreteNode implements MachNode { + SubRISC( Node sub ) { super(sub); } + + // Register mask allowed on input i. + @Override public RegMask regmap(int i) { assert i==1 || i==2; return riscv.RMASK; } + // Register mask allowed as a result. 0 for no register. + @Override public RegMask outregmap() { return riscv.RMASK; } + // Output is same register as input#1 + @Override public int twoAddress() { return 0; } + + // Encoding is appended into the byte array; size is returned + @Override public int encoding(ByteArrayOutputStream bytes) { + throw Utils.TODO(); + } + + // General form: "sub # rd = rs1 - rs2" + @Override public void asm(CodeGen code, SB sb) { + sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" - ").p(code.reg(in(2))); + } + + @Override public String op() { return "sub"; } +} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/TFPRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/TFPRISC.java new file mode 100644 index 00000000..b508541a --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/TFPRISC.java @@ -0,0 +1,43 @@ +package com.seaofnodes.simple.node.cpus.riscv; + + +import com.seaofnodes.simple.*; +import com.seaofnodes.simple.node.ConstantNode; +import com.seaofnodes.simple.node.MachNode; +import com.seaofnodes.simple.node.cpus.x86_64_v2.TFPX86; +import com.seaofnodes.simple.node.cpus.x86_64_v2.x86_64_v2; +import com.seaofnodes.simple.type.Type; +import com.seaofnodes.simple.type.TypeFunPtr; + +import java.io.ByteArrayOutputStream; + +public class TFPRISC extends ConstantNode implements MachNode{ + TFPRISC(ConstantNode con) {super(con);} + // Register mask allowed on input i. 0 for no register. + @Override public RegMask regmap(int i) { return null; } + // General int registers + @Override public RegMask outregmap() { return x86_64_v2.RMASK; } + + // Encoding is appended into the byte array; size is returned + @Override public int encoding(ByteArrayOutputStream bytes) { + throw Utils.TODO(); + } + + // Human-readable form appended to the SB. Things like the encoding, + // indentation, leading address or block labels not printed here. + // Just something like "ld4\tR17=[R18+12] // Load array base". + // General form: "op\tdst=src+src" + @Override public void asm(CodeGen code, SB sb) { + String reg = code.reg(this); + if( _con == Type.NIL ) + sb.p(reg).p(",").p(reg); + else + _con.print(sb.p(reg).p(" #")); + } + + @Override public String op() { + if( _con == Type.NIL ) + return "xor"; + return "ldx"; // Some fancier encoding + } +} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/XorIRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/XorIRISC.java new file mode 100644 index 00000000..3d53ee64 --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/XorIRISC.java @@ -0,0 +1,45 @@ +package com.seaofnodes.simple.node.cpus.riscv; + +import com.seaofnodes.simple.CodeGen; +import com.seaofnodes.simple.RegMask; +import com.seaofnodes.simple.SB; +import com.seaofnodes.simple.Utils; +import com.seaofnodes.simple.node.MachConcreteNode; +import com.seaofnodes.simple.node.MachNode; +import com.seaofnodes.simple.node.Node; +import com.seaofnodes.simple.node.cpus.x86_64_v2.x86_64_v2; +import com.seaofnodes.simple.type.TypeInteger; + +import java.io.ByteArrayOutputStream; + +public class XorIRISC extends MachConcreteNode implements MachNode{ + final TypeInteger _ti; + XorIRISC(Node xor, TypeInteger ti) {super(xor); _inputs.pop(); _ti = ti;} + + // Register mask allowed on input i. + // This is the normal calling convention + @Override public RegMask regmap(int i) { + assert i==1 || i==2; + + return riscv.RMASK; } + + // Register mask allowed as a result. 0 for no register. + @Override public RegMask outregmap() { return riscv.RMASK; } + + // Output is same register as input#1 + @Override public int twoAddress() { return 1; } + + // Encoding is appended into the byte array; size is returned + @Override public int encoding(ByteArrayOutputStream bytes) { + throw Utils.TODO(); + } + + // General form + // General form: "rd = rs1 ^ imm" + @Override public void asm(CodeGen code, SB sb) { + sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" ^ #"); + _ti.print(sb); + } + + @Override public String op() { return "xori"; } +} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/XorRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/XorRISC.java new file mode 100644 index 00000000..2f34529e --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/XorRISC.java @@ -0,0 +1,43 @@ +package com.seaofnodes.simple.node.cpus.riscv; + +import com.seaofnodes.simple.CodeGen; +import com.seaofnodes.simple.RegMask; +import com.seaofnodes.simple.SB; +import com.seaofnodes.simple.Utils; +import com.seaofnodes.simple.node.MachConcreteNode; +import com.seaofnodes.simple.node.MachNode; +import com.seaofnodes.simple.node.Node; +import com.seaofnodes.simple.type.TypeInteger; + +import java.io.ByteArrayOutputStream; + +public class XorRISC extends MachConcreteNode implements MachNode { + + XorRISC(Node or) { + super(or); + } + + // Register mask allowed on input i. + // This is the normal calling convention + @Override public RegMask regmap(int i) { + assert i==1 || i==2; + return riscv.RMASK; + } + + // Register mask allowed as a result. 0 for no register. + @Override public RegMask outregmap() { return riscv.RMASK; } + + // Output is same register as input#1 + @Override public int twoAddress() { return 0; } + + // Encoding is appended into the byte array; size is returned + @Override public int encoding(ByteArrayOutputStream bytes) { + throw Utils.TODO(); + } + + // General form + // General form: # # rd = rs1 ^ rs2 + @Override public void asm(CodeGen code, SB sb) { + sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))); + } +} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/riscv.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/riscv.java new file mode 100644 index 00000000..ad6e1c0f --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/riscv.java @@ -0,0 +1,332 @@ +package com.seaofnodes.simple.node.cpus.riscv; + +import com.seaofnodes.simple.Machine; +import com.seaofnodes.simple.RegMask; +import com.seaofnodes.simple.Utils; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.node.cpus.x86_64_v2.*; +import com.seaofnodes.simple.type.*; + +public class riscv extends Machine{ + @Override public String name() {return "riscv";} + + // Using ABI names instead of register names + public static int ZERO = 0, RA = 1, SP = 2, GP = 3, TP = 4, T0 = 5, T1 = 6, T2 = 7; + public static int S0 = 8, S1 = 9, A0 = 10, A1 = 11, A2 = 12, A3 = 13, A4 = 14, A5 = 15; + public static int A6 = 16, A7 = 17, S2 = 18, S3 = 19, S4 = 20, S5 = 21, S6 = 22, S7 = 23; + public static int S8 = 24, S9 = 25, S10 = 26, S11 = 27, T3 = 28, T4 = 29, T5 = 30, T6 = 31; + + // FP registers + public static int F0 = 32, F1 = 33, F2 = 34, F3 = 35, F4 = 36, F5 = 37, F6 = 38, F7 = 39; + public static int FS0 = 40, FS1 = 41, FA0 = 42, FA1 = 43, FA2 = 44, FA3 = 45, FA4 = 46, FA5 = 47; + public static int FA6 = 48, FA7 = 49, FS2 = 50, FS3 = 51, FS4 = 52, FS5 = 53, FS6 = 54, FS7 = 55; + public static int FS8 = 56, FS9 = 57, FS10 = 58, FS11 = 59, FT8 = 60, FT9 = 61, FT10 = 62, FT11 = 63; + + public static int FLAGS = 64; + + // General purpose register mask: pointers and ints, not floats + public static RegMask RMASK = new RegMask(0b11111111111111111111111111111111); + // Float mask from(ft0–ft11) + public static RegMask FMASK = new RegMask(0b11111111111111111111111111111111); + + // Return single int/ptr register + public static RegMask RET_MASK = new RegMask(A0); + + // Arguments masks + public static RegMask A0_MASK = new RegMask(1L< addf(addf); + case AddNode add -> add(add); + case AndNode and -> and(and); + case BoolNode bool -> cmp(bool); + case CallEndNode cend -> new CallEndNode((CallNode) cend.in(0)); + case CallNode call -> call(call); + case CProjNode c -> new CProjNode(c); + case ConstantNode con -> con(con); + case DivFNode divf -> new DivFRISC(divf); + case DivNode div -> new DivRISC(div); + case FunNode fun -> new FunRISC(fun); + case IfNode iff -> jmp(iff); + case LoadNode ld -> ld(ld); + case MemMergeNode mem -> new MemMergeNode(mem); + case MulFNode mulf -> new MulFRISC(mulf); + case MulNode mul -> mul(mul); + case NewNode nnn -> new NewRISC(nnn); + case OrNode or -> or(or); + case ParmNode parm -> new ParmRISC(parm); + case BulkMemPhiNode phi -> new BulkMemPhiNode(phi); + case MemPhiNode phi -> new MemPhiNode(phi); + case PhiNode phi -> new PhiNode(phi); + case ProjNode prj -> prj(prj); + case ReadOnlyNode read -> new ReadOnlyNode(read); + case ReturnNode ret -> new RetRISC(ret, ret.fun()); + case SarNode sar -> sra(sar); + case ShlNode shl -> sll(shl); + case ShrNode shr -> srl(shr); + case StartNode start -> new StartNode(start); + case StopNode stop -> new StopNode(stop); + case StoreNode st -> st(st); + case SubFNode subf -> new SubFRISC(subf); + case SubNode sub -> sub(sub); + case ToFloatNode tfn -> i2f8(tfn); + case ToIntegerNode cvt -> new F8ToIRISC(cvt); + case XorNode xor -> xor(xor); + + case LoopNode loop -> new LoopNode(loop); + case RegionNode region -> new RegionNode(region); + default -> throw Utils.TODO(); + }; + } + + private Node addf(AddFNode addf) { + return new AddFRISC(addf); + } + + private Node add(AddNode add) { + Node rhs = add.in(2); + if( rhs instanceof ConstantNode off && off._con instanceof TypeInteger toff ) { + return new AddIRISC(add, toff); + } + return new AddRISC(add); + } + + private Node and(AndNode and) { + if( and.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti ) + return new AndIRISC(and, ti); + return new AndRISC(and); + } + + private Node cmp(BoolNode bool) { + Node cmp = _cmp(bool); + return new SetRISC(cmp, bool.op()); + } + + private Node call(CallNode call) { + if( call.fptr() instanceof ConstantNode con && con._con instanceof TypeFunPtr tfp ) + return new CallRISC(call, tfp); + return new CallRRISC(call); + } + + private Node _cmp(BoolNode bool) { + Node rhs = bool.in(2); + + return rhs instanceof ConstantNode con && con._con instanceof TypeInteger ti + ? new CmpIRISC(bool, ti) + : new CmpRISC(bool); + } + + private Node con( ConstantNode con ) { + return switch( con._con ) { + case TypeInteger ti -> new IntRISC(con); + case TypeFloat tf -> new IntRISC(con); + case TypeFunPtr tfp -> new TFPRISC(con); + case TypeMemPtr tmp -> new ConstantNode(con); + case TypeNil tn -> throw Utils.TODO(); + // TOP, BOTTOM, XCtrl, Ctrl, etc. Never any executable code. + case Type t -> new ConstantNode(con); + }; + } + + private Node jmp( IfNode iff ) { + // If/Bool combos will match to a Cmp/Set which sets flags. + // Most general arith ops will also set flags, which the Jmp needs directly. + // Loads do not set the flags, and will need an explicit TEST + if( !(iff.in(1) instanceof BoolNode) ) + iff.setDef(1,new BoolNode.EQ(iff.in(1),new ConstantNode(TypeInteger.ZERO))); + return new CBranchRISC(iff, ((BoolNode)iff.in(1)).op()); + } + + private Node or(OrNode or) { + if(or.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti) + return new OrIRISC(or, ti); + + return new OrRISC(or); + } + + private Node xor(XorNode xor) { + if(xor.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti) + return new XorIRISC(xor, ti); + return new XorRISC(xor); + } + + private Node sra(SarNode sar) { + if( sar.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti) + return new SraIRISC(sar, ti); + return new SraRISC(sar); + } + + private Node srl(ShrNode shr) { + if( shr.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti) + return new SrlIRISC(shr, ti); + return new SrlRISC(shr); + } + + private Node sll(ShlNode sll) { + if( sll.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti) + return new SllIRISC(sll, ti); + return new SllRISC(sll); + } + + private Node sub(SubNode sub) { + return sub.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti + ? new AddIRISC(sub, TypeInteger.constant(-ti.value())) + : new SubRISC(sub); + } + + private Node i2f8(ToFloatNode tfn) { + assert tfn.in(1)._type instanceof TypeInteger ti; + return new I2F8RISC(tfn); + } + + private Node prj(ProjNode prj) { + return new ProjRISC(prj); + } + + private Node ld(LoadNode ld) { + return new LoadRISC(ld); + } + + private Node st(StoreNode st) { + return new StoreRISC(st); + } + + private Node mul(MulNode mul) { + return mul.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti + ? new MulIRISC(mul, ti) + : new MulRISC(mul); + } +} + diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddFMemX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddFMemX86.java new file mode 100644 index 00000000..4bb24fa3 --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddFMemX86.java @@ -0,0 +1,33 @@ +package com.seaofnodes.simple.node.cpus.x86_64_v2; + +import com.seaofnodes.simple.CodeGen; +import com.seaofnodes.simple.Utils; +import com.seaofnodes.simple.RegMask; +import com.seaofnodes.simple.SB; +import com.seaofnodes.simple.node.*; + +public class AddFMemX86 extends MemOpX86 { + AddFMemX86( AddFNode add, LoadNode ld , Node base, Node idx, int off, int scale, Node val ) { + super(add,ld, base, idx, off, scale, 0, val ); + } + + @Override public RegMask regmap(int i) { + if( i==1 ) return null; // Memory + if( i==2 ) return x86_64_v2.RMASK; // base + if( i==3 ) return x86_64_v2.RMASK; // index + if( i==4 ) return x86_64_v2.XMASK; // value + throw Utils.TODO(); + } + + // Register mask allowed as a result. 0 for no register. + @Override public RegMask outregmap() { return x86_64_v2.XMASK; } + + + // General form: "add dst = src + [base + idx<<2 + 12]" + @Override public void asm(CodeGen code, SB sb) { + sb.p(code.reg(this)).p(" = "); + sb.p(val()==null ? "#"+_imm : code.reg(val())).p(" + "); + asm_address(code,sb); + } + @Override public String op() { return "addf"+_sz; } +} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddFX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddFX86.java new file mode 100644 index 00000000..a3f5c7b3 --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddFX86.java @@ -0,0 +1,29 @@ +package com.seaofnodes.simple.node.cpus.x86_64_v2; + +import com.seaofnodes.simple.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.type.TypeInteger; +import java.io.ByteArrayOutputStream; + +public class AddFX86 extends MachConcreteNode implements MachNode { + AddFX86( Node addf ) { super(addf); } + + // Register mask allowed on input i. + @Override public RegMask regmap(int i) { assert i==1 || i==2; return x86_64_v2.XMASK; } + // Register mask allowed as a result. 0 for no register. + @Override public RegMask outregmap() { return x86_64_v2.XMASK; } + // Output is same register as input#1 + @Override public int twoAddress() { return 1; } + + // Encoding is appended into the byte array; size is returned + @Override public int encoding(ByteArrayOutputStream bytes) { + throw Utils.TODO(); + } + + // General form: "addf dst += src" + @Override public void asm(CodeGen code, SB sb) { + sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" + ").p(code.reg(in(2))); + } + + @Override public String op() { return "addf"; } +} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddIX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddIX86.java new file mode 100644 index 00000000..6a332ff5 --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddIX86.java @@ -0,0 +1,39 @@ +package com.seaofnodes.simple.node.cpus.x86_64_v2; + +import com.seaofnodes.simple.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.type.TypeInteger; +import java.io.ByteArrayOutputStream; + +public class AddIX86 extends MachConcreteNode implements MachNode { + final TypeInteger _ti; + AddIX86( Node add, TypeInteger ti ) { + super(add); + _inputs.pop(); + _ti = ti; + } + + // Register mask allowed on input i. + @Override public RegMask regmap(int i) { + // assert i== i; + return x86_64_v2.WMASK; } + // Register mask allowed as a result. 0 for no register. + @Override public RegMask outregmap() { return x86_64_v2.WMASK; } + // Output is same register as input#1 + @Override public int twoAddress() { return 1; } + + // Encoding is appended into the byte array; size is returned + @Override public int encoding(ByteArrayOutputStream bytes) { + throw Utils.TODO(); + } + + // General form: "addi dst += #imm" + @Override public void asm(CodeGen code, SB sb) { + sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" + #"); + _ti.print(sb); + } + + @Override public String op() { + return _ti.value() == 1 ? "inc" : (_ti.value() == -1 ? "dec" : "addi"); + } +} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddMemX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddMemX86.java new file mode 100644 index 00000000..7d45d757 --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddMemX86.java @@ -0,0 +1,25 @@ +package com.seaofnodes.simple.node.cpus.x86_64_v2; + +import com.seaofnodes.simple.CodeGen; +import com.seaofnodes.simple.RegMask; +import com.seaofnodes.simple.SB; +import com.seaofnodes.simple.node.*; + +public class AddMemX86 extends MemOpX86 { + AddMemX86( AddNode add, LoadNode ld , Node base, Node idx, int off, int scale, int imm, Node val ) { + super(add,ld, base, idx, off, scale, imm, val ); + } + + @Override public String op() { return "add"+_sz; } + + // Register mask allowed as a result. 0 for no register. + @Override public RegMask outregmap() { return x86_64_v2.WMASK; } + + + // General form: "add dst = src + [base + idx<<2 + 12]" + @Override public void asm(CodeGen code, SB sb) { + sb.p(code.reg(this)).p(" = "); + sb.p(val()==null ? "#"+_imm : code.reg(val())).p(" + "); + asm_address(code,sb); + } +} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddX86.java new file mode 100644 index 00000000..61aa5a9e --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddX86.java @@ -0,0 +1,29 @@ +package com.seaofnodes.simple.node.cpus.x86_64_v2; + +import com.seaofnodes.simple.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.type.TypeInteger; +import java.io.ByteArrayOutputStream; + +public class AddX86 extends MachConcreteNode implements MachNode { + AddX86( Node add ) { super(add); } + + // Register mask allowed on input i. + @Override public RegMask regmap(int i) { assert i==1 || i==2; return x86_64_v2.RMASK; } + // Register mask allowed as a result. 0 for no register. + @Override public RegMask outregmap() { return x86_64_v2.WMASK; } + // Output is same register as input#1 + @Override public int twoAddress() { return 1; } + + // Encoding is appended into the byte array; size is returned + @Override public int encoding(ByteArrayOutputStream bytes) { + throw Utils.TODO(); + } + + // General form: "add dst += src" + @Override public void asm(CodeGen code, SB sb) { + sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" + ").p(code.reg(in(2))); + } + + @Override public String op() { return "add"; } +} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AndIX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AndIX86.java new file mode 100644 index 00000000..f2ee1c66 --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AndIX86.java @@ -0,0 +1,42 @@ +package com.seaofnodes.simple.node.cpus.x86_64_v2; + +import com.seaofnodes.simple.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.type.Type; +import com.seaofnodes.simple.type.TypeInteger; +import java.io.ByteArrayOutputStream; +import java.util.BitSet; +import java.lang.StringBuilder; + +public class AndIX86 extends MachConcreteNode implements MachNode { + final TypeInteger _ti; + AndIX86(Node and, TypeInteger ti) { + super(and); + _inputs.pop(); + _ti = ti; + } + + // Register mask allowed on input i. + // This is the normal calling convention + @Override public RegMask regmap(int i) { assert i==1; return x86_64_v2.WMASK; } + // Register mask allowed as a result. 0 for no register. + @Override public RegMask outregmap() { return x86_64_v2.WMASK; } + + + // Output is same register as input#1 + @Override public int twoAddress() { return 1; } + + // Encoding is appended into the byte array; size is returned + @Override public int encoding(ByteArrayOutputStream bytes) { + throw Utils.TODO(); + } + + // General form + // General form: "andi dst & #imm" + @Override public void asm(CodeGen code, SB sb) { + sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" & #"); + _ti.print(sb); + } + + @Override public String op() { return "andi"; } +} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AndX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AndX86.java new file mode 100644 index 00000000..45cf2b22 --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AndX86.java @@ -0,0 +1,33 @@ +package com.seaofnodes.simple.node.cpus.x86_64_v2; + +import com.seaofnodes.simple.*; +import com.seaofnodes.simple.node.*; +import java.io.ByteArrayOutputStream; + +public class AndX86 extends MachConcreteNode implements MachNode{ + AndX86(Node and) { + super(and); + } + // Register mask allowed on input i. + // This is the normal calling convention + @Override public RegMask regmap(int i) { assert i==1; return x86_64_v2.WMASK; } + + @Override public RegMask outregmap() { return x86_64_v2.WMASK; } + + // Output is same register as input#1 + @Override public int twoAddress() { return 1; } + + // Encoding is appended into the byte array; size is returned + @Override public int encoding(ByteArrayOutputStream bytes) { + throw Utils.TODO(); + } + + // General form + // General form: "and dst & #imm" + @Override public void asm(CodeGen code, SB sb) { + sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))); + } + + @Override public String op() { return "and"; } +} + diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CallEndX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CallEndX86.java new file mode 100644 index 00000000..55ab90c9 --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CallEndX86.java @@ -0,0 +1,37 @@ +package com.seaofnodes.simple.node.cpus.x86_64_v2; + +import com.seaofnodes.simple.SB; +import com.seaofnodes.simple.Utils; +import com.seaofnodes.simple.CodeGen; +import com.seaofnodes.simple.RegMask; +import com.seaofnodes.simple.node.CallEndNode; +import com.seaofnodes.simple.node.MachNode; +import com.seaofnodes.simple.type.TypeFloat; +import com.seaofnodes.simple.type.TypeFunPtr; +import java.io.ByteArrayOutputStream; + +public class CallEndX86 extends CallEndNode implements MachNode { + final TypeFunPtr _tfp; + CallEndX86( CallEndNode cend ) { + super(cend); + _tfp = (TypeFunPtr)cend.call().fptr()._type; + } + + @Override public String label() { return op(); } + @Override public RegMask regmap(int i) { return null; } + @Override public RegMask outregmap() { throw Utils.TODO(); } + @Override public RegMask outregmap(int idx) { + if( idx != 2 ) return null; + return _tfp._ret instanceof TypeFloat ? x86_64_v2.RET_FMASK : x86_64_v2.RET_MASK; + } + + // Encoding is appended into the byte array; size is returned + @Override public int encoding(ByteArrayOutputStream bytes) { + throw Utils.TODO(); + } + + @Override public void asm(CodeGen code, SB sb) { } + + @Override public String op() { return "cend"; } + +} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CallRX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CallRX86.java new file mode 100644 index 00000000..99260a27 --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CallRX86.java @@ -0,0 +1,31 @@ +package com.seaofnodes.simple.node.cpus.x86_64_v2; + +import com.seaofnodes.simple.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.type.Type; +import com.seaofnodes.simple.type.TypeFunPtr; +import java.io.ByteArrayOutputStream; + +public class CallRX86 extends CallNode implements MachNode { + CallRX86( CallNode call ) { super(call); } + + @Override public String label() { return op(); } + @Override public RegMask regmap(int i) { + return i==_inputs._len + ? x86_64_v2.WMASK // Function call target + : x86_64_v2.callInMask(tfp(),i); // Normal argument + } + @Override public RegMask outregmap() { return x86_64_v2.RET_MASK; } + + // Encoding is appended into the byte array; size is returned + @Override public int encoding(ByteArrayOutputStream bytes) { + throw Utils.TODO(); + } + + @Override public void asm(CodeGen code, SB sb) { + sb.p(code.reg(fptr())); + } + + @Override public String op() { return "callr"; } + +} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CallX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CallX86.java new file mode 100644 index 00000000..b3e3bbe8 --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CallX86.java @@ -0,0 +1,41 @@ +package com.seaofnodes.simple.node.cpus.x86_64_v2; + +import com.seaofnodes.simple.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.type.Type; +import com.seaofnodes.simple.type.TypeFunPtr; +import java.io.ByteArrayOutputStream; +import java.util.BitSet; + +public class CallX86 extends CallNode implements MachNode { + final TypeFunPtr _tfp; + final String _name; + CallX86( CallNode call, TypeFunPtr tfp ) { + super(call); + _inputs.pop(); // Pop constant target + assert tfp.isConstant(); + _tfp = tfp; + _name = CodeGen.CODE.link(tfp)._name; + } + + @Override public String label() { return op(); } + @Override public RegMask regmap(int i) { + return x86_64_v2.callInMask(tfp(),i); // Normal argument + } + @Override public RegMask outregmap() { return x86_64_v2.RET_MASK; } + + @Override public String name() { return _name; } + @Override public TypeFunPtr tfp() { return _tfp; } + + // Encoding is appended into the byte array; size is returned + @Override public int encoding(ByteArrayOutputStream bytes) { + throw Utils.TODO(); + } + + @Override public void asm(CodeGen code, SB sb) { + sb.p(_name); + } + + @Override public String op() { return "call"; } + +} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CmpIX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CmpIX86.java new file mode 100644 index 00000000..ea526f33 --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CmpIX86.java @@ -0,0 +1,43 @@ +package com.seaofnodes.simple.node.cpus.x86_64_v2; + +import com.seaofnodes.simple.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.type.Type; +import com.seaofnodes.simple.type.TypeInteger; +import java.io.ByteArrayOutputStream; + +// Compare immediate. Sets flags.(RFLAGS) +public class CmpIX86 extends MachConcreteNode implements MachNode { + final int _imm; + final String _bop; + CmpIX86( BoolNode bool, TypeInteger ti ) { + super(bool); + _inputs.pop(); // Toss ideal ConstantNode away, embedded into machine op + _bop = bool.op(); // One of <,<=,== + _imm = (int)ti.value(); + assert _imm == ti.value(); + } + CmpIX86( Node cmp, double ignore ) { + super(cmp); + _bop = "=="; + _imm = 0; + } + + @Override public RegMask regmap(int i) { assert i==1; return x86_64_v2.RMASK; } + @Override public RegMask outregmap() { return x86_64_v2.FLAGS_MASK; } + + // Encoding is appended into the byte array; size is returned + @Override public int encoding(ByteArrayOutputStream bytes) { + throw Utils.TODO(); + } + + @Override public void asm(CodeGen code, SB sb) { + String dst = code.reg(this); + if( dst!="FLAGS" ) sb.p(dst).p(" = "); + sb.p(code.reg(in(1))); + if( _imm != 0 ) sb.p(", #").p(_imm); + } + + @Override public String op() { return _imm==0 ? "test" : "cmp"; } + +} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CmpMemX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CmpMemX86.java new file mode 100644 index 00000000..873ca9df --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CmpMemX86.java @@ -0,0 +1,33 @@ +package com.seaofnodes.simple.node.cpus.x86_64_v2; + +import com.seaofnodes.simple.CodeGen; +import com.seaofnodes.simple.RegMask; +import com.seaofnodes.simple.SB; +import com.seaofnodes.simple.node.*; + +public class CmpMemX86 extends MemOpX86 { + final boolean _invert; // Op switched LHS, RHS + CmpMemX86( BoolNode bool, LoadNode ld , Node base, Node idx, int off, int scale, int imm, Node val, boolean invert ) { + super(bool,ld, base, idx, off, scale, imm, val ); + _invert = invert; + } + + // Register mask allowed as a result. 0 for no register. + @Override public RegMask outregmap() { return x86_64_v2.FLAGS_MASK; } + + // General form: "cmp dst = src, [base + idx<<2 + 12]" + @Override public void asm(CodeGen code, SB sb) { + String dst = code.reg(this); + if( dst!="FLAGS" ) sb.p(dst).p(" = "); + if( _invert ) asm_address(code,sb).p(","); + if( val()==null ) { + if( _imm!=0 ) sb.p("#").p(_imm); + } else { + sb.p(code.reg(val())); + } + if( !_invert ) asm_address(code,sb.p(", ")); + } + + @Override public String op() { return ((val()==null && _imm==0) ? "test" : "cmp") + _sz; } + +} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CmpX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CmpX86.java new file mode 100644 index 00000000..d8ebb12d --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CmpX86.java @@ -0,0 +1,28 @@ +package com.seaofnodes.simple.node.cpus.x86_64_v2; + +import com.seaofnodes.simple.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.type.TypeInteger; +import java.io.ByteArrayOutputStream; + +// Compare. Sets flags.(RFLAGS) +public class CmpX86 extends MachConcreteNode implements MachNode { + CmpX86( Node cmp ) { super(cmp); } + + @Override public RegMask regmap(int i) { assert i==1 || i==2; return x86_64_v2.RMASK; } + @Override public RegMask outregmap() { return x86_64_v2.FLAGS_MASK; } + + // Encoding is appended into the byte array; size is returned + @Override public int encoding(ByteArrayOutputStream bytes) { + throw Utils.TODO(); + } + + // General form: "cmp reg1, reg2" + @Override public void asm(CodeGen code, SB sb) { + String dst = code.reg(this); + if( dst!="FLAGS" ) sb.p(dst).p(" = "); + sb.p(code.reg(in(1))).p(", ").p(code.reg(in(2))); + } + + @Override public String op() { return "cmp"; } +} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/DivFX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/DivFX86.java new file mode 100644 index 00000000..d73bfd5b --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/DivFX86.java @@ -0,0 +1,33 @@ +package com.seaofnodes.simple.node.cpus.x86_64_v2; + +import com.seaofnodes.simple.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.type.TypeInteger; +import java.io.ByteArrayOutputStream; + +public class DivFX86 extends MachConcreteNode implements MachNode { + DivFX86( Node divf) { super(divf); } + + // Register mask allowed on input i. + @Override public RegMask regmap(int i) { assert i==1 || i==2; return x86_64_v2.XMASK; } + // Register mask allowed as a result. 0 for no register. + @Override public RegMask outregmap() { return x86_64_v2.XMASK; } + // Output is same register as input#1 + @Override public int twoAddress() { return 1; } + + // Encoding is appended into the byte array; size is returned + @Override public int encoding(ByteArrayOutputStream bytes) { + throw Utils.TODO(); + } + + + // General form: "divf dst /= src" + @Override public void asm(CodeGen code, SB sb) { + sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" / ").p(code.reg(in(2))); + } + + @Override public String op() { + // 8 bytes = 64 bits + return "divf"; + } +} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/DivX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/DivX86.java new file mode 100644 index 00000000..1dc20017 --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/DivX86.java @@ -0,0 +1,31 @@ +package com.seaofnodes.simple.node.cpus.x86_64_v2; + +import com.seaofnodes.simple.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.type.TypeInteger; +import java.io.ByteArrayOutputStream; + +public class DivX86 extends MachConcreteNode implements MachNode { + DivX86( Node div ) { super(div); } + + // Register mask allowed on input i. + @Override public RegMask regmap(int i) { + assert i==1 || i==2; + return x86_64_v2.RMASK; } + // Register mask allowed as a result. 0 for no register. + @Override public RegMask outregmap() { return x86_64_v2.WMASK; } + // Output is same register as input#1 + @Override public int twoAddress() { return 1; } + + // Encoding is appended into the byte array; size is returned + @Override public int encoding(ByteArrayOutputStream bytes) { + throw Utils.TODO(); + } + + // General form: "div dst /= src" + @Override public void asm(CodeGen code, SB sb) { + sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" / ").p(code.reg(in(2))); + } + + @Override public String op() { return "div"; } +} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/F8ToIX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/F8ToIX86.java new file mode 100644 index 00000000..33a77d4c --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/F8ToIX86.java @@ -0,0 +1,27 @@ +package com.seaofnodes.simple.node.cpus.x86_64_v2; + +import com.seaofnodes.simple.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.type.TypeInteger; +import java.io.ByteArrayOutputStream; + +public class F8ToIX86 extends MachConcreteNode implements MachNode { + F8ToIX86(Node f8toi ) { super(f8toi); } + + // Register mask allowed on input i. + @Override public RegMask regmap(int i) { assert i==1; return x86_64_v2.XMASK; } + // Register mask allowed as a result. 0 for no register. + @Override public RegMask outregmap() { return x86_64_v2.WMASK; } + + // Encoding is appended into the byte array; size is returned + @Override public int encoding(ByteArrayOutputStream bytes) { + throw Utils.TODO(); + } + + + @Override public void asm(CodeGen code, SB sb) { + sb.p(code.reg(this)).p(" = ").p("(int)").p(code.reg(in(1))); + } + + @Override public String op() { return "f8toi"; } +} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/FltX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/FltX86.java new file mode 100644 index 00000000..5e1190da --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/FltX86.java @@ -0,0 +1,36 @@ +package com.seaofnodes.simple.node.cpus.x86_64_v2; + +import com.seaofnodes.simple.CodeGen; +import com.seaofnodes.simple.RegMask; +import com.seaofnodes.simple.SB; +import com.seaofnodes.simple.Utils; +import com.seaofnodes.simple.node.ConstantNode; +import com.seaofnodes.simple.node.MachNode; + +import java.io.ByteArrayOutputStream; + +public class FltX86 extends ConstantNode implements MachNode { + FltX86(ConstantNode con ) { super(con); } + + // Register mask allowed on input i. 0 for no register. + @Override public RegMask regmap(int i) { return null; } + // General int registers + @Override public RegMask outregmap() { return x86_64_v2.XMASK; } + + // Encoding is appended into the byte array; size is returned + @Override public int encoding(ByteArrayOutputStream bytes) { + throw Utils.TODO(); + } + + // Human-readable form appended to the SB. Things like the encoding, + // indentation, leading address or block labels not printed here. + // Just something like "ld4\tR17=[R18+12] // Load array base". + // General form: "op\tdst=src+src" + @Override public void asm(CodeGen code, SB sb) { + _con.print(sb.p(code.reg(this)).p(" #")); + } + + @Override public String op() { + return "fld"; // Some fancier encoding + } +} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/FunX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/FunX86.java new file mode 100644 index 00000000..8b0f016a --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/FunX86.java @@ -0,0 +1,21 @@ +package com.seaofnodes.simple.node.cpus.x86_64_v2; + +import com.seaofnodes.simple.RegMask; +import com.seaofnodes.simple.Utils; +import com.seaofnodes.simple.node.FunNode; +import com.seaofnodes.simple.node.MachNode; +import java.io.ByteArrayOutputStream; + +public class FunX86 extends FunNode implements MachNode { + FunX86( FunNode fun ) { super(fun); } + + // Register mask allowed on input i. 0 for no register. + @Override public RegMask regmap(int i) { return null; } + // Register mask allowed as a result. 0 for no register. + @Override public RegMask outregmap() { return null; } + + // Encoding is appended into the byte array; size is returned + @Override public int encoding(ByteArrayOutputStream bytes) { + throw Utils.TODO(); + } +} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/I2f8X86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/I2f8X86.java new file mode 100644 index 00000000..983f817d --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/I2f8X86.java @@ -0,0 +1,28 @@ +package com.seaofnodes.simple.node.cpus.x86_64_v2; + +import com.seaofnodes.simple.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.type.TypeInteger; +import java.io.ByteArrayOutputStream; + +public class I2f8X86 extends MachConcreteNode implements MachNode { + I2f8X86(Node i2f8 ) { super(i2f8); } + + // Register mask allowed on input i. + @Override public RegMask regmap(int i) { assert i==1; return x86_64_v2.WMASK; } + // Register mask allowed as a result. 0 for no register. + @Override public RegMask outregmap() { return x86_64_v2.XMASK; } + + // Encoding is appended into the byte array; size is returned + @Override public int encoding(ByteArrayOutputStream bytes) { + throw Utils.TODO(); + } + + // General form: "i2f8 (flt)int_value" + + @Override public void asm(CodeGen code, SB sb) { + sb.p(code.reg(this)).p(" = ").p("(flt)").p(code.reg(in(1))); + } + + @Override public String op() { return "i2f8"; } +} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/IntX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/IntX86.java new file mode 100644 index 00000000..566a7293 --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/IntX86.java @@ -0,0 +1,45 @@ +package com.seaofnodes.simple.node.cpus.x86_64_v2; + +import com.seaofnodes.simple.*; +import com.seaofnodes.simple.node.ConstantNode; +import com.seaofnodes.simple.node.MachNode; +import com.seaofnodes.simple.type.Type; +import com.seaofnodes.simple.type.TypeInteger; + +import java.io.ByteArrayOutputStream; + +// Integer constants +public class IntX86 extends ConstantNode implements MachNode { + + IntX86( ConstantNode con ) { + super(con); + } + + // Register mask allowed on input i. 0 for no register. + @Override public RegMask regmap(int i) { return null; } + // General int registers + @Override public RegMask outregmap() { return x86_64_v2.WMASK; } + + // Encoding is appended into the byte array; size is returned + @Override public int encoding(ByteArrayOutputStream bytes) { + throw Utils.TODO(); + } + + // Human-readable form appended to the SB. Things like the encoding, + // indentation, leading address or block labels not printed here. + // Just something like "ld4\tR17=[R18+12] // Load array base". + // General form: "op\tdst=src+src" + @Override public void asm(CodeGen code, SB sb) { + String reg = code.reg(this); + if( _con == Type.NIL || _con == TypeInteger.ZERO ) + sb.p(reg).p(",").p(reg); + else + _con.print(sb.p(reg).p(" #")); + } + + @Override public String op() { + if( _con == Type.NIL || _con == TypeInteger.ZERO ) + return "xor"; + return "ldi"; // Some fancier encoding + } +} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/JmpX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/JmpX86.java new file mode 100644 index 00000000..2262e847 --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/JmpX86.java @@ -0,0 +1,46 @@ +package com.seaofnodes.simple.node.cpus.x86_64_v2; + +import com.seaofnodes.simple.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.type.Type; +import com.seaofnodes.simple.type.TypeInteger; +import java.io.ByteArrayOutputStream; + +// Jump on flags, uses flags +public class JmpX86 extends IfNode implements MachNode { + final String _bop; + JmpX86( IfNode iff, String bop ) { + super(iff); + _bop = bop; + } + + @Override public String label() { return op(); } + + @Override public void postSelect() { + Node set = in(1); + Node cmp = set.in(1); + // Bypass an expected Set and just reference the cmp directly + if( set instanceof SetX86 && (cmp instanceof CmpX86 || cmp instanceof CmpIX86 || cmp instanceof CmpMemX86) ) + _inputs.set(1,cmp); + else + throw Utils.TODO(); + } + @Override public RegMask regmap(int i) { assert i==1; return x86_64_v2.FLAGS_MASK; } + @Override public RegMask outregmap() { return null; } + + // Encoding is appended into the byte array; size is returned + @Override public int encoding(ByteArrayOutputStream bytes) { + throw Utils.TODO(); + } + + @Override public void asm(CodeGen code, SB sb) { + String src = code.reg(in(1)); + if( src!="FLAGS" ) sb.p(src); + } + + @Override public String op() { return "j"+_bop; } + + @Override public String comment() { + return "L"+cproj(1)._nid+", L"+cproj(0)._nid; + } +} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/LeaX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/LeaX86.java new file mode 100644 index 00000000..a49b8d7f --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/LeaX86.java @@ -0,0 +1,42 @@ +package com.seaofnodes.simple.node.cpus.x86_64_v2; + +import com.seaofnodes.simple.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.type.TypeInteger; +import java.io.ByteArrayOutputStream; + +public class LeaX86 extends MachConcreteNode implements MachNode { + final int _scale; + final long _offset; + LeaX86( Node add, Node base, Node idx, int scale, long offset ) { + super(add); + assert scale==1 || scale==2 || scale==4 || scale==8; + _inputs.pop(); + _inputs.pop(); + _inputs.push(base); + _inputs.push(idx); + _scale = scale; + _offset = offset; + } + + // Register mask allowed on input i. + @Override public RegMask regmap(int i) { assert i==1 || i==2; return x86_64_v2.RMASK; } + // Register mask allowed as a result. 0 for no register. + @Override public RegMask outregmap() { return x86_64_v2.WMASK; } + + // Encoding is appended into the byte array; size is returned + @Override public int encoding(ByteArrayOutputStream bytes) { + throw Utils.TODO(); + } + + // General form: "lea dst = base + 4*idx + 12" + @Override public void asm(CodeGen code, SB sb) { + sb.p(code.reg(this)).p(" = "); + if( in(1) != null ) + sb.p(code.reg(in(1))).p(" + "); + sb.p(code.reg(in(2))).p("*").p(_scale); + if( _offset!=0 ) sb.p(" + #").p(_offset); + } + + @Override public String op() { return "lea"; } +} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/LoadX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/LoadX86.java new file mode 100644 index 00000000..02a9a4fe --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/LoadX86.java @@ -0,0 +1,25 @@ +package com.seaofnodes.simple.node.cpus.x86_64_v2; + +import com.seaofnodes.simple.CodeGen; +import com.seaofnodes.simple.RegMask; +import com.seaofnodes.simple.SB; +import com.seaofnodes.simple.node.LoadNode; +import com.seaofnodes.simple.node.Node; + +public class LoadX86 extends MemOpX86 { + LoadX86( LoadNode ld, Node base, Node idx, int off, int scale ) { + super(ld,ld, base, idx, off, scale, 0); + } + + @Override public String op() { return "ld"+_sz; } + + // Register mask allowed as a result. 0 for no register. + @Override public RegMask outregmap() { return x86_64_v2.WMASK; } + + + // General form: "ldN dst,[base + idx<<2 + 12]" + @Override public void asm(CodeGen code, SB sb) { + sb.p(code.reg(this)).p(","); + asm_address(code,sb); + } +} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MemAddX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MemAddX86.java new file mode 100644 index 00000000..a6cdbd05 --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MemAddX86.java @@ -0,0 +1,28 @@ +package com.seaofnodes.simple.node.cpus.x86_64_v2; + +import com.seaofnodes.simple.CodeGen; +import com.seaofnodes.simple.RegMask; +import com.seaofnodes.simple.SB; +import com.seaofnodes.simple.node.*; + +public class MemAddX86 extends MemOpX86 { + MemAddX86( StoreNode st, Node base, Node idx, int off, int scale, int imm, Node val ) { + super(st, st, base, idx, off, scale, imm, val ); + } + + // Register mask allowed as a result. 0 for no register. + @Override public RegMask outregmap() { return null; } + + + // General form: "add [base + idx<<2 + 12] += src" + @Override public void asm(CodeGen code, SB sb) { + asm_address(code,sb); + if( val()==null ) { + if( _imm != 1 && _imm != -1 ) sb.p(" += #").p(_imm); + } else sb.p(" += ").p(code.reg(val())); + } + + @Override public String op() { + return (_imm == 1 ? "inc" : (_imm == -1 ? "dec" : "add")) + _sz; + } +} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MemOpX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MemOpX86.java new file mode 100644 index 00000000..5a79fb09 --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MemOpX86.java @@ -0,0 +1,90 @@ +package com.seaofnodes.simple.node.cpus.x86_64_v2; + +import com.seaofnodes.print.ExprPrinter; + +import com.seaofnodes.simple.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.type.*; +import java.io.ByteArrayOutputStream; +import java.lang.StringBuilder; + +// Generic X86 memory operand base. +// inputs: +// ctrl - will appear for possibly RCE (i.e. array ops) +// mem - memory dependence edge +// base - Basic object pointer +// idx/null - Scaled index offset, or null if none +// val/null - Value to store, as part of a op-to-mem or op-from-mem. Null for loads, or if an immediate is being used +// Constants: +// offset - offset added to base. Can be zero. +// scale - scale on index; only 0,1,2,4,8 allowed, 0 is only when index is null +// imm - immediate value to store or op-to-mem, and only when val is null +public abstract class MemOpX86 extends MemOpNode implements MachNode { + final int _off; // Limit 32 bits + final int _scale; // Limit 0,1,2,3 + final int _imm; // Limit 32 bits + final char _sz = (char)('0'+(1<<_declaredType.log_size())); + MemOpX86( Node op, MemOpNode mop, Node base, Node idx, int off, int scale, int imm ) { + super(op,mop); + assert base._type instanceof TypeMemPtr && !(base instanceof AddNode); + assert (idx==null && scale==0) || (idx!=null && 0<= scale && scale<=3); + + // Copy memory parts from eg the LoadNode over the opcode, e.g. an Add + if( op != mop ) { + _inputs.set(0,mop.in(0)); // Control from mem op + _inputs.set(1,mop.in(1)); // Memory from mem op + _inputs.set(2,base); // Base handed in + } + + assert ptr() == base; + _inputs.setX(3,idx); + _off = off; + _scale = scale; + _imm = imm; + } + + // Store-based flavors have a value edge + MemOpX86( Node op, MemOpNode mop, Node base, Node idx, int off, int scale, int imm, Node val ) { + this(op,mop,base,idx,off,scale,imm); + _inputs.setX(4,val); + } + + Node idx() { return in(3); } + Node val() { return in(4); } + + @Override protected ExprPrinter _printMach(ExprPrinter p) { return p.p(".").p(_name); } + + @Override public boolean isMem() { return _type instanceof TypeMem; } + + @Override public String label() { return op(); } + @Override public Type compute() { throw Utils.TODO(); } + @Override public Node idealize() { throw Utils.TODO(); } + + // Register mask allowed on input i. + @Override public RegMask regmap(int i) { + if( i==1 ) return null; // Memory + if( i==2 ) return x86_64_v2.RMASK; // base + if( i==3 ) return x86_64_v2.RMASK; // index + if( i==4 ) return x86_64_v2.RMASK; // value + return null; // Trailing anti-dependence edges + } + // Register mask allowed as a result. 0 for no register. + @Override public RegMask outregmap() { throw Utils.TODO(); } + + @Override public int encoding(ByteArrayOutputStream bytes) { throw Utils.TODO(); } + + + // "[base + idx<<2 + 12]" + SB asm_address(CodeGen code, SB sb) { + sb.p("[").p(code.reg(ptr())); + if( idx() != null ) { + sb.p("+").p(code.reg(idx())); + if( _scale != 0 ) + sb.p("<<").p(_scale); + } + if( _off != 0 ) + sb.p("+").p(_off); + return sb.p("]"); + } + +} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MulFX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MulFX86.java new file mode 100644 index 00000000..aef813e1 --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MulFX86.java @@ -0,0 +1,30 @@ +package com.seaofnodes.simple.node.cpus.x86_64_v2; + +import com.seaofnodes.simple.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.type.TypeFloat; +import com.seaofnodes.simple.type.TypeInteger; +import java.io.ByteArrayOutputStream; + +public class MulFX86 extends MachConcreteNode implements MachNode { + MulFX86( Node mulf) { super(mulf); } + + // Register mask allowed on input i. + @Override public RegMask regmap(int i) { assert i==1 || i==2; return x86_64_v2.XMASK; } + // Register mask allowed as a result. 0 for no register. + @Override public RegMask outregmap() { return x86_64_v2.XMASK; } + // Output is same register as input#1 + @Override public int twoAddress() { return 1; } + + // Encoding is appended into the byte array; size is returned + @Override public int encoding(ByteArrayOutputStream bytes) { + throw Utils.TODO(); + } + + // General form: "mulf dst *= src" + @Override public void asm(CodeGen code, SB sb) { + sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" * ").p(code.reg(in(2))); + } + + @Override public String op() { return "mulf"; } +} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MulIX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MulIX86.java new file mode 100644 index 00000000..e9eb17fc --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MulIX86.java @@ -0,0 +1,36 @@ +package com.seaofnodes.simple.node.cpus.x86_64_v2; + +import com.seaofnodes.simple.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.type.TypeInteger; +import java.io.ByteArrayOutputStream; + +public class MulIX86 extends MachConcreteNode implements MachNode { + final TypeInteger _ti; + MulIX86(Node mul, TypeInteger ti) {super(mul); _inputs.pop(); _ti = ti;} + + // Register mask allowed on input i. + // This is the normal calling convention + @Override public RegMask regmap(int i) { + assert i==1; + return x86_64_v2.WMASK; } + // Register mask allowed as a result. 0 for no register. + @Override public RegMask outregmap() { return x86_64_v2.WMASK; } + + + // Output is same register as input#1 + @Override public int twoAddress() { return 1; } + + // Encoding is appended into the byte array; size is returned + @Override public int encoding(ByteArrayOutputStream bytes) { + throw Utils.TODO(); + } + // General form + // General form: "muli dst * #imm" + @Override public void asm(CodeGen code, SB sb) { + sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" * #"); + _ti.print(sb); + } + + @Override public String op() { return "muli"; } +} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MulX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MulX86.java new file mode 100644 index 00000000..15fd9496 --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MulX86.java @@ -0,0 +1,30 @@ +package com.seaofnodes.simple.node.cpus.x86_64_v2; + +import com.seaofnodes.simple.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.type.TypeInteger; +import java.io.ByteArrayOutputStream; + +public class MulX86 extends MachConcreteNode implements MachNode { + + MulX86( Node mul ) { super(mul); } + + // Register mask allowed on input i. + @Override public RegMask regmap(int i) { assert i==1 || i==2; return x86_64_v2.RMASK; } + // Register mask allowed as a result. 0 for no register. + @Override public RegMask outregmap() { return x86_64_v2.WMASK; } + // Output is same register as input#1 + @Override public int twoAddress() { return 1; } + + // Encoding is appended into the byte array; size is returned + @Override public int encoding(ByteArrayOutputStream bytes) { + throw Utils.TODO(); + } + + // General form: "mul dst *= src" + @Override public void asm(CodeGen code, SB sb) { + sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" * ").p(code.reg(in(2))); + } + + @Override public String op() { return "mul"; } +} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/NewX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/NewX86.java new file mode 100644 index 00000000..c70e1c54 --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/NewX86.java @@ -0,0 +1,40 @@ +package com.seaofnodes.simple.node.cpus.x86_64_v2; + +import com.seaofnodes.simple.*; +import com.seaofnodes.simple.node.*; +import java.io.ByteArrayOutputStream; + +public class NewX86 extends NewNode implements MachNode { + + // A pre-zeroed chunk of memory. + NewX86( NewNode nnn ) { super(nnn); } + + // Register mask allowed on input i, the size + @Override public RegMask regmap(int i) { + // Size + if( i==2 ) return x86_64_v2.RDI_MASK; + // Memory has no register + return null; + } + @Override public RegMask outregmap() { throw Utils.TODO(); } + + // Register mask allowed as a result. Pointer result in standard calling + // convention. + @Override public RegMask outregmap(int i) { + if( i == 0 ) return x86_64_v2.RET_MASK; + // Memory has no register + return null; + } + + // Encoding is appended into the byte array; size is returned + @Override public int encoding(ByteArrayOutputStream bytes) { + throw Utils.TODO(); + } + + // General form: "alloc #bytes" + @Override public void asm(CodeGen code, SB sb) { + sb.p(code.reg(size())); + } + + @Override public String op() { return "alloc"; } +} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/OrIX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/OrIX86.java new file mode 100644 index 00000000..d3940838 --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/OrIX86.java @@ -0,0 +1,44 @@ +package com.seaofnodes.simple.node.cpus.x86_64_v2; + +import com.seaofnodes.simple.CodeGen; +import com.seaofnodes.simple.RegMask; +import com.seaofnodes.simple.SB; +import com.seaofnodes.simple.Utils; +import com.seaofnodes.simple.node.MachConcreteNode; +import com.seaofnodes.simple.node.MachNode; +import com.seaofnodes.simple.node.Node; +import com.seaofnodes.simple.type.TypeInteger; + +import java.io.ByteArrayOutputStream; + +public class OrIX86 extends MachConcreteNode implements MachNode { + + final TypeInteger _ti; + OrIX86(Node or, TypeInteger ti) {super(or); _inputs.pop(); _ti = ti;} + + // Register mask allowed on input i. + // This is the normal calling convention + @Override public RegMask regmap(int i) { + assert i==1; + return x86_64_v2.WMASK; } + // Register mask allowed as a result. 0 for no register. + @Override public RegMask outregmap() { return x86_64_v2.WMASK; } + + + // Output is same register as input#1 + @Override public int twoAddress() { return 1; } + + // Encoding is appended into the byte array; size is returned + @Override public int encoding(ByteArrayOutputStream bytes) { + throw Utils.TODO(); + } + + // General form + // General form: "ori ori | #imm" + @Override public void asm(CodeGen code, SB sb) { + sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" | #"); + _ti.print(sb); + } + + @Override public String op() { return "ori"; } +} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/OrX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/OrX86.java new file mode 100644 index 00000000..7c25bdae --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/OrX86.java @@ -0,0 +1,33 @@ +package com.seaofnodes.simple.node.cpus.x86_64_v2; + +import com.seaofnodes.simple.*; +import com.seaofnodes.simple.node.*; +import java.io.ByteArrayOutputStream; + +public class OrX86 extends MachConcreteNode implements MachNode{ + OrX86(Node or) { + super(or); + } + + // Register mask allowed on input i. + // This is the normal calling convention + @Override public RegMask regmap(int i) { assert i==1; return x86_64_v2.WMASK; } + + @Override public RegMask outregmap() { return x86_64_v2.WMASK; } + + // Output is same register as input#1 + @Override public int twoAddress() { return 1; } + + // Encoding is appended into the byte array; size is returned + @Override public int encoding(ByteArrayOutputStream bytes) { + throw Utils.TODO(); + } + + // General form + // General form: "and dst & #imm" + @Override public void asm(CodeGen code, SB sb) { + sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))); + } + + @Override public String op() { return "or"; } +} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ParmX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ParmX86.java new file mode 100644 index 00000000..0fcc922d --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ParmX86.java @@ -0,0 +1,23 @@ +package com.seaofnodes.simple.node.cpus.x86_64_v2; + +import com.seaofnodes.simple.RegMask; +import com.seaofnodes.simple.node.ParmNode; +import com.seaofnodes.simple.node.MachNode; +import java.io.ByteArrayOutputStream; + +public class ParmX86 extends ParmNode implements MachNode { + final RegMask _rmask; + ParmX86( ParmNode parm ) { + super(parm); + // default to int + _rmask = x86_64_v2.callInMask(fun().sig(),_idx); + } + + // Register mask allowed on input i. 0 for no register. + @Override public RegMask regmap(int i) { return null; } + // Register mask allowed as a result. Calling convention register + @Override public RegMask outregmap() { return _rmask; } + + // Encoding is appended into the byte array. Returns size + @Override public int encoding(ByteArrayOutputStream bytes) { return 0; } +} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ProjX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ProjX86.java new file mode 100644 index 00000000..77fd7d34 --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ProjX86.java @@ -0,0 +1,25 @@ +package com.seaofnodes.simple.node.cpus.x86_64_v2; + +import com.seaofnodes.simple.RegMask; +import com.seaofnodes.simple.SB; +import com.seaofnodes.simple.Utils; +import com.seaofnodes.simple.node.ProjNode; +import com.seaofnodes.simple.node.MachNode; +import java.io.ByteArrayOutputStream; + +public class ProjX86 extends ProjNode implements MachNode { + + ProjX86( ProjNode p ) { super(p); } + + // Register mask allowed on input i. 0 for no register. + @Override public RegMask regmap(int i) { return null; } + // Register mask allowed as a result. 0 for no register. + @Override public RegMask outregmap() { + return ((MachNode)in(0)).outregmap(_idx); + } + + // Encoding is appended into the byte array; size is returned + @Override public int encoding(ByteArrayOutputStream bytes) { + throw Utils.TODO(); + } +} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/RetX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/RetX86.java new file mode 100644 index 00000000..21db209c --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/RetX86.java @@ -0,0 +1,48 @@ +package com.seaofnodes.simple.node.cpus.x86_64_v2; + +import com.seaofnodes.simple.*; +import com.seaofnodes.simple.node.FunNode; +import com.seaofnodes.simple.node.ReturnNode; +import com.seaofnodes.simple.node.MachNode; +import java.io.ByteArrayOutputStream; + +// Return +public class RetX86 extends ReturnNode implements MachNode { + RetX86( ReturnNode ret, FunNode fun ) { super(ret, fun); fun.setRet(this); } + + // Correct Nodes outside the normal edges + @Override public void postSelect() { + FunNode fun = (FunNode)rpc().in(0); + _fun = fun; + fun.setRet(this); + } + + // Register mask allowed on input i. + // This is the normal calling convention + @Override public RegMask regmap(int i) { + return switch( i ) { + case 0 -> null; + case 1 -> null; + case 2 -> x86_64_v2.RET_MASK; + case 3 -> null; // RPC is always on stack + default -> throw Utils.TODO(); + }; + } + // Register mask allowed as a result. 0 for no register. + @Override public RegMask outregmap() { return null; } + + // Encoding is appended into the byte array; size is returned + @Override public int encoding(ByteArrayOutputStream bytes) { + throw Utils.TODO(); + } + + // Human-readable form appended to the SB. Things like the encoding, + // indentation, leading address or block labels not printed here. + // Just something like "ld4\tR17=[R18+12] // Load array base". + // General form: "op\tdst=src+src" + @Override public void asm(CodeGen code, SB sb) { + sb.p(code.reg(in(2))); + } + + @Override public String op() { return "ret"; } +} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SarIX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SarIX86.java new file mode 100644 index 00000000..f5337ee8 --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SarIX86.java @@ -0,0 +1,37 @@ +package com.seaofnodes.simple.node.cpus.x86_64_v2; + +import com.seaofnodes.simple.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.type.TypeInteger; +import java.io.ByteArrayOutputStream; + +// Arithmetic Right Shift +public class SarIX86 extends MachConcreteNode implements MachNode { + final TypeInteger _ti; + SarIX86(Node sar, TypeInteger ti) {super(sar); _inputs.pop(); _ti = ti;} + + // Register mask allowed on input i. + // This is the normal calling convention + @Override public RegMask regmap(int i) { + // assert i==1; + return x86_64_v2.RMASK; } + // Register mask allowed as a result. 0 for no register. + @Override public RegMask outregmap() { return x86_64_v2.WMASK; } + + // Output is same register as input#1 + @Override public int twoAddress() { return 1; } + + // Encoding is appended into the byte array; size is returned + @Override public int encoding(ByteArrayOutputStream bytes) { + throw Utils.TODO(); + } + + // General form + // General form: "sari dst << #imm" + @Override public void asm(CodeGen code, SB sb) { + sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" >> #"); + _ti.print(sb); + } + + @Override public String op() { return "sari"; } +} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SarX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SarX86.java new file mode 100644 index 00000000..cb0b18bf --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SarX86.java @@ -0,0 +1,42 @@ +package com.seaofnodes.simple.node.cpus.x86_64_v2; + +import com.seaofnodes.simple.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.type.Type; +import com.seaofnodes.simple.type.TypeInteger; +import java.io.ByteArrayOutputStream; +import java.util.BitSet; +import java.lang.StringBuilder; + +public class SarX86 extends MachConcreteNode implements MachNode{ + SarX86(Node sar) {super(sar);} + + // Register mask allowed on input i. + // This is the normal calling convention + + // CL register must be used for input(2) + @Override public RegMask regmap(int i) { + if(i == 1) return x86_64_v2.WMASK; + if(i == 2) return x86_64_v2.RCX_MASK; + throw Utils.TODO(); + } + + @Override public RegMask outregmap() { return x86_64_v2.WMASK; } + + // Output is same register as input#1 + @Override public int twoAddress() { return 1; } + + // Encoding is appended into the byte array; size is returned + @Override public int encoding(ByteArrayOutputStream bytes) { + throw Utils.TODO(); + } + + // General form + // General form: "sar dst >> src" + @Override public void asm(CodeGen code, SB sb) { + sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))); + } + + @Override public String op() { return "sar"; } + +} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SetX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SetX86.java new file mode 100644 index 00000000..25367e03 --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SetX86.java @@ -0,0 +1,39 @@ +package com.seaofnodes.simple.node.cpus.x86_64_v2; + +import com.seaofnodes.simple.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.type.Type; +import com.seaofnodes.simple.type.TypeInteger; +import java.io.ByteArrayOutputStream; + +// Compare immediate. Sets flags. +// Corresponds to the x86 instruction "sete && setne". +// Use result of comparison without jump. +public class SetX86 extends MachConcreteNode implements MachNode { + final String _bop; // One of <,<=,== + // Constructor expects input is an X86 and not an Ideal node. + SetX86( Node cmp, String bop ) { + super(cmp); + _inputs.setLen(1); // Pop the cmp inputs + // Replace with the matched cmp + _inputs.push(cmp); + _bop = bop; + } + + @Override public RegMask regmap(int i) { assert i==1; return x86_64_v2.FLAGS_MASK; } + @Override public RegMask outregmap() { return x86_64_v2.WMASK; } + + // Encoding is appended into the byte array; size is returned + @Override public int encoding(ByteArrayOutputStream bytes) { + throw Utils.TODO(); + } + + @Override public void asm(CodeGen code, SB sb) { + sb.p(code.reg(this)); + String src = code.reg(in(1)); + if( src!="FLAGS" ) sb.p(" = ").p(src); + } + + @Override public String op() { return "set"+_bop; } + +} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShlIX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShlIX86.java new file mode 100644 index 00000000..61e0d262 --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShlIX86.java @@ -0,0 +1,38 @@ +package com.seaofnodes.simple.node.cpus.x86_64_v2; + +import com.seaofnodes.simple.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.type.Type; +import com.seaofnodes.simple.type.TypeInteger; +import java.io.ByteArrayOutputStream; +import java.util.BitSet; + +public class ShlIX86 extends MachConcreteNode implements MachNode{ + final TypeInteger _ti; + ShlIX86(Node shl, TypeInteger ti) {super(shl); _inputs.pop(); _ti = ti;} + + // Register mask allowed on input i. + // This is the normal calling convention + @Override public RegMask regmap(int i) { + //assert i==1; + return x86_64_v2.RMASK; } + // Register mask allowed as a result. 0 for no register. + @Override public RegMask outregmap() { return x86_64_v2.WMASK; } + + // Output is same register as input#1 + @Override public int twoAddress() { return 1; } + + // Encoding is appended into the byte array; size is returned + @Override public int encoding(ByteArrayOutputStream bytes) { + throw Utils.TODO(); + } + + // General form + // General form: "shli dst << #imm" + @Override public void asm(CodeGen code, SB sb) { + sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" << #"); + _ti.print(sb); + } + + @Override public String op() { return "shli"; } +} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShlX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShlX86.java new file mode 100644 index 00000000..b95f9f46 --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShlX86.java @@ -0,0 +1,39 @@ +package com.seaofnodes.simple.node.cpus.x86_64_v2; + +import com.seaofnodes.simple.*; +import com.seaofnodes.simple.node.*; +import java.io.ByteArrayOutputStream; + +public class ShlX86 extends MachConcreteNode implements MachNode { + ShlX86(Node shl) { + super(shl); + }; + + // Register mask allowed on input i. + // This is the normal calling convention + + // CL register must be used for input(2) + @Override public RegMask regmap(int i) { + if(i == 1) return x86_64_v2.WMASK; + if(i == 2) return x86_64_v2.RCX_MASK; + throw Utils.TODO(); + } + + @Override public RegMask outregmap() { return x86_64_v2.WMASK; } + + // Output is same register as input#1 + @Override public int twoAddress() { return 1; } + + // Encoding is appended into the byte array; size is returned + @Override public int encoding(ByteArrayOutputStream bytes) { + throw Utils.TODO(); + } + + // General form + // General form: "shl dst << src" + @Override public void asm(CodeGen code, SB sb) { + sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))); + } + + @Override public String op() { return "shl"; } +} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShrIX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShrIX86.java new file mode 100644 index 00000000..80115ff2 --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShrIX86.java @@ -0,0 +1,40 @@ +package com.seaofnodes.simple.node.cpus.x86_64_v2; + +import com.seaofnodes.simple.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.type.Type; +import com.seaofnodes.simple.type.TypeInteger; +import java.io.ByteArrayOutputStream; +import java.util.BitSet; +import java.lang.StringBuilder; + +// Logical Right Shift +public class ShrIX86 extends MachConcreteNode implements MachNode{ + final TypeInteger _ti; + ShrIX86(Node shr, TypeInteger ti) {super(shr); _inputs.pop(); _ti = ti;} + + // Register mask allowed on input i. + // This is the normal calling convention + @Override public RegMask regmap(int i) { + //assert i==1; + return x86_64_v2.RMASK; } + // Register mask allowed as a result. 0 for no register. + @Override public RegMask outregmap() { return x86_64_v2.WMASK; } + + // Output is same register as input#1 + @Override public int twoAddress() { return 1; } + + // Encoding is appended into the byte array; size is returned + @Override public int encoding(ByteArrayOutputStream bytes) { + throw Utils.TODO(); + } + + // General form + // General form: "shri dst >>> #imm" + @Override public void asm(CodeGen code, SB sb) { + sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" >>> #"); + _ti.print(sb); + } + + @Override public String op() { return "shri"; } +} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShrX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShrX86.java new file mode 100644 index 00000000..43fc730b --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShrX86.java @@ -0,0 +1,38 @@ +package com.seaofnodes.simple.node.cpus.x86_64_v2; + +import com.seaofnodes.simple.*; +import com.seaofnodes.simple.node.*; +import java.io.ByteArrayOutputStream; + +public class ShrX86 extends MachConcreteNode implements MachNode { + ShrX86(Node shr) { + super(shr); + }; + // Register mask allowed on input i. + // This is the normal calling convention + + // CL register must be used for input(2) + @Override public RegMask regmap(int i) { + if(i == 1) return x86_64_v2.WMASK; + if(i == 2) return x86_64_v2.RCX_MASK; + throw Utils.TODO(); + } + + @Override public RegMask outregmap() { return x86_64_v2.WMASK; } + + // Output is same register as input#1 + @Override public int twoAddress() { return 1; } + + // Encoding is appended into the byte array; size is returned + @Override public int encoding(ByteArrayOutputStream bytes) { + throw Utils.TODO(); + } + + // General form + // General form: "shr dst >>> src" + @Override public void asm(CodeGen code, SB sb) { + sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))); + } + + @Override public String op() { return "shr"; } +} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/StoreX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/StoreX86.java new file mode 100644 index 00000000..76bd2afb --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/StoreX86.java @@ -0,0 +1,31 @@ +package com.seaofnodes.simple.node.cpus.x86_64_v2; + +import com.seaofnodes.print.ExprPrinter; + +import com.seaofnodes.simple.CodeGen; +import com.seaofnodes.simple.RegMask; +import com.seaofnodes.simple.SB; +import com.seaofnodes.simple.node.Node; +import com.seaofnodes.simple.node.StoreNode; + +public class StoreX86 extends MemOpX86 { + StoreX86( StoreNode st, Node base, Node idx, int off, int scale, int imm, Node val ) { + super(st,st, base, idx, off, scale, imm, val); + } + + @Override public String op() { return "st"+_sz; } + + @Override protected ExprPrinter _printMach(ExprPrinter p) { + return p.p(".").p(_name).p("=").p(val()==null ? _imm : val()).p(";"); + } + + // Register mask allowed as a result. 0 for no register. + @Override public RegMask outregmap() { return null; } + + // General form: "stN [base + idx<<2 + 12],val" + @Override public void asm(CodeGen code, SB sb) { + asm_address(code,sb).p(","); + if( val()==null ) sb.p("#").p(_imm); + else sb.p(code.reg(val())); + } +} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SubFX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SubFX86.java new file mode 100644 index 00000000..ed19ad79 --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SubFX86.java @@ -0,0 +1,29 @@ +package com.seaofnodes.simple.node.cpus.x86_64_v2; + +import com.seaofnodes.simple.*; +import com.seaofnodes.simple.node.*; +import java.io.ByteArrayOutputStream; + +public class SubFX86 extends MachConcreteNode implements MachNode { + SubFX86( Node subf) { super(subf); } + + + // Register mask allowed on input i. + @Override public RegMask regmap(int i) { assert i==1 || i==2; return x86_64_v2.XMASK; } + // Register mask allowed as a result. 0 for no register. + @Override public RegMask outregmap() { return x86_64_v2.XMASK; } + // Output is same register as input#1 + @Override public int twoAddress() { return 1; } + + // Encoding is appended into the byte array; size is returned + @Override public int encoding(ByteArrayOutputStream bytes) { + throw Utils.TODO(); + } + + // General form: "subf dst -= src" + @Override public void asm(CodeGen code, SB sb) { + sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" - ").p(code.reg(in(2))); + } + + @Override public String op() { return "subf"; } +} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SubX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SubX86.java new file mode 100644 index 00000000..cac59d3b --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SubX86.java @@ -0,0 +1,29 @@ +package com.seaofnodes.simple.node.cpus.x86_64_v2; + +import com.seaofnodes.simple.*; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.type.TypeInteger; +import java.io.ByteArrayOutputStream; + +public class SubX86 extends MachConcreteNode implements MachNode { + SubX86( Node sub ) { super(sub); } + + // Register mask allowed on input i. + @Override public RegMask regmap(int i) { assert i==1 || i==2; return x86_64_v2.RMASK; } + // Register mask allowed as a result. 0 for no register. + @Override public RegMask outregmap() { return x86_64_v2.WMASK; } + // Output is same register as input#1 + @Override public int twoAddress() { return 1; } + + // Encoding is appended into the byte array; size is returned + @Override public int encoding(ByteArrayOutputStream bytes) { + throw Utils.TODO(); + } + + // General form: "sub dst -= src" + @Override public void asm(CodeGen code, SB sb) { + sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" - ").p(code.reg(in(2))); + } + + @Override public String op() { return "sub"; } +} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/TFPX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/TFPX86.java new file mode 100644 index 00000000..c7edfcc4 --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/TFPX86.java @@ -0,0 +1,41 @@ +package com.seaofnodes.simple.node.cpus.x86_64_v2; + +import com.seaofnodes.simple.*; +import com.seaofnodes.simple.node.ConstantNode; +import com.seaofnodes.simple.node.MachNode; +import com.seaofnodes.simple.type.Type; +import com.seaofnodes.simple.type.TypeFunPtr; + +import java.io.ByteArrayOutputStream; + +// Function constants +public class TFPX86 extends ConstantNode implements MachNode { + TFPX86( ConstantNode con ) { super(con); } + // Register mask allowed on input i. 0 for no register. + @Override public RegMask regmap(int i) { return null; } + // General int registers + @Override public RegMask outregmap() { return x86_64_v2.WMASK; } + + // Encoding is appended into the byte array; size is returned + @Override public int encoding(ByteArrayOutputStream bytes) { + throw Utils.TODO(); + } + + // Human-readable form appended to the SB. Things like the encoding, + // indentation, leading address or block labels not printed here. + // Just something like "ld4\tR17=[R18+12] // Load array base". + // General form: "op\tdst=src+src" + @Override public void asm(CodeGen code, SB sb) { + String reg = code.reg(this); + if( _con == Type.NIL ) + sb.p(reg).p(",").p(reg); + else + _con.print(sb.p(reg).p(" #")); + } + + @Override public String op() { + if( _con == Type.NIL ) + return "xor"; + return "ldx"; // Some fancier encoding + } +} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/XorIX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/XorIX86.java new file mode 100644 index 00000000..d71a49dc --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/XorIX86.java @@ -0,0 +1,42 @@ +package com.seaofnodes.simple.node.cpus.x86_64_v2; + +import com.seaofnodes.simple.CodeGen; +import com.seaofnodes.simple.RegMask; +import com.seaofnodes.simple.SB; +import com.seaofnodes.simple.Utils; +import com.seaofnodes.simple.node.MachConcreteNode; +import com.seaofnodes.simple.node.MachNode; +import com.seaofnodes.simple.node.Node; +import com.seaofnodes.simple.type.TypeInteger; + +import java.io.ByteArrayOutputStream; + +public class XorIX86 extends MachConcreteNode implements MachNode { + + final TypeInteger _ti; + XorIX86(Node xor, TypeInteger ti) {super(xor); _inputs.pop(); _ti = ti;} + + // Register mask allowed on input i. + // This is the normal calling convention + @Override public RegMask regmap(int i) { assert i==1; return x86_64_v2.WMASK; } + // Register mask allowed as a result. 0 for no register. + @Override public RegMask outregmap() { return x86_64_v2.WMASK; } + + + // Output is same register as input#1 + @Override public int twoAddress() { return 1; } + + // Encoding is appended into the byte array; size is returned + @Override public int encoding(ByteArrayOutputStream bytes) { + throw Utils.TODO(); + } + + // General form + // General form: "ori xori ^ #imm" + @Override public void asm(CodeGen code, SB sb) { + sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" ^ #"); + _ti.print(sb); + } + + @Override public String op() { return "xori"; } +} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/x86_64_v2.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/x86_64_v2.java new file mode 100644 index 00000000..e7e0b874 --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/x86_64_v2.java @@ -0,0 +1,443 @@ +package com.seaofnodes.simple.node.cpus.x86_64_v2; + +import com.seaofnodes.simple.CodeGen; +import com.seaofnodes.simple.Machine; +import com.seaofnodes.simple.RegMask; +import com.seaofnodes.simple.Utils; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.type.*; + +public class x86_64_v2 extends Machine { + // X86-64 V2. Includes e.g. SSE4.2 and POPCNT. + @Override public String name() { return "x86_64_v2"; } + + public static int RAX = 0, RCX = 1, RDX = 2, RBX = 3, RSP = 4, RBP = 5, RSI = 6, RDI = 7; + public static int R08 = 8, R09 = 9, R10 = 10, R11 = 11, R12 = 12, R13 = 13, R14 = 14, R15 = 15; + public static int FLAGS = 32; + + public static int XMM0 = 16, XMM1 = 17, XMM2 = 18, XMM3 = 19, XMM4 = 20, XMM5 = 21, XMM6 = 22, XMM7 = 23; + public static int XMM8 = 24, XMM9 = 25, XMM10 = 26, XMM11 = 27, XMM12 = 28, XMM13 = 29, XMM14 = 30, XMM15 = 31; + + // General purpose register mask: pointers and ints, not floats + public static RegMask RMASK = new RegMask(0b1111111111111111); + // No RSP in the *write* general set. + public static RegMask WMASK = new RegMask(0b1111111111101111); + // Xmm register mask + public static RegMask XMASK = new RegMask( 0b1111111111111111L << XMM0); + + public static RegMask FLAGS_MASK = new RegMask(1L< CALLINARG_SYSTEMV_INT; + case CodeGen.CallingConv.Win64 -> CALLINARG_WIN64_INT; + }; + if( idx < 2 ) return -1; + if( idx >= cargs.length ) + throw Utils.TODO(); // Pass on stack slot + if( tfp.arg(idx-2) instanceof TypeFloat ) + return XMM0 + (idx-2); + return cargs[idx]; + } + + // WIN64(param passing) + static int[] CALLINARG_WIN64_INT = new int[] { + -1, // Control, no register + -1, // Memory, no register + RCX, + RDX, + R08, + R09, + }; + static RegMask[] CALLINARG_WIN64_MASK = new RegMask[] { + null, // Control, no register + null, // Memory, no register + RCX_MASK, + RDX_MASK, + R08_MASK, + R09_MASK, + }; + + // caller saved(win64) + public static final long WIN64_ABI_CALLER_SAVED = + (1L << RAX) | (1L << RCX) | (1L << RDX) | (1L << R08) | (1L << R09) | (1L << R10) | (1L << R11); + + // callee saved(win64) + public static final long WIN64_ABI_CALLEE_SAVED = ~WIN64_ABI_CALLER_SAVED; + + static int[] CALLINARG_SYSTEMV_INT = new int[] { + -1, // Control, no register + -1, // Memory, no register + RDI, + RSI, + RDX, + RCX, + R08, + R09, + }; + static RegMask[] CALLINARG_SYSTEMV_MASK = new RegMask[] { + null, // Control, no register + null, // Memory, no register + RDI_MASK, + RSI_MASK, + RDX_MASK, + RCX_MASK, + R08_MASK, + R09_MASK, + }; + // caller saved(systemv) + // caller saved(win64) + public static final long SYSTEMV_ABI_CALLER_SAVED = + (1L << RAX) | (1L << RDI) | (1L << RSI) | (1L << RCX) | (1L << RDX) | (1L << R08) | (1L << R09) << (1L << R10) << (1L << R11); + // callee saved(systemv) + public static final long SYSTEMV_ABI_CALLE_SAVED = ~SYSTEMV_ABI_CALLER_SAVED; + + + static RegMask callInMask( TypeFunPtr tfp, int idx ) { + RegMask[] cargs = switch( CodeGen.CODE._callingConv ) { + case CodeGen.CallingConv.SystemV -> CALLINARG_SYSTEMV_MASK; + case CodeGen.CallingConv.Win64 -> CALLINARG_WIN64_MASK; + }; + if( idx < 2 ) return null; + if( idx >= cargs.length ) + throw Utils.TODO(); // Pass on stack slot + if( tfp.arg(idx-2) instanceof TypeFloat ) + return XMMS[idx-2]; + return cargs[idx]; + } + + // Create a split op; any register to any register, including stack slots + @Override + public Node split() { + throw Utils.TODO(); + } + + // Return a MachNode unconditional branch + @Override public CFGNode jump() { + throw Utils.TODO(); + } + + // Break an infinite loop + @Override public IfNode never( CFGNode ctrl ) { + throw Utils.TODO(); + } + + // Instruction selection + @Override public Node instSelect( Node n ) { + return switch( n ) { + case AddFNode addf -> addf(addf); + case AddNode add -> add(add); + case AndNode and -> and(and); + case BoolNode bool -> cmp(bool); + case CallEndNode cend -> new CallEndX86(cend); + case CallNode call -> call(call); + case CProjNode c -> new CProjNode(c); + case ConstantNode con -> con(con); + case DivFNode divf -> new DivFX86(divf); + case DivNode div -> new DivX86(div); + case FunNode fun -> new FunX86(fun); + case IfNode iff -> jmp(iff); + case LoadNode ld -> ld(ld); + case MemMergeNode mem -> new MemMergeNode(mem); + case MulFNode mulf -> new MulFX86(mulf); + case MulNode mul -> mul(mul); + case NewNode nnn -> new NewX86(nnn); + case OrNode or -> or(or); + case ParmNode parm -> new ParmX86(parm); + case BulkMemPhiNode phi -> new BulkMemPhiNode(phi); + case MemPhiNode phi -> new MemPhiNode(phi); + case PhiNode phi -> new PhiNode(phi); + case ProjNode prj -> prj(prj); + case ReadOnlyNode read -> new ReadOnlyNode(read); + case ReturnNode ret -> new RetX86(ret,ret.fun()); + case SarNode sar -> sar(sar); + case ShlNode shl -> shl(shl); + case ShrNode shr -> shr(shr); + case StartNode start -> new StartNode(start); + case StopNode stop -> new StopNode(stop); + case StoreNode st -> st(st); + case SubFNode subf -> new SubFX86(subf); + case SubNode sub -> sub(sub); + case ToFloatNode tfn -> i2f8(tfn); + case ToIntegerNode cvt -> new F8ToIX86(cvt); + case XorNode xor -> xor(xor); + + case LoopNode loop -> new LoopNode(loop); + case RegionNode region-> new RegionNode(region); + default -> throw Utils.TODO(); + }; + } + + + // Attempt a full LEA-style break down. + private Node add( AddNode add ) { + Node lhs = add.in(1); + Node rhs = add.in(2); + if( lhs instanceof LoadNode ld && ld.nOuts()==1 ) + return new AddMemX86(add,address(ld),ld.ptr(),idx,off,scale, imm(rhs),val); + + if( rhs instanceof LoadNode ld && ld.nOuts()==1 ) + return new AddMemX86(add,address(ld),ld.ptr(),idx,off,scale, imm(lhs),val); + + // Attempt a full LEA-style break down. + // Returns one of AddX86, AddIX86, LeaX86, or LHS + if( rhs instanceof ConstantNode off && off._con instanceof TypeInteger toff ) { + if( lhs instanceof AddNode ladd ) + // ((base + (idx << scale)) + off) + return _lea(add,ladd.in(1),ladd.in(2),toff.value()); + if( lhs instanceof ShlNode shift ) + // (idx << scale) + off; no base + return _lea(add,null,shift,toff.value()); + + // lhs + rhs1 + if( toff.value()==0 ) return add; + return new AddIX86(add, toff); + } + return _lea(add,lhs,rhs,0); + } + + + private Node addf( AddFNode addf ) { + if( addf.in(1) instanceof LoadNode ld && ld.nOuts()==1 ) + return new AddFMemX86(addf,address(ld),ld.ptr(),idx,off,scale, addf.in(2)); + + if( addf.in(2) instanceof LoadNode ld && ld.nOuts()==1 ) + throw Utils.TODO(); // Swap load sides + + return new AddFX86(addf); + } + + + private Node _lea( Node add, Node base, Node idx, long off ) { + int scale = 1; + if( base instanceof ShlNode && !(idx instanceof ShlNode) ) throw Utils.TODO(); // Bug in canonicalization, should on RHS + if( idx instanceof ShlNode shift && shift.in(2) instanceof ConstantNode shfcon && + shfcon._con instanceof TypeInteger tscale && 0 <= tscale.value() && tscale.value() <= 3 ) { + idx = shift.in(1); + scale = 1 << ((int)tscale.value()); + } + // (base + idx) + off + return off==0 && scale==1 + ? new AddX86(add) + : new LeaX86(add,base,idx,scale,off); + } + + + private Node and(AndNode and) { + if( and.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti ) + return new AndIX86(and, ti); + return new AndX86(and); + } + + private Node call(CallNode call) { + if( call.fptr() instanceof ConstantNode con && con._con instanceof TypeFunPtr tfp ) + return new CallX86(call, tfp); + return new CallRX86( call ); + } + + // Because X86 flags, a normal ideal Bool is 2 X86 ops: a "cmp" and at "setz". + // Ideal If reading from a setz will skip it and use the "cmp" instead. + private Node cmp( BoolNode bool ) { + Node cmp = _cmp(bool); + return new SetX86(cmp,bool.op()); + } + private Node _cmp( BoolNode bool ) { + Node lhs = bool.in(1); + Node rhs = bool.in(2); + if( lhs instanceof LoadNode ld && ld.nOuts()==1 ) + return new CmpMemX86(bool,address(ld),ld.ptr(),idx,off,scale, imm(rhs),val,false); + + if( rhs instanceof LoadNode ld && ld.nOuts()==1 ) + return new CmpMemX86(bool,address(ld),ld.ptr(),idx,off,scale, imm(lhs),val,true); + + return rhs instanceof ConstantNode con && con._con instanceof TypeInteger ti + ? new CmpIX86(bool, ti) + : new CmpX86(bool); + } + + private Node con( ConstantNode con ) { + return switch( con._con ) { + case TypeInteger ti -> new IntX86(con); + case TypeFloat tf -> new FltX86(con); + case TypeFunPtr tfp -> new TFPX86(con); + case TypeMemPtr tmp -> new ConstantNode(con); + case TypeNil tn -> throw Utils.TODO(); + // TOP, BOTTOM, XCtrl, Ctrl, etc. Never any executable code. + case Type t -> new ConstantNode(con); + }; + } + + private Node i2f8(ToFloatNode tfn) { + assert tfn.in(1)._type instanceof TypeInteger ti; + return new I2f8X86(tfn); + } + + private Node jmp( IfNode iff ) { + // If/Bool combos will match to a Cmp/Set which sets flags. + // Most general arith ops will also set flags, which the Jmp needs directly. + // Loads do not set the flags, and will need an explicit TEST + if( !(iff.in(1) instanceof BoolNode) ) + iff.setDef(1,new BoolNode.EQ(iff.in(1),new ConstantNode(TypeInteger.ZERO))); + return new JmpX86(iff, ((BoolNode)iff.in(1)).op()); + } + + private Node ld( LoadNode ld ) { + return new LoadX86(address(ld),ld.ptr(),idx,off,scale); + } + + private Node mul(MulNode mul) { + return mul.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti + ? new MulIX86(mul, ti) + : new MulX86(mul); + } + + private Node or(OrNode or) { + if( or.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti) + return new OrIX86(or, ti); + return new OrX86(or); + } + + private Node prj( ProjNode prj ) { + return new ProjX86(prj); + } + + private Node sar( SarNode sar ) { + if( sar.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti) + return new SarIX86(sar, ti); + return new SarX86(sar); + } + + private Node shl( ShlNode shl ) { + if( shl.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti ) + return new ShlIX86(shl, ti); + return new ShlX86(shl); + } + + private Node shr(ShrNode shr) { + if( shr.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti ) + return new ShrIX86(shr, ti); + return new ShrX86(shr); + } + + private Node st( StoreNode st ) { + // Look for "*ptr op= val" + Node op = st.val(); + if( op instanceof AddNode ) { + if( op.in(1) instanceof LoadNode ld && + ld.in(0)==st.in(0) && + ld.mem()==st.mem() && + ld.ptr()==st.ptr() && + ld.off()==st.off() ) { + if( op instanceof AddNode ) { + return new MemAddX86(address(st),st.ptr(),idx,off,scale,imm(op.in(2)),val); + } + throw Utils.TODO(); + } + } + + return new StoreX86(address(st),st.ptr(),idx,off,scale,imm(st.val()),val); + } + + private Node sub( SubNode sub ) { + return sub.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti + ? new AddIX86(sub, TypeInteger.constant(-ti.value())) + : new SubX86(sub); + } + + private Node xor(XorNode xor) { + if(xor.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti) + return new XorIX86(xor, ti); + throw Utils.TODO(); + } + + + // Gather X86 addressing mode bits prior to constructing. This is a + // builder pattern, but saving the bits in a *local* *global* here to keep + // mess contained. + private static int off, scale, imm; + private static Node idx, val; + private N address( N mop ) { + off = scale = imm = 0; // Reset + idx = val = null; + Node base = mop.ptr(); + // Skip/throw-away a ReadOnly, only used to typecheck + if( base instanceof ReadOnlyNode read ) base = read.in(1); + assert !(base instanceof AddNode) && base._type instanceof TypeMemPtr; // Base ptr always, not some derived + if( mop.off() instanceof AddNode add && add.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti ) { + off = (int)ti.value(); + assert off == ti.value(); // In 32-bit range + idx = add.in(1); + if( idx instanceof ShlNode shift && shift.in(2) instanceof ConstantNode shfcon && + shfcon._con instanceof TypeInteger tscale && 0 <= tscale.value() && tscale.value() <= 3 ) { + idx = shift.in(1); + scale = (int)tscale.value(); + } + } else { + if( mop.off() instanceof ConstantNode con && con._con instanceof TypeInteger ti ) { + off = (int)ti.value(); + assert off == ti.value(); // In 32-bit range + } else { + idx = mop.off(); + } + } + return mop; + } + private int imm( Node xval ) { + assert val==null && imm==0; + if( xval instanceof ConstantNode con && con._con instanceof TypeInteger ti ) { + val = null; + imm = (int)ti.value(); + assert imm == ti.value(); // In 32-bit range + } else { + val = xval; + } + return imm; + } + +} diff --git a/src/main/java/com/seaofnodes/simple/print/SimpleGraphAdapter.java b/src/main/java/com/seaofnodes/simple/print/SimpleGraphAdapter.java index 3e23968a..1992a0c0 100644 --- a/src/main/java/com/seaofnodes/simple/print/SimpleGraphAdapter.java +++ b/src/main/java/com/seaofnodes/simple/print/SimpleGraphAdapter.java @@ -6,7 +6,7 @@ import com.seaofnodes.simple.node.*; import com.seaofnodes.simple.node.Node; import com.seaofnodes.simple.type.TypeMem; -/** Chapter 18's view of the IR; browser, transport and layout are shared. */ +/** Chapter 19's view of the IR; browser, transport and layout are shared. */ public class SimpleGraphAdapter extends GraphAdapter { @Override protected String edgeName(Node n, int i) { diff --git a/src/main/java/com/seaofnodes/simple/print/SimpleGraphObserver.java b/src/main/java/com/seaofnodes/simple/print/SimpleGraphObserver.java index b2dd3107..4ec55f4d 100644 --- a/src/main/java/com/seaofnodes/simple/print/SimpleGraphObserver.java +++ b/src/main/java/com/seaofnodes/simple/print/SimpleGraphObserver.java @@ -7,7 +7,7 @@ import com.seaofnodes.simple.node.Node; import java.util.ArrayList; import java.io.IOException; -/** Chapter 18's compilation context; capture and event bookkeeping are shared. */ +/** Chapter 19's compilation context; capture and event bookkeeping are shared. */ public class SimpleGraphObserver extends GraphCapture { private CodeGen _code; public SimpleGraphObserver() { super(new SimpleGraphAdapter()); } @@ -20,16 +20,15 @@ public class SimpleGraphObserver extends GraphCapture { _code.parse().opto().typeCheck(); } @Override protected String phase() { return _code._phase.name(); } - @Override protected int pos() { return _code.P != null ? _code.P.pos() : -1; } + @Override protected int pos() { return _code._phase == CodeGen.Phase.Parse ? _code.P.pos() : -1; } @Override protected void detach() { if( _code != null ) _code._obs = null; _code = null; } - @Override protected Node scope() { return _code.P == null ? null : _code.P._scope; } + @Override protected Node scope() { return _code.P._scope; } @Override protected void roots(ArrayList roots) { roots.add(_code._stop); - if( _code.P != null ) { - roots.add(com.seaofnodes.simple.Parser.START); + if( _code._phase == CodeGen.Phase.Parse ) { roots.addAll(_code.P._xScopes); } } diff --git a/src/main/java/com/seaofnodes/simple/type/Type.java b/src/main/java/com/seaofnodes/simple/type/Type.java index 34f54be8..1ce5dbd0 100644 --- a/src/main/java/com/seaofnodes/simple/type/Type.java +++ b/src/main/java/com/seaofnodes/simple/type/Type.java @@ -236,8 +236,4 @@ public class Type { // This is used by error messages, and is a shorted print. public String str() { return STRS[_type]; } - - public static void reset() { - TypeFunPtr.reset(); - } } diff --git a/src/main/java/com/seaofnodes/simple/type/TypeFunPtr.java b/src/main/java/com/seaofnodes/simple/type/TypeFunPtr.java index 61fc71cf..94352790 100644 --- a/src/main/java/com/seaofnodes/simple/type/TypeFunPtr.java +++ b/src/main/java/com/seaofnodes/simple/type/TypeFunPtr.java @@ -25,7 +25,6 @@ public class TypeFunPtr extends TypeNil { // within the TypeTuple. Can be upgraded to a BitSet for larger classes of // functions. Negative means "these 63 concrete bits plus infinite unknown more" public final long _fidxs; // 63 unique functions per signature - public String _name; // Optional debug function name; only for named single function private TypeFunPtr(byte nil, TypeTuple sig, Type ret, long fidxs) { super(TFUNPTR,nil); @@ -35,30 +34,16 @@ public class TypeFunPtr extends TypeNil { _fidxs = fidxs; } - static TypeFunPtr make( byte nil, TypeTuple sig, Type ret, long fidxs ) { return new TypeFunPtr(nil,sig,ret,fidxs).intern(); } + public static TypeFunPtr make( byte nil, TypeTuple sig, Type ret, long fidxs ) { return new TypeFunPtr(nil,sig,ret,fidxs).intern(); } public static TypeFunPtr make( boolean nil, TypeTuple sig, Type ret ) { return make((byte)(nil ? 3 : 2),sig,ret,-1); } - @Override TypeFunPtr makeFrom( byte nil ) { return nil ==_nil ? this : make( nil,_sig,_ret, _fidxs).setName(_name); } - public TypeFunPtr makeFrom( Type ret ) { return ret ==_ret ? this : make( _nil,_sig, ret, _fidxs).setName(_name); } - public TypeFunPtr makeFrom( int fidx ) { return make((byte)2, _sig,_ret,1L< FIDXS = new HashMap<>(); - private static int nextFIDX(TypeTuple sig) { - Integer i = FIDXS.get(sig); - int fidx = i==null ? 0 : i; - FIDXS.put(sig,fidx+1); // Track count per sig - assert fidx<64; // TODO: need a larger FIDX space - return fidx; // Return - } - public static TypeFunPtr makeFun( TypeTuple sig, Type ret ) { - return make((byte)2,sig,ret,1L< ts) { ts.add(TEST); ts.add(TEST0); ts.add(BOT); ts.add(MAIN); } + public static void gather(ArrayList ts) { ts.add(TEST); ts.add(TEST0); ts.add(BOT); } @Override Type xmeet(Type t) { @@ -93,15 +78,6 @@ public class TypeFunPtr extends TypeNil { return _sig == ptr._sig && _ret == ptr._ret && _fidxs == ptr._fidxs && super.eq(ptr); } - public TypeFunPtr setName(String name) { - if( name==null ) return this; - assert _fidxs > 0 && Long.bitCount(_fidxs) == 1; - // Name can be set to different things, just for debug - //assert _name==null || _name.equals(name); - _name = name; - return this; - } - @Override public String str() { return print(new SB()).toString(); } @Override public SB print(SB sb) { return _print(sb,false,true); } @@ -112,32 +88,20 @@ public class TypeFunPtr extends TypeNil { if( this==BOT ) return sb.p("FunBot"); if( _nil==0 && _sig==TypeTuple.TOP && _ret==Type.TOP && _fidxs==0 ) return sb.p("FunTop"); sb.p(x()).p("{ "); - if( n && _name!=null ) sb.p(_name); - else { - if( _sig._types!=null ) - for( Type t : _sig._types ) - _print(sb,g,t).p(" "); - _print(sb.p(g ? "→ " : "-> "),g,_ret).p(" #"); - if( isHigh() ) sb.p("~"); - long fidxs = isHigh() ? ~_fidxs : _fidxs; - String fidx = fidxs==0 ? "" - : Long.bitCount(fidxs) == 1 ? ""+Long.numberOfTrailingZeros(fidxs) - : fidxs == -1 ? "ALL" - : "b"+Long.toBinaryString(fidxs); // Just some function bits - sb.p(fidx); - } - return sb.p("}").p(q()); + if( _sig._types!=null ) + for( Type t : _sig._types ) + _print(sb,g,t).p(" "); + _print(sb.p(g ? "→ " : "-> "),g,_ret).p(" #"); + if( isHigh() ) sb.p("~"); + long fidxs = isHigh() ? ~_fidxs : _fidxs; + String fidx = fidxs==0 ? "" + : Long.bitCount(fidxs) == 1 ? ""+Long.numberOfTrailingZeros(fidxs) + : fidxs == -1 ? "ALL" + : "b"+Long.toBinaryString(fidxs); // Just some function bits + return sb.p(fidx).p("}").p(q()); } // Usage: for( long fidxs=fidxs(); fidxs!=0; fidxs=nextFIDX(fidxs) { int fidxs = Long.numberOfTrailingZeros(fidxs); ... } public static long nextFIDX(long fidxs) { return fidxs & (fidxs-1); } - public static void reset() { - FIDXS.clear(); - FIDXS.put(MAIN._sig,1); - for( Type t : INTERN.keySet() ) - if( t instanceof TypeFunPtr tfp && tfp!=MAIN) - tfp._name = null; - } - } diff --git a/src/main/java/com/seaofnodes/simple/type/TypeStruct.java b/src/main/java/com/seaofnodes/simple/type/TypeStruct.java index fff6a7db..89c79a1f 100644 --- a/src/main/java/com/seaofnodes/simple/type/TypeStruct.java +++ b/src/main/java/com/seaofnodes/simple/type/TypeStruct.java @@ -50,6 +50,7 @@ public class TypeStruct extends Type { // Array public static TypeStruct makeAry(TypeInteger len, int lenAlias, Type body, int bodyAlias) { assert body instanceof TypeInteger || body instanceof TypeFloat || (body instanceof TypeNil tn && tn.nullable()); + assert len.isa(TypeInteger.U32); return make("[" + body.str() + "]", Field.make("#" ,len , lenAlias,true ), Field.make("[]",body,bodyAlias,false)); @@ -61,7 +62,7 @@ public class TypeStruct extends Type { public static final TypeStruct S1 = make("S1", Field.make("a", TypeInteger.BOT, -1, false), Field.make("s2",TypeMemPtr.make((byte)2,S2F),-2, false) ); private static final TypeStruct S2 = make("S2", Field.make("b", TypeFloat .F64, -3, false), Field.make("s1",TypeMemPtr.make((byte)2,S1F),-4, false) ); - private static final TypeStruct ARY = makeAry(TypeInteger.BOT,-1,TypeInteger.BOT,-2); + private static final TypeStruct ARY = makeAry(TypeInteger.U32,-1,TypeInteger.BOT,-2); public static void gather(ArrayList ts) { ts.add(TEST); ts.add(BOT); ts.add(S1); ts.add(S2); ts.add(ARY); } @@ -225,6 +226,11 @@ public class TypeStruct extends Type { private int[] offsets() { // Field byte offsets // Compute a layout for a collection of fields assert _fields != null; // No forward refs + + // Array layout is different: len,[pad],body... + if( isAry() ) + return _offs = new int[]{ 0, _fields[1]._type.log_size() < 3 ? 4 : 8 }; + // Compute a layout int[] cnts = new int[4]; // Count of fields at log field size for( Field f : _fields ) diff --git a/src/test/java/com/seaofnodes/simple/BrainFuckTest.java b/src/test/java/com/seaofnodes/simple/BrainFuckTest.java index 37ecde1c..8f09cb3d 100644 --- a/src/test/java/com/seaofnodes/simple/BrainFuckTest.java +++ b/src/test/java/com/seaofnodes/simple/BrainFuckTest.java @@ -63,7 +63,7 @@ for( int pc = 0; pc < program#; pc++ ) { } return output; """); - code.parse().opto().typeCheck().GCM(); + code.parse().opto().typeCheck()/*.instSelect("x86_64_v2", "SystemV")*/.GCM().localSched(); assertEquals("Hello World!\n", Eval2.eval(code, 0, 10000)); } } diff --git a/src/test/java/com/seaofnodes/simple/Chapter03Test.java b/src/test/java/com/seaofnodes/simple/Chapter03Test.java index 409e23ed..8212d246 100644 --- a/src/test/java/com/seaofnodes/simple/Chapter03Test.java +++ b/src/test/java/com/seaofnodes/simple/Chapter03Test.java @@ -46,7 +46,7 @@ public class Chapter03Test { @Test public void testRedeclareVar() { try { - new Parser("int a=1; int b=2; int c=0; int b=3; c=a+b;").parse(); + new CodeGen("int a=1; int b=2; int c=0; int b=3; c=a+b;").parse(); fail(); } catch( RuntimeException e ) { assertEquals("Redefining name 'b'",e.getMessage()); diff --git a/src/test/java/com/seaofnodes/simple/Chapter04Test.java b/src/test/java/com/seaofnodes/simple/Chapter04Test.java index d2260e1f..934e794f 100644 --- a/src/test/java/com/seaofnodes/simple/Chapter04Test.java +++ b/src/test/java/com/seaofnodes/simple/Chapter04Test.java @@ -34,14 +34,14 @@ public class Chapter04Test { public void testAddAddMul() { CodeGen code = new CodeGen("return arg+0+arg;"); code.parse(); - assertEquals("return (arg*2);", code.print()); + assertEquals("return (arg<<1);", code.print()); } @Test public void testPeephole3() { CodeGen code = new CodeGen("return 1+arg+2+arg+3; "); code.parse(); - assertEquals("return ((arg*2)+6);", code.print()); + assertEquals("return ((arg<<1)+6);", code.print()); } @Test @@ -90,8 +90,7 @@ public class Chapter04Test { // Adding functions means `main` does not constant fold the incoming arg value @Ignore @Test public void testConstantArg() { - CodeGen code = new CodeGen("return arg; ", TypeInteger.constant(2)); - code.parse(); + CodeGen code = new CodeGen("return arg; ", TypeInteger.constant(2), 123L).parse(); assertEquals("return 2;", code.expr().in(1).toString()); } @@ -139,12 +138,8 @@ public class Chapter04Test { @Test public void testBug3() { - try { - new Parser("inta=1; return a;").parse(); - fail(); - } catch( RuntimeException e ) { - assertEquals("Undefined name 'inta'",e.getMessage()); - } + try { new CodeGen("inta=1; return a;").parse(); fail(); } + catch( RuntimeException e ) { assertEquals("Undefined name 'inta'",e.getMessage()); } } @Test @@ -156,8 +151,8 @@ public class Chapter04Test { @Test public void testBug5() { - Parser parser = new Parser("return arg--2;"); - StopNode ret = parser.parse(); - assertEquals("return (arg--2);", ret.print()); + CodeGen code = new CodeGen("return arg--2;"); + code.parse(); + assertEquals("return (arg--2);", code.print()); } } diff --git a/src/test/java/com/seaofnodes/simple/Chapter05Test.java b/src/test/java/com/seaofnodes/simple/Chapter05Test.java index ff60c695..7a9414a2 100644 --- a/src/test/java/com/seaofnodes/simple/Chapter05Test.java +++ b/src/test/java/com/seaofnodes/simple/Chapter05Test.java @@ -64,7 +64,7 @@ else b=a+1; return a+b;"""); code.parse().opto(); - assertEquals("return ((arg*2)+Phi(Region,2,3));", code.print()); + assertEquals("return ((arg<<1)+Phi(Region,2,3));", code.print()); } @Test @@ -79,7 +79,7 @@ else a=b+1; return a+b;"""); code.parse().opto(); - assertEquals("return ((Phi(Region,(arg*2),arg)+arg)+Phi(Region,4,5));", code.print()); + assertEquals("return ((Phi(Region,(arg<<1),arg)+arg)+Phi(Region,4,5));", code.print()); } @Test @@ -150,79 +150,51 @@ return a; @Test public void testTrue() { - StopNode stop = new Parser("return true;").parse(); - assertEquals("return 1;",stop.ret().print()); + CodeGen code = new CodeGen("return true;").parse(); + assertEquals("return 1;",code.print()); } @Test public void testHalfDef() { - try { - new CodeGen("if( arg==1 ) int b=2; return b;").parse(); - fail(); - } catch( RuntimeException e ) { - assertEquals("Cannot define a 'b' on one arm of an if",e.getMessage()); - } + try { new CodeGen("if( arg==1 ) int b=2; return b;").parse(); fail(); } + catch( RuntimeException e ) { assertEquals("Cannot define a 'b' on one arm of an if",e.getMessage()); } } @Test public void testHalfDef2() { - try { - new Parser("if( arg==1 ) { int b=2; } else { int b=3; } return b;").parse(); - fail(); - } catch( RuntimeException e ) { - assertEquals("Undefined name 'b'",e.getMessage()); - } + try { new CodeGen("if( arg==1 ) { int b=2; } else { int b=3; } return b;").parse(); fail(); } + catch( RuntimeException e ) { assertEquals("Undefined name 'b'",e.getMessage()); } } @Test public void testRegress1() { - try { - new CodeGen("if(arg==2) int a=1; else int b=2; return a;").parse(); - fail(); - } catch( RuntimeException e ) { - assertEquals("Cannot define a 'a' on one arm of an if",e.getMessage()); - } + try { new CodeGen("if(arg==2) int a=1; else int b=2; return a;").parse(); fail(); } + catch( RuntimeException e ) { assertEquals("Cannot define a 'a' on one arm of an if",e.getMessage()); } } @Test public void testBadNum() { - try { - new Parser("return 1-;").parse(); - fail(); - } catch( RuntimeException e ) { - assertEquals("Syntax error, expected an identifier or expression: ;",e.getMessage()); - } + try { new CodeGen("return 1-;").parse(); fail(); } + catch( RuntimeException e ) { assertEquals("Syntax error, expected an identifier or expression: ;",e.getMessage()); } } @Test public void testKeyword1() { - try { - new Parser("int true=0; return true;").parse(); - fail(); - } catch( RuntimeException e ) { - assertEquals("Expected an identifier, found 'true'",e.getMessage()); - } + try { new CodeGen("int true=0; return true;").parse(); fail(); } + catch( RuntimeException e ) { assertEquals("Expected an identifier, found 'true'",e.getMessage()); } } @Test public void testKeyword2() { - try { - new Parser("int else=arg; if(else) else=2; else else=1; return else;").parse(); - fail(); - } catch( RuntimeException e ) { - assertEquals("Expected an identifier, found 'else'",e.getMessage()); - } + try { new CodeGen("int else=arg; if(else) else=2; else else=1; return else;").parse(); fail(); } + catch( RuntimeException e ) { assertEquals("Expected an identifier, found 'else'",e.getMessage()); } } @Test public void testKeyword3() { - try { - new CodeGen("int a=1; ififif(arg);a=2;return a;").parse(); - fail(); - } catch( RuntimeException e ) { - assertEquals("Undefined name 'ififif'",e.getMessage()); - } + try { new CodeGen("int a=1; ififif(arg);a=2;return a;").parse(); fail(); } + catch( RuntimeException e ) { assertEquals("Undefined name 'ififif'",e.getMessage()); } } } diff --git a/src/test/java/com/seaofnodes/simple/Chapter06Test.java b/src/test/java/com/seaofnodes/simple/Chapter06Test.java index 2107cdb0..117531f0 100644 --- a/src/test/java/com/seaofnodes/simple/Chapter06Test.java +++ b/src/test/java/com/seaofnodes/simple/Chapter06Test.java @@ -103,8 +103,7 @@ else if( arg==3 ) else a=5; return a; -""", TypeInteger.constant(2)); - code.parse().opto(); +""", TypeInteger.constant(2), 123L).parse().opto(); assertEquals("return 5;", code.print()); } @@ -124,8 +123,7 @@ else if( arg==3 ) else a=5; return a; -""", TypeInteger.constant(1)); - code.parse().opto(); +""", TypeInteger.constant(1), 123L).parse().opto(); assertEquals("return 3;", code.print()); } @@ -165,7 +163,7 @@ else if( arg==3 ) else a=5; return a; -""", TypeInteger.constant(1)); +""", TypeInteger.constant(1), 123L); code.parse().opto(); assertEquals("return 3;", code.print()); } @@ -186,7 +184,7 @@ else if( arg==3 ) else a=5; return a; -""", TypeInteger.constant(3)); +""", TypeInteger.constant(3), 123L); code.parse().opto(); assertEquals("return 4;", code.print()); } @@ -210,7 +208,8 @@ return a+b; @Test public void testDemo1True() { - CodeGen code = new CodeGen(""" + CodeGen code = new CodeGen( +""" arg=1; int a = 0; int b = 1; @@ -220,14 +219,15 @@ if( arg ) { else b = 3; } return a+b; -""", TypeInteger.constant(1)); +""", TypeInteger.constant(1), 123L); code.parse().opto(); assertEquals("return 4;", code.print()); } @Test public void testDemo1False() { - CodeGen code = new CodeGen(""" + CodeGen code = new CodeGen( +""" arg=0; int a = 0; int b = 1; @@ -237,7 +237,7 @@ if( arg ) { else b = 3; } return a+b; -""", TypeInteger.constant(0)); +""", TypeInteger.constant(0), 123L); code.parse().opto(); assertEquals("return 1;", code.print()); } @@ -263,7 +263,8 @@ return a+b+c; @Test public void testDemo2True() { - CodeGen code = new CodeGen(""" + CodeGen code = new CodeGen( +""" arg=1; int a = 0; int b = 1; @@ -275,14 +276,15 @@ if( arg ) { else b = 3; } return a+b+c; -""", TypeInteger.constant(1)); +""", TypeInteger.constant(1), 123L); code.parse().opto(); assertEquals("return 6;", code.print()); } @Test public void testDemo2arg2() { - CodeGen code = new CodeGen(""" + CodeGen code = new CodeGen( +""" arg=2; int a = 0; int b = 1; @@ -294,7 +296,7 @@ if( arg ) { else b = 3; } return a+b+c; -""", TypeInteger.constant(2)); +""", TypeInteger.constant(2), 123L); code.parse().opto(); assertEquals("return 5;", code.print()); } diff --git a/src/test/java/com/seaofnodes/simple/Chapter08Test.java b/src/test/java/com/seaofnodes/simple/Chapter08Test.java index 03a2c50a..350df18b 100644 --- a/src/test/java/com/seaofnodes/simple/Chapter08Test.java +++ b/src/test/java/com/seaofnodes/simple/Chapter08Test.java @@ -1,8 +1,6 @@ package com.seaofnodes.simple; -import com.seaofnodes.simple.evaluator.Evaluator; import com.seaofnodes.simple.node.*; -import com.seaofnodes.simple.type.TypeInteger; import org.junit.Assert; import org.junit.Test; @@ -147,7 +145,7 @@ return arg; @Test public void testBreakOutsideLoop() { try { - new Parser(""" + new CodeGen(""" if(arg <= 10) { break; arg = arg + 1; diff --git a/src/test/java/com/seaofnodes/simple/Chapter09Test.java b/src/test/java/com/seaofnodes/simple/Chapter09Test.java index b210f7e9..10f4a78b 100644 --- a/src/test/java/com/seaofnodes/simple/Chapter09Test.java +++ b/src/test/java/com/seaofnodes/simple/Chapter09Test.java @@ -41,7 +41,7 @@ else { return x; """); code.parse().opto(); - assertEquals("return Phi(Region,(arg*2),(Mul+1));", code.print()); + assertEquals("return Phi(Region,(arg<<1),(Shl+1));", code.print()); Assert.assertEquals( "2", Eval2.eval(code, 1)); Assert.assertEquals("23", Eval2.eval(code, 11)); } @@ -255,7 +255,7 @@ while(0==1) while(v0) v0=1+v0; """); code.parse().opto(); - assertEquals("return ~int;", code.print()); + assertEquals("return 0;", code.print()); } @Test diff --git a/src/test/java/com/seaofnodes/simple/Chapter11Test.java b/src/test/java/com/seaofnodes/simple/Chapter11Test.java index 9fd392d2..14819869 100644 --- a/src/test/java/com/seaofnodes/simple/Chapter11Test.java +++ b/src/test/java/com/seaofnodes/simple/Chapter11Test.java @@ -48,17 +48,11 @@ public class Chapter11Test { assertEquals("S{x=1,y=2,z=3}",Eval2.eval(code,4)); } - @Test public void testLateSliceWorklist() throws ReflectiveOperationException { + @Test public void testLateSliceWorklist() { // Seed 97 exposed a store selecting a bulk predecessor without queuing // it. Keep both the worklist assertion and the final heap check. for (int seed : new int[]{0,97,123,456}) { - var code = new CodeGen(Chapter10Test.NESTED_MEMORY).parse(); - var field = IterPeeps.class.getDeclaredField("WORK"); - field.setAccessible(true); - Object work = field.get(null); - field = work.getClass().getDeclaredField("_R"); - field.setAccessible(true); - ((java.util.Random)field.get(work)).setSeed(seed); + var code = new CodeGen(Chapter10Test.NESTED_MEMORY,com.seaofnodes.simple.type.TypeInteger.BOT,seed).parse(); code.opto(); assertEquals("S{x=9,y=25,z=17}",Eval2.eval(code,4)); } diff --git a/src/test/java/com/seaofnodes/simple/Chapter12Test.java b/src/test/java/com/seaofnodes/simple/Chapter12Test.java index a15132d4..90aaf631 100644 --- a/src/test/java/com/seaofnodes/simple/Chapter12Test.java +++ b/src/test/java/com/seaofnodes/simple/Chapter12Test.java @@ -183,7 +183,7 @@ return new S1.s=new S2; struct I {int i;}; struct P { !I? !pi; }; !P !p1 = new P; -!P p2 = new P; +!P !p2 = new P; p2.pi = new I; p2.pi.i = 2; if (arg) p1 = new P; diff --git a/src/test/java/com/seaofnodes/simple/Chapter15Test.java b/src/test/java/com/seaofnodes/simple/Chapter15Test.java index 97f695bf..c38dfb00 100644 --- a/src/test/java/com/seaofnodes/simple/Chapter15Test.java +++ b/src/test/java/com/seaofnodes/simple/Chapter15Test.java @@ -142,7 +142,7 @@ struct S { int x; flt y; }; !S !s = new S; s.x=99; s.y = 3.14; // Double-d array of Ss. Fill in one row. -!S?[]?[] iss = new !S?[]?[2]; +!S?[]?[] !iss = new !S?[]?[2]; iss[0] = new !S?[7]; iss[0][2] = s; @@ -151,10 +151,12 @@ flt rez = 1.2; if( iss[arg] ) if( iss[arg][2] ) rez = iss[arg][2].y; -return rez; -"""); - try { code.parse().opto().typeCheck(); fail(); } - catch( Exception e ) { assertEquals("Might be null accessing '[]'",e.getMessage()); } +return rez;"""); + code.parse().opto().typeCheck(); + assertEquals("return Phi(Region,Phi(Region,.y,1.2),1.2);", code.print()); + assertEquals("3.14", Eval2.eval(code, 0)); + try { Eval2.eval(code, 10); fail(); } // Fails AIOOBE in Eval2, no range checks + catch( Exception e ) { assertEquals("Index 10 out of bounds for length 2",e.getMessage()); } } @Test diff --git a/src/test/java/com/seaofnodes/simple/Chapter17bTest.java b/src/test/java/com/seaofnodes/simple/Chapter17bTest.java index e83b43af..19cc25d9 100644 --- a/src/test/java/com/seaofnodes/simple/Chapter17bTest.java +++ b/src/test/java/com/seaofnodes/simple/Chapter17bTest.java @@ -39,7 +39,7 @@ return arg++; return arg+++arg++; """); code.parse().opto(); - assertEquals("return ((arg*2)+1);", code.print()); + assertEquals("return ((arg<<1)+1);", code.print()); assertEquals("1", Eval2.eval(code, 0)); } @@ -50,7 +50,7 @@ return arg+++arg++; return -arg---arg--; """); code.parse().opto(); - assertEquals("return (-((arg*2)+-1));", code.print()); + assertEquals("return (-((arg<<1)+-1));", code.print()); assertEquals("1", Eval2.eval(code, 0)); } @@ -344,7 +344,7 @@ var b = arg ? new Bar : null; return b ? b.x++ + b.x++ : -1; """); code.parse().opto(); - assertEquals("return Phi(Region,((.x*2)+1),-1);", code.print()); + assertEquals("return Phi(Region,((.x<<1)+1),-1);", code.print()); assertEquals("-1", Eval2.eval(code, 0)); assertEquals("1", Eval2.eval(code, 1)); } @@ -357,7 +357,7 @@ var b = arg ? new Bar; return b ? b.x++ + b.x++ : -1; """); code.parse().opto(); - assertEquals("return Phi(Region,((.x*2)+1),-1);", code.print()); + assertEquals("return Phi(Region,((.x<<1)+1),-1);", code.print()); assertEquals("-1", Eval2.eval(code, 0)); assertEquals("1", Eval2.eval(code, 1)); } diff --git a/src/test/java/com/seaofnodes/simple/Chapter18Test.java b/src/test/java/com/seaofnodes/simple/Chapter18Test.java index 7f2b92ac..f965edd4 100644 --- a/src/test/java/com/seaofnodes/simple/Chapter18Test.java +++ b/src/test/java/com/seaofnodes/simple/Chapter18Test.java @@ -9,30 +9,6 @@ import static org.junit.Assert.fail; import org.junit.Ignore; public class Chapter18Test { - @Test public void testFloatArrays() { - for( String type : new String[]{"flt","f64","f32"} ) { - CodeGen zero = new CodeGen("var a = new "+type+"[arg]; return a[arg-1];") - .parse().opto().typeCheck(); - assertEquals("0.0",Eval2.eval(zero,3)); - new CodeGen(type+" x = arg; var a = new "+type+"[arg]; a[0] = x; return a[0];") - .parse().opto().typeCheck(); - CodeGen loop = new CodeGen("var a = new "+type+"[arg]; "+ - "for(int i=0; i int #1}", Eval2.eval(code, 3)); } @Test @@ -216,7 +192,7 @@ var sq = { int x -> }; return sq(arg)+sq(3); """); - code.parse().opto(); + code.parse().opto().typeCheck().GCM().localSched(); assertEquals("Stop[ return (sq( 3)+sq( arg)); return (Parm_x(sq,int,3,arg)*x); ]", code._stop.toString()); assertEquals("13", Eval2.eval(code, 2)); } @@ -255,7 +231,7 @@ var fcn = arg ? { int x -> x*x; } : { int x -> x+x; }; return fcn(3); """); code.parse().opto(); - assertEquals("Stop[ return Phi(Region,{ int -> int #1},{ int -> int #2})( 3); return (Parm_x($fun,int,3)*x); return (Parm_x($fun,int,3)*2); ]", code._stop.toString()); + assertEquals("Stop[ return Phi(Region,{ int -> int #1},{ int -> int #2})( 3); return (Parm_x($fun,int,3)*x); return (Parm_x($fun,int,3)<<1); ]", code._stop.toString()); assertEquals("6", Eval2.eval(code, 0)); assertEquals("9", Eval2.eval(code, 1)); } @@ -264,7 +240,7 @@ return fcn(3); @Test public void testFcn5() { CodeGen code = new CodeGen("val fact = { int x -> x <= 1 ? 1 : x*fact(x-1); }; return fact(arg);"); - code.parse().opto(); + code.parse().opto().typeCheck(); assertEquals("Stop[ return fact( arg); return Phi(Region,1,(Parm_x(fact,int,arg,(x-1))*fact( Sub))); ]", code._stop.toString()); assertEquals( "1", Eval2.eval(code, 0)); assertEquals( "1", Eval2.eval(code, 1)); @@ -319,7 +295,6 @@ for(;;) { assertEquals("3", Eval2.eval(code, 0)); } - @Test public void testFcn9() { CodeGen code = new CodeGen( @@ -336,6 +311,31 @@ for(;;) { assertEquals(null, Eval2.eval(code, 0)); } + + @Test + public void testFcn10() { + CodeGen code = new CodeGen( +""" +struct Person { + int age; +}; + +val fcn = { !Person?[] !ps, int x -> + val tmp = ps[x]; + if( ps[x] ) + ps[x].age++; +}; + +var ps = new !Person?[2]; +ps[0] = new Person; +ps[1] = new Person; +fcn(ps,1); +"""); + code.parse().opto().typeCheck().GCM().localSched(); + assertEquals("return 0;", code._stop.toString()); + assertEquals("0", Eval2.eval(code, 0)); + } + // Function break @Test public void testErr1() { diff --git a/src/test/java/com/seaofnodes/simple/Chapter19Test.java b/src/test/java/com/seaofnodes/simple/Chapter19Test.java new file mode 100644 index 00000000..1abda81f --- /dev/null +++ b/src/test/java/com/seaofnodes/simple/Chapter19Test.java @@ -0,0 +1,529 @@ +package com.seaofnodes.simple; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.type.*; + + +import org.junit.Test; +import static org.junit.Assert.*; +import static org.junit.Assert.fail; +import org.junit.Ignore; + +public class Chapter19Test { + @Test public void testPrintingFunctionLookup() { + var code = new CodeGen("return 0;").parse(); + var tfp = TypeFunPtr.make((byte)2,TypeTuple.make(TypeInteger.constant(987654)),TypeInteger.constant(123456),1L<<30); + var con = new ConstantNode(tfp); + var call = new CallNode(null,code._start,con,con); + // Diagnostic lookup may construct/intern the return-erased linker key. + con.toString(); + org.junit.Assert.assertNull(call.name()); + + var fun = new FunNode(null,tfp); + fun._name = "printerTarget"; + fun.addDef(code._start); + code.link(fun); + var otherReturn = tfp.makeFrom(TypeInteger.constant(654321)); + org.junit.Assert.assertSame(fun,code.link(otherReturn)); + org.junit.Assert.assertTrue(con.toString().contains("printerTarget")); + assertEquals("printerTarget",call.name()); + } + + + @Ignore + @Test + public void testJig() { + CodeGen code = new CodeGen(""" +return 0; +"""); + code.parse().opto().typeCheck(); + assertEquals("return 0;", code._stop.toString()); + assertEquals("0", Eval2.eval(code, 2)); + } + + + @Test + public void testString() { + CodeGen code = new CodeGen( +""" +struct String { + u8[] !cs; + int _hashCode; +}; + +val equals = { !String !self, !String s -> + if( self == s ) return true; + if( self.cs# != s.cs# ) return false; + for( int i=0; i< self.cs#; i++ ) + if( self.cs[i] != s.cs[i] ) + return false; + return true; +}; + +val hashCode = { !String self -> + self._hashCode + ? self._hashCode + : (self._hashCode = _hashCodeString(self)); +}; + +val _hashCodeString = { !String self -> + int hash=0; + if( self.cs ) { + for( int i=0; i< self.cs#; i++ ) + hash = hash*31 + self.cs[i]; + } + if( !hash ) hash = 123456789; + return hash; +}; + +!String !s = new String { cs = new u8[17]; }; +s.cs[0] = 67; // C +s.cs[1] = 108; // l +hashCode(s);"""); + code.parse().opto().typeCheck().GCM().localSched(); + assertEquals("Stop[ return Phi(Region,123456789,Phi(Loop,0,(.[]+((Phi_hash<<5)-Phi_hash)))); return Phi(Region,1,0,0,1); ]", code._stop.toString()); + assertEquals("-2449306563677080489", Eval2.eval(code, 2)); + } + + @Test + public void testBasic0() { + CodeGen code = new CodeGen("return 0;").parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched(); + assertEquals("return 0;", code._stop.toString()); + } + + @Test + public void testBasic1() { + CodeGen code = new CodeGen("return arg+1;").parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched(); + assertEquals("return (inc,arg);", code._stop.toString()); + } + + @Test + public void testBasic2() { + CodeGen code = new CodeGen("return -17;").parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched(); + assertEquals("return -17;", code._stop.toString()); + } + + + @Test + public void testBasic3() { + CodeGen code = new CodeGen("return arg==1;").parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched(); + assertEquals("return (set==,(cmp,arg));", code._stop.toString()); + } + + @Test + public void testBasic4() { + CodeGen code = new CodeGen("return arg<<1;").parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched(); + assertEquals("return (shli,arg);", code._stop.toString()); + } + + @Test + public void testBasic5() { + CodeGen code = new CodeGen("return arg >> 1;").parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched(); + assertEquals("return (sari,arg);", code._stop.toString()); + } + + @Test + public void testBasic6() { + CodeGen code = new CodeGen("return arg >>> 1;").parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched(); + assertEquals("return (shri,arg);", code._stop.toString()); + } + + @Test + public void testBasic7() { + CodeGen code = new CodeGen("return arg / 2;").parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched(); + assertEquals("return (div,arg,2);", code._stop.toString()); + } + + @Test + public void testBasic8() { + CodeGen code = new CodeGen("return arg * 6;").parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched(); + assertEquals("return (muli,arg);", code._stop.toString()); + } + + @Test + public void testBasic9() { + CodeGen code = new CodeGen("return arg & 2;").parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched(); + assertEquals("return (andi,arg);", code._stop.toString()); + } + + @Test + public void testBasic10() { + CodeGen code = new CodeGen("return arg | 2;").parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched(); + assertEquals("return (ori,arg);", code._stop.toString()); + } + + @Test + public void testBasic11() { + CodeGen code = new CodeGen("return arg ^ 2;").parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched(); + assertEquals("return (xori,arg);", code._stop.toString()); + } + + @Test + public void testBasic12() { + CodeGen code = new CodeGen("return arg + 2.0;").parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched(); + assertEquals("return (addf,(i2f8,arg),2.0f);", code._stop.toString()); + } + + @Test + public void testBasic13() { + CodeGen code = new CodeGen("return arg - 2.0;").parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched(); + assertEquals("return (subf,(i2f8,arg),2.0f);", code._stop.toString()); + } + + @Test + public void testBasic14() { + CodeGen code = new CodeGen("return arg * 2.0;").parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched(); + assertEquals("return (mulf,(i2f8,arg),2.0f);", code._stop.toString()); + } + + @Test + public void testBasic15() { + CodeGen code = new CodeGen("return arg / 2.0;").parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched(); + assertEquals("return (divf,(i2f8,arg),2.0f);", code._stop.toString()); + } + + @Test + public void testBasic16() { + CodeGen code = new CodeGen( +""" +int arg1 = arg + 1; +return arg1 / arg;"""); + code.parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched(); + assertEquals("return (div,(inc,arg),arg);", code._stop.toString()); + } + + @Test + public void testBasic17() { + CodeGen code = new CodeGen( +""" +int arg1 = arg + 1; +return arg1 * arg; +"""); + code.parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched(); + assertEquals("return (mul,(inc,arg),arg);", code._stop.toString()); + } + + @Test + public void testBasic18() { + CodeGen code = new CodeGen( + """ + int arg1 = arg + 1; + return arg1 << arg; + """); + code.parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched(); + assertEquals("return (shl,(inc,arg),arg);", code._stop.toString()); + } + + @Test + public void testBasic19() { + CodeGen code = new CodeGen( + """ + int arg1 = arg + 1; + return arg1 >> arg; + """); + code.parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched(); + assertEquals("return (sar,(inc,arg),arg);", code._stop.toString()); + } + + @Test + public void testBasic20() { + CodeGen code = new CodeGen( + """ + int arg1 = arg + 1; + return arg1 >>> arg; + """); + code.parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched(); + assertEquals("return (shr,(inc,arg),arg);", code._stop.toString()); + } + + @Test + public void testToFloat() { + CodeGen code = new CodeGen(""" + int a = arg; + return a + 2.0; + """ + ).parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched(); + assertEquals("return (addf,(i2f8,arg),2.0f);", code._stop.toString()); + } + + @Test + public void testIfStmt() { + CodeGen code = new CodeGen( +""" +int a = 1; +if (arg == 1) + a = arg+2; +else { + a = arg-3; +} +return a;"""); + code.parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched(); + assertEquals("return Phi(Region,(addi,arg),(addi,arg));", code.print()); + } + + @Test + public void testIfMerge2() { + CodeGen code = new CodeGen( +""" +int a=arg+1; +int b=arg+2; +if( arg==1 ) + b=b+a; +else + a=b+1; +return a+b;"""); + code.parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched(); + assertEquals("return (add,(add,Phi(Region,(shli,arg),arg),arg),Phi(Region,4,5));", code.print()); + } + + @Test + public void testLoop() { + CodeGen code = new CodeGen( +""" +int sum=0; +for( int i=0; i + flt guess = x; + while( 1 ) { + flt next = (x/guess + guess)/2; + if( next == guess ) return guess; + guess = next; + } +}; +flt farg = arg; +return sqrt(farg); +"""); + code.parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched(); + assertEquals("return Phi(Loop,(i2f8,arg),(divf,(addf,(divf,i2f8,Phi_guess),Phi_guess),2.0f));", code.print()); + }; + + + @Test + public void sieveOfEratosthenes() { + CodeGen code = new CodeGen( +""" +var ary = new bool[arg], primes = new int[arg]; +var nprimes=0, p=0; +// Find primes while p^2 < arg +for( p=2; p*p < arg; p++ ) { + // skip marked non-primes + while( ary[p] ) p++; + // p is now a prime + primes[nprimes++] = p; + // Mark out the rest non-primes + for( int i = p + p; i < ary#; i += p ) + ary[i] = true; +} +// Now just collect the remaining primes, no more marking +for( ; p < arg; p++ ) + if( !ary[p] ) + primes[nprimes++] = p; +// Copy/shrink the result array +var !rez = new int[nprimes]; +for( int j=0; j x*x; } : { int x -> x+x; }; +return fcn(2)*10 + fcn(3); +"""); + code.parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched(); + assertEquals("Stop[ return (add,Phi(Region,{ int -> int #1},{ int -> int #2})( 3),(muli,Phi_( 2))); return (mul,Parm_x($fun,int,3,2),x); return (shli,Parm_x($fun,int,3,2)); ]", code.print()); + } + + @Test + public void testFcn2() { + CodeGen code = new CodeGen( +""" +val sq = { int x -> x*x; }; +return sq(arg) + sq(3); +"""); + code.parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched(); + assertEquals("Stop[ return (add,sq(),sq()); return (mul,Parm_x(sq,int,3,arg),x); ]", code.print()); + } + + @Test public void testSelectedMemory() { + for( String cpu : new String[]{"x86_64_v2","riscv"} ) + for( String src : new String[]{Chapter10Test.NESTED_MEMORY,Chapter16Test.CONSTRUCTOR_MEMORY, + Chapter18Test.CALL_MEMORY,Chapter18Test.RECURSIVE_MEMORY} ) { + var code = new CodeGen(src).parse().opto().typeCheck(); + var aliases = memoryPhis(code._stop); + code.instSelect(cpu,"SystemV"); + assertEquals(aliases,memoryPhis(code._stop)); + code.GCM().localSched(); + code._stop.walk(n -> { + if( !(n instanceof CFGNode) && !(n instanceof ProjNode) ) + assertTrue(n.in(0) instanceof CFGNode); + if( n instanceof NewNode nn ) { + assertEquals(2,((TypeTuple)nn._type)._types.length); + assertTrue(nn.mem() instanceof MemMergeNode); + assertNull(nn.mem().in(1)); // Partial allocation input. + MachNode mach = (MachNode)nn; + assertNull(mach.regmap(1)); + assertNotNull(mach.regmap(2)); + assertNotNull(mach.outregmap(0)); + assertNull(mach.outregmap(1)); + } + if( n instanceof CallNode call ) { + assertEquals(4,call.nIns()); // ctrl, memory, two args; direct target is embedded. + } + return null; + }); + } + } + + private static java.util.ArrayList memoryPhis(Node stop) { + var phis = new java.util.ArrayList(); + // Count only definitions reachable from Stop, as instruction selection does. + // A dead Phi cycle can still appear through the reverse use edges. + var seen = new java.util.BitSet(); + var work = new java.util.ArrayList(); + work.add(stop); + for( int i=0; i + n instanceof com.seaofnodes.simple.node.cpus.x86_64_v2.AddMemX86 ? n : null); + assertNotNull(read); + int writes = 0; + for( Node use : read.antiDeps() ) + if( use instanceof MemOpNode st && st.isMem() ) { + assertSame(read.cfg0(),st.cfg0()); + assertTrue(st._inputs.find(read)>=0); + assertTrue(read.cfg0()._outputs.find(read) 0L; + case TypeFloat tf -> 0D; + default -> null; + }; } return new Object[]{new Obj(type,body),null}; - } private Object load(LoadNode load) { @@ -341,7 +344,7 @@ public class Evaluator { assert block != null; for (; i < block.nodes().length; i++) { if (!(block.nodes()[i] instanceof PhiNode phi)) break; - var val = region instanceof FunNode fun && "main".equals(fun.sig()._name) && ((ParmNode)phi)._idx==2 && exit==1 + var val = region instanceof FunNode fun && "main".equals(fun._name) && ((ParmNode)phi)._idx==2 && exit==1 ? parameter : val(phi.in(exit)); phiCache.add(val); diff --git a/src/test/java/com/seaofnodes/simple/evaluator/Scheduler.java b/src/test/java/com/seaofnodes/simple/evaluator/Scheduler.java index 52d8f3e3..4bfa7628 100644 --- a/src/test/java/com/seaofnodes/simple/evaluator/Scheduler.java +++ b/src/test/java/com/seaofnodes/simple/evaluator/Scheduler.java @@ -508,7 +508,7 @@ public class Scheduler { private static CFGNode findSingleCFGOut(Node node) { if (node instanceof StartNode) { for(var n:node._outputs) - if (n instanceof FunNode fun && "main".equals(fun.sig()._name) ) + if (n instanceof FunNode fun && "main".equals(fun._name) ) return fun; return null; } diff --git a/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java b/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java index a5634e82..851380cf 100644 --- a/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java +++ b/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java @@ -126,17 +126,16 @@ public class Fuzzer { var code = new CodeGen(sb.toString()); code.parse(); - int parse_peeps= Node.ITER_CNT; - int parse_nops = Node.ITER_NOP_CNT; + int nids = code.UID(); + if( nids <= max_nids ) return max_nids; // Smaller, looking for trends as we grow + int parse_peeps= code._iter_cnt; + int parse_nops = code._iter_nop_cnt; double parse_nop_ratio = (double)parse_nops/parse_peeps; - int nids = Node.UID(); - if( nids <= max_nids ) return max_nids; - double parse_peeps_per_node = (double)parse_peeps/nids; code.opto(); - int iter_peeps= Node.ITER_CNT; - int iter_nops = Node.ITER_NOP_CNT; + int iter_peeps= code._iter_cnt; + int iter_nops = code._iter_nop_cnt; double iter_nop_ratio = (double)iter_nops/iter_peeps; double iter_peeps_per_node = (double)iter_peeps/nids; @@ -173,7 +172,7 @@ public class Fuzzer { return exceptions.isEmpty(); } - /** Run a fixed seed without reducing or swallowing failures. */ + /** Run a fixed chapter-local seed without reducing or swallowing failures. */ public void fuzzPeepsRegression(long seed) { var rand = new Random(seed); var sb = new StringBuilder(); diff --git a/src/test/java/com/seaofnodes/simple/fuzzer/FuzzerUtils.java b/src/test/java/com/seaofnodes/simple/fuzzer/FuzzerUtils.java index 4509c6fc..5c89a485 100644 --- a/src/test/java/com/seaofnodes/simple/fuzzer/FuzzerUtils.java +++ b/src/test/java/com/seaofnodes/simple/fuzzer/FuzzerUtils.java @@ -4,6 +4,7 @@ import com.seaofnodes.simple.CodeGen; import com.seaofnodes.simple.IterPeeps; import com.seaofnodes.simple.node.Node; import com.seaofnodes.simple.node.StopNode; +import com.seaofnodes.simple.type.TypeInteger; import java.io.OutputStream; import java.io.PrintStream; import java.lang.invoke.MethodHandle; @@ -46,34 +47,11 @@ class FuzzerUtils { return new RuntimeException(e); } - /** - * Copy of Node.WVISIST - * Used to clear as exceptions might happen in the walk and left this bitset not cleared - */ - private static final BitSet NodeWalkVisit; - private static final java.util.Random WorkRandom; - - /** - * Write access to Iterate.MID_ASSERT - * Exception could happen mid assert and left this at true. Used to reset to false prior to parsing. - */ - private static final MethodHandle set_MID_ASSERT; - /** * Some problems might trigger debug output. Suppress this output with a null print stream during fuzzing. */ public static final PrintStream NULL_PRINT_STREAM = new PrintStream(OutputStream.nullOutputStream()); - static { - try { - NodeWalkVisit = getFieldValue(Node.class, "WVISIT"); - WorkRandom = getFieldValue(getFieldValue(IterPeeps.class, "WORK"), "_R"); - set_MID_ASSERT = MethodHandles.lookup().unreflectSetter(getField( IterPeeps.class, "MID_ASSERT")); - } catch (Exception e) { - throw new ExceptionInInitializerError(e); - } - } - /** * Check that the messages are similar. */ @@ -122,17 +100,9 @@ class FuzzerUtils { try { System.setErr(NULL_PRINT_STREAM); System.setOut(NULL_PRINT_STREAM); - try { - set_MID_ASSERT.invokeExact(false); - } catch (Throwable e) { - throw rethrow(e); - } - var code = new CodeGen(script); - code.parse(); - WorkRandom.setSeed(workListSeed); - return code.opto().typeCheck().GCM().localSched(); + var code = new CodeGen(script, TypeInteger.BOT, workListSeed); + return code.parse().opto().typeCheck().GCM().localSched(); } finally { - NodeWalkVisit.clear(); System.setErr(err); System.setOut(out); } diff --git a/src/test/java/com/seaofnodes/simple/fuzzer/ScriptGenerator.java b/src/test/java/com/seaofnodes/simple/fuzzer/ScriptGenerator.java index d061a656..167a5b24 100644 --- a/src/test/java/com/seaofnodes/simple/fuzzer/ScriptGenerator.java +++ b/src/test/java/com/seaofnodes/simple/fuzzer/ScriptGenerator.java @@ -3,12 +3,13 @@ package com.seaofnodes.simple.fuzzer; import com.seaofnodes.simple.Parser; import com.seaofnodes.simple.node.ScopeNode; import com.seaofnodes.simple.type.TypeInteger; - import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Random; import java.util.function.Predicate; +import static com.seaofnodes.simple.Parser.KEYWORDS; + /** * Generate a pseudo random script. @@ -34,18 +35,6 @@ public class ScriptGenerator { * Valid characters for identifiers. The last 10 are not valid for the first character. */ private static final String VAR_CHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_0123456789"; - /** - * List of keywords not valid for identifiers. - */ - private static final HashSet KEYWORDS; - - static { - try { - KEYWORDS = FuzzerUtils.getFieldValue(new Parser(""), "KEYWORDS"); - } catch (Throwable e) { - throw new ExceptionInInitializerError(e); - } - } /** * Flag for a statement that it does not pass on control flow to the next statement. @@ -108,7 +97,6 @@ public class ScriptGenerator { private static final List INTTYPES = new ArrayList<>(); static { - new Parser("", TypeInteger.BOT); for( var e : Parser.defaultTypes().entrySet() ) { if( e.getValue() instanceof TypeInteger ) { INTTYPES.add(new TypeInt(e.getKey())); diff --git a/src/test/java/com/seaofnodes/simple/progs/fcn.smp b/src/test/java/com/seaofnodes/simple/progs/fcn.smp new file mode 100644 index 00000000..43d398b3 --- /dev/null +++ b/src/test/java/com/seaofnodes/simple/progs/fcn.smp @@ -0,0 +1,15 @@ + +struct Person { + int age; +}; + +val fcn = { !Person?[] ps, int x -> + val tmp = ps[x]; + if( ps[x] ) + ps[x].age++; +}; + +!Person?[] !ps = new !Person?[2]; +ps[0] = new Person; +ps[1] = new Person; +fcn(ps,1); diff --git a/src/test/java/com/seaofnodes/simple/progs/fcn2.smp b/src/test/java/com/seaofnodes/simple/progs/fcn2.smp new file mode 100644 index 00000000..049132d9 --- /dev/null +++ b/src/test/java/com/seaofnodes/simple/progs/fcn2.smp @@ -0,0 +1,5 @@ + +val fcn = (arg&1) + ? { int x -> x*x; } + : { int x -> x+x; }; +fcn(arg); diff --git a/src/test/java/com/seaofnodes/simple/progs/fcn3.smp b/src/test/java/com/seaofnodes/simple/progs/fcn3.smp new file mode 100644 index 00000000..55b74455 --- /dev/null +++ b/src/test/java/com/seaofnodes/simple/progs/fcn3.smp @@ -0,0 +1,42 @@ + +//val fact = { int n -> n <= 1 ? 1 : fact(n-1)*n; } +struct LL { !LL? !next; int payload; }; + + +struct String { + + val _hashCodeString = { -> + int hash=0; + for( int i=0; i< cs#; i++ ) + hash = hash*31 + cs[i]; + if( !hash ) hash = 123456789; + return hash; + }; + + val equals = { !String s -> + if( self == s ) return true; + if( cs# != s.cs# ) return false; + for( int i=0; i< #; i++ ) + if( [i] != s.cs[i] ) + return false; + return true; + }; + + int _hashCode = hash(); + u8[] cs; +}; + + + +val hashCode= { !String self -> + int hash = self._hashCode + ? self._hashCode + : self._hashCode = _hashCodeString(self); + return hash; +}; + + +String s = new String { cs = new u8[17]; }; +s.cs[0] = 67; // C +s.cs[1] = 108; // l +hashCode(s); diff --git a/src/test/java/com/seaofnodes/simple/progs/sieve.smp b/src/test/java/com/seaofnodes/simple/progs/sieve.smp new file mode 100644 index 00000000..0788e6c5 --- /dev/null +++ b/src/test/java/com/seaofnodes/simple/progs/sieve.smp @@ -0,0 +1,32 @@ + +int[] !ary = new int[arg], !primes = new int[arg]; +int nprimes = 0, p=2; +// Find primes while p^2 < arg +while( p*p < arg ) { + // skip marked non-primes + while( ary[p]==1 ) p = p + 1; + // p is now a prime + primes[nprimes] = p; nprimes = nprimes + 1; + // Mark out the rest non-primes + int i = p + p; + while( i < ary# ) { + ary[i] = 1; + i = i + p; + } + p = p + 1; +} +// Now just collect the remaining primes, no more marking +while( p < arg ) { + if( ary[p] == 0 ) { + primes[nprimes] = p; nprimes = nprimes + 1; + } + p = p + 1; +} +// Copy/shrink the result array +int[] !rez = new int[nprimes]; +int j = 0; +while( j < nprimes ) { + rez[j] = primes[j]; + j = j + 1; +} +return rez; -- cgit v1.2.3