From 43316b84a9cc0b7ac03a4d85fa4f4dff0f26ccd5 Mon Sep 17 00:00:00 2001 From: Cliff Click Date: Fri, 7 Feb 2025 09:12:14 -0800 Subject: Chapter 20: Graph Coloring Register Allocation 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: 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 | 19 +- src/main/java/com/seaofnodes/simple/CodeGen.java | 305 ----------- .../com/seaofnodes/simple/GlobalCodeMotion.java | 302 ----------- src/main/java/com/seaofnodes/simple/IRPrinter.java | 54 -- src/main/java/com/seaofnodes/simple/IterPeeps.java | 25 +- .../java/com/seaofnodes/simple/ListScheduler.java | 231 --------- src/main/java/com/seaofnodes/simple/Machine.java | 21 - src/main/java/com/seaofnodes/simple/Parser.java | 3 +- src/main/java/com/seaofnodes/simple/RegMask.java | 47 -- src/main/java/com/seaofnodes/simple/SB.java | 1 - .../com/seaofnodes/simple/codegen/BuildLRG.java | 96 ++++ .../com/seaofnodes/simple/codegen/CodeGen.java | 355 +++++++++++++ .../simple/codegen/GlobalCodeMotion.java | 306 +++++++++++ .../java/com/seaofnodes/simple/codegen/IFG.java | 512 +++++++++++++++++++ .../java/com/seaofnodes/simple/codegen/LRG.java | 219 ++++++++ .../seaofnodes/simple/codegen/ListScheduler.java | 258 ++++++++++ .../com/seaofnodes/simple/codegen/Machine.java | 22 + .../com/seaofnodes/simple/codegen/RegAlloc.java | 557 +++++++++++++++++++++ .../com/seaofnodes/simple/codegen/RegMask.java | 139 +++++ .../java/com/seaofnodes/simple/node/AddNode.java | 6 +- .../java/com/seaofnodes/simple/node/AndNode.java | 9 +- .../java/com/seaofnodes/simple/node/BoolNode.java | 10 +- .../com/seaofnodes/simple/node/BulkMemPhiNode.java | 2 +- .../java/com/seaofnodes/simple/node/CFGNode.java | 33 +- .../java/com/seaofnodes/simple/node/CProjNode.java | 2 +- .../com/seaofnodes/simple/node/CallEndNode.java | 115 ++++- .../java/com/seaofnodes/simple/node/CallNode.java | 27 +- .../com/seaofnodes/simple/node/CalleeSaveNode.java | 25 + .../java/com/seaofnodes/simple/node/CastNode.java | 8 +- .../com/seaofnodes/simple/node/ConstantNode.java | 6 +- .../java/com/seaofnodes/simple/node/CtrlNode.java | 2 +- .../java/com/seaofnodes/simple/node/DivFNode.java | 11 +- .../java/com/seaofnodes/simple/node/FunNode.java | 25 +- .../java/com/seaofnodes/simple/node/IfNode.java | 4 +- .../java/com/seaofnodes/simple/node/LoadNode.java | 6 +- .../com/seaofnodes/simple/node/LogicalNode.java | 4 +- .../seaofnodes/simple/node/MachConcreteNode.java | 2 +- .../java/com/seaofnodes/simple/node/MachNode.java | 18 +- .../com/seaofnodes/simple/node/MemMergeNode.java | 7 +- .../java/com/seaofnodes/simple/node/MemOpNode.java | 10 +- .../com/seaofnodes/simple/node/MemPhiNode.java | 2 +- .../java/com/seaofnodes/simple/node/MulNode.java | 4 +- src/main/java/com/seaofnodes/simple/node/Node.java | 95 +++- .../java/com/seaofnodes/simple/node/PhiNode.java | 9 +- .../java/com/seaofnodes/simple/node/ProjNode.java | 1 - .../com/seaofnodes/simple/node/RegionNode.java | 4 +- .../com/seaofnodes/simple/node/ReturnNode.java | 5 +- .../java/com/seaofnodes/simple/node/ScopeNode.java | 1 + .../java/com/seaofnodes/simple/node/SplitNode.java | 26 + .../java/com/seaofnodes/simple/node/StartNode.java | 5 +- .../java/com/seaofnodes/simple/node/StopNode.java | 2 +- .../java/com/seaofnodes/simple/node/StoreNode.java | 1 + .../java/com/seaofnodes/simple/node/XCtrlNode.java | 3 +- .../seaofnodes/simple/node/cpus/arm/AddARM.java | 34 ++ .../seaofnodes/simple/node/cpus/arm/AddFARM.java | 31 ++ .../seaofnodes/simple/node/cpus/arm/AddIARM.java | 51 ++ .../seaofnodes/simple/node/cpus/arm/AndARM.java | 38 ++ .../seaofnodes/simple/node/cpus/arm/AndIARM.java | 56 +++ .../seaofnodes/simple/node/cpus/arm/AsrARM.java | 40 ++ .../seaofnodes/simple/node/cpus/arm/AsrIARM.java | 41 ++ .../seaofnodes/simple/node/cpus/arm/BranchARM.java | 48 ++ .../seaofnodes/simple/node/cpus/arm/CallARM.java | 45 ++ .../simple/node/cpus/arm/CallEndARM.java | 35 ++ .../seaofnodes/simple/node/cpus/arm/CallRRARM.java | 32 ++ .../seaofnodes/simple/node/cpus/arm/CmpARM.java | 30 ++ .../seaofnodes/simple/node/cpus/arm/CmpFARM.java | 31 ++ .../seaofnodes/simple/node/cpus/arm/CmpIARM.java | 54 ++ .../seaofnodes/simple/node/cpus/arm/CmpMemARM.java | 4 + .../seaofnodes/simple/node/cpus/arm/DivARM.java | 33 ++ .../seaofnodes/simple/node/cpus/arm/DivFARM.java | 30 ++ .../seaofnodes/simple/node/cpus/arm/F8ToIARM.java | 28 ++ .../seaofnodes/simple/node/cpus/arm/FloatARM.java | 36 ++ .../seaofnodes/simple/node/cpus/arm/FunARM.java | 21 + .../seaofnodes/simple/node/cpus/arm/I2F8ARM.java | 29 ++ .../seaofnodes/simple/node/cpus/arm/IntARM.java | 38 ++ .../seaofnodes/simple/node/cpus/arm/LoadARM.java | 37 ++ .../seaofnodes/simple/node/cpus/arm/LslARM.java | 41 ++ .../seaofnodes/simple/node/cpus/arm/LslIARM.java | 42 ++ .../seaofnodes/simple/node/cpus/arm/LsrARM.java | 40 ++ .../seaofnodes/simple/node/cpus/arm/LsrIARM.java | 41 ++ .../seaofnodes/simple/node/cpus/arm/MemOpARM.java | 61 +++ .../seaofnodes/simple/node/cpus/arm/MulARM.java | 34 ++ .../seaofnodes/simple/node/cpus/arm/MulFARM.java | 30 ++ .../seaofnodes/simple/node/cpus/arm/MulIARM.java | 43 ++ .../seaofnodes/simple/node/cpus/arm/NewARM.java | 31 ++ .../seaofnodes/simple/node/cpus/arm/NotARM.java | 26 + .../com/seaofnodes/simple/node/cpus/arm/OrARM.java | 42 ++ .../seaofnodes/simple/node/cpus/arm/OrIARM.java | 40 ++ .../seaofnodes/simple/node/cpus/arm/ParmARM.java | 25 + .../seaofnodes/simple/node/cpus/arm/ProjARM.java | 23 + .../seaofnodes/simple/node/cpus/arm/RetARM.java | 49 ++ .../seaofnodes/simple/node/cpus/arm/SetARM.java | 41 ++ .../seaofnodes/simple/node/cpus/arm/SplitARM.java | 22 + .../seaofnodes/simple/node/cpus/arm/StoreARM.java | 52 ++ .../seaofnodes/simple/node/cpus/arm/SubARM.java | 30 ++ .../seaofnodes/simple/node/cpus/arm/SubFARM.java | 32 ++ .../seaofnodes/simple/node/cpus/arm/SubIARM.java | 42 ++ .../seaofnodes/simple/node/cpus/arm/TFPARM.java | 37 ++ .../seaofnodes/simple/node/cpus/arm/XorARM.java | 41 ++ .../seaofnodes/simple/node/cpus/arm/XorIARM.java | 43 ++ .../com/seaofnodes/simple/node/cpus/arm/arm.java | 388 ++++++++++++++ .../com/seaofnodes/simple/node/cpus/arm/i2f8.java | 4 + .../simple/node/cpus/riscv/AddFRISC.java | 4 +- .../simple/node/cpus/riscv/AddIRISC.java | 44 +- .../seaofnodes/simple/node/cpus/riscv/AddRISC.java | 17 +- .../simple/node/cpus/riscv/AndIRISC.java | 41 +- .../seaofnodes/simple/node/cpus/riscv/AndRISC.java | 11 +- .../simple/node/cpus/riscv/BranchRISC.java | 48 ++ .../simple/node/cpus/riscv/CBranchRISC.java | 49 -- .../simple/node/cpus/riscv/CallEndRISC.java | 35 ++ .../simple/node/cpus/riscv/CallRISC.java | 13 +- .../simple/node/cpus/riscv/CallRRISC.java | 14 +- .../simple/node/cpus/riscv/CastRISC.java | 18 + .../simple/node/cpus/riscv/CmpIRISC.java | 44 -- .../seaofnodes/simple/node/cpus/riscv/CmpRISC.java | 28 -- .../simple/node/cpus/riscv/DivFRISC.java | 6 +- .../seaofnodes/simple/node/cpus/riscv/DivRISC.java | 9 +- .../simple/node/cpus/riscv/F8ToIRISC.java | 5 +- .../seaofnodes/simple/node/cpus/riscv/FltRISC.java | 20 +- .../seaofnodes/simple/node/cpus/riscv/FunRISC.java | 7 +- .../simple/node/cpus/riscv/I2F8RISC.java | 3 +- .../seaofnodes/simple/node/cpus/riscv/ImmRISC.java | 40 ++ .../seaofnodes/simple/node/cpus/riscv/IntRISC.java | 28 +- .../simple/node/cpus/riscv/LoadRISC.java | 30 +- .../simple/node/cpus/riscv/MemOpRISC.java | 31 +- .../simple/node/cpus/riscv/MulFRISC.java | 6 +- .../simple/node/cpus/riscv/MulIRISC.java | 35 -- .../seaofnodes/simple/node/cpus/riscv/MulRISC.java | 7 +- .../seaofnodes/simple/node/cpus/riscv/NewRISC.java | 33 +- .../seaofnodes/simple/node/cpus/riscv/NotRISC.java | 25 + .../seaofnodes/simple/node/cpus/riscv/OrIRISC.java | 40 +- .../seaofnodes/simple/node/cpus/riscv/OrRISC.java | 18 +- .../simple/node/cpus/riscv/ParmRISC.java | 11 +- .../simple/node/cpus/riscv/ProjRISC.java | 16 +- .../seaofnodes/simple/node/cpus/riscv/RetRISC.java | 30 +- .../simple/node/cpus/riscv/SetFRISC.java | 33 ++ .../simple/node/cpus/riscv/SetIRISC.java | 17 + .../seaofnodes/simple/node/cpus/riscv/SetRISC.java | 32 +- .../simple/node/cpus/riscv/SllIRISC.java | 41 +- .../seaofnodes/simple/node/cpus/riscv/SllRISC.java | 11 +- .../simple/node/cpus/riscv/SplitRISC.java | 21 + .../simple/node/cpus/riscv/SraIRISC.java | 44 +- .../seaofnodes/simple/node/cpus/riscv/SraRISC.java | 12 +- .../simple/node/cpus/riscv/SrlIRISC.java | 44 +- .../seaofnodes/simple/node/cpus/riscv/SrlRISC.java | 10 +- .../simple/node/cpus/riscv/StoreRISC.java | 30 +- .../simple/node/cpus/riscv/SubFRISC.java | 4 +- .../seaofnodes/simple/node/cpus/riscv/SubRISC.java | 6 +- .../seaofnodes/simple/node/cpus/riscv/TFPRISC.java | 18 +- .../simple/node/cpus/riscv/XorIRISC.java | 42 +- .../seaofnodes/simple/node/cpus/riscv/XorRISC.java | 14 +- .../seaofnodes/simple/node/cpus/riscv/riscv.java | 430 ++++++++-------- .../simple/node/cpus/x86_64_v2/AddFMemX86.java | 14 +- .../simple/node/cpus/x86_64_v2/AddFX86.java | 4 + .../simple/node/cpus/x86_64_v2/AddIX86.java | 2 + .../simple/node/cpus/x86_64_v2/AddMemX86.java | 7 +- .../simple/node/cpus/x86_64_v2/AddX86.java | 6 +- .../simple/node/cpus/x86_64_v2/AndIX86.java | 2 + .../simple/node/cpus/x86_64_v2/AndX86.java | 7 +- .../simple/node/cpus/x86_64_v2/CallEndX86.java | 14 +- .../simple/node/cpus/x86_64_v2/CallRX86.java | 25 +- .../simple/node/cpus/x86_64_v2/CallX86.java | 28 +- .../simple/node/cpus/x86_64_v2/CastX86.java | 18 + .../simple/node/cpus/x86_64_v2/CmpFX86.java | 29 ++ .../simple/node/cpus/x86_64_v2/CmpIX86.java | 20 +- .../simple/node/cpus/x86_64_v2/CmpMemX86.java | 4 +- .../simple/node/cpus/x86_64_v2/CmpX86.java | 5 +- .../simple/node/cpus/x86_64_v2/DivFX86.java | 2 + .../simple/node/cpus/x86_64_v2/DivX86.java | 14 +- .../simple/node/cpus/x86_64_v2/F8ToIX86.java | 3 + .../simple/node/cpus/x86_64_v2/FltX86.java | 11 +- .../simple/node/cpus/x86_64_v2/FunX86.java | 5 +- .../simple/node/cpus/x86_64_v2/I2f8X86.java | 2 + .../simple/node/cpus/x86_64_v2/IntX86.java | 24 +- .../simple/node/cpus/x86_64_v2/JmpX86.java | 6 +- .../simple/node/cpus/x86_64_v2/LeaX86.java | 2 + .../simple/node/cpus/x86_64_v2/LoadX86.java | 6 +- .../simple/node/cpus/x86_64_v2/MemAddX86.java | 4 +- .../simple/node/cpus/x86_64_v2/MemOpX86.java | 20 +- .../simple/node/cpus/x86_64_v2/MulFX86.java | 4 + .../simple/node/cpus/x86_64_v2/MulIX86.java | 11 +- .../simple/node/cpus/x86_64_v2/MulX86.java | 5 +- .../simple/node/cpus/x86_64_v2/NewX86.java | 27 +- .../simple/node/cpus/x86_64_v2/NotX86.java | 26 + .../simple/node/cpus/x86_64_v2/OrIX86.java | 7 +- .../simple/node/cpus/x86_64_v2/OrX86.java | 4 +- .../simple/node/cpus/x86_64_v2/ParmX86.java | 14 +- .../simple/node/cpus/x86_64_v2/ProjX86.java | 8 +- .../simple/node/cpus/x86_64_v2/RetX86.java | 31 +- .../simple/node/cpus/x86_64_v2/SarIX86.java | 2 + .../simple/node/cpus/x86_64_v2/SarX86.java | 15 +- .../simple/node/cpus/x86_64_v2/SetX86.java | 4 +- .../simple/node/cpus/x86_64_v2/ShlIX86.java | 2 + .../simple/node/cpus/x86_64_v2/ShlX86.java | 8 +- .../simple/node/cpus/x86_64_v2/ShrIX86.java | 3 +- .../simple/node/cpus/x86_64_v2/ShrX86.java | 4 +- .../simple/node/cpus/x86_64_v2/SplitX86.java | 21 + .../simple/node/cpus/x86_64_v2/StoreX86.java | 10 +- .../simple/node/cpus/x86_64_v2/SubFX86.java | 2 + .../simple/node/cpus/x86_64_v2/SubX86.java | 2 + .../simple/node/cpus/x86_64_v2/TFPX86.java | 11 +- .../simple/node/cpus/x86_64_v2/XorIX86.java | 7 +- .../simple/node/cpus/x86_64_v2/XorX86.java | 36 ++ .../simple/node/cpus/x86_64_v2/x86_64_v2.java | 286 ++++++----- .../com/seaofnodes/simple/print/ASMPrinter.java | 53 ++ .../com/seaofnodes/simple/print/IRPrinter.java | 61 +++ .../simple/print/SimpleGraphAdapter.java | 2 +- .../simple/print/SimpleGraphObserver.java | 4 +- .../com/seaofnodes/simple/type/TypeFunPtr.java | 4 +- .../com/seaofnodes/simple/type/TypeStruct.java | 1 + .../java/com/seaofnodes/simple/type/TypeTuple.java | 2 + 212 files changed, 6432 insertions(+), 2239 deletions(-) delete mode 100644 src/main/java/com/seaofnodes/simple/ASMPrinter.java delete mode 100644 src/main/java/com/seaofnodes/simple/CodeGen.java delete mode 100644 src/main/java/com/seaofnodes/simple/GlobalCodeMotion.java delete mode 100644 src/main/java/com/seaofnodes/simple/IRPrinter.java delete mode 100644 src/main/java/com/seaofnodes/simple/ListScheduler.java delete mode 100644 src/main/java/com/seaofnodes/simple/Machine.java delete mode 100644 src/main/java/com/seaofnodes/simple/RegMask.java create mode 100644 src/main/java/com/seaofnodes/simple/codegen/BuildLRG.java create mode 100644 src/main/java/com/seaofnodes/simple/codegen/CodeGen.java create mode 100644 src/main/java/com/seaofnodes/simple/codegen/GlobalCodeMotion.java create mode 100644 src/main/java/com/seaofnodes/simple/codegen/IFG.java create mode 100644 src/main/java/com/seaofnodes/simple/codegen/LRG.java create mode 100644 src/main/java/com/seaofnodes/simple/codegen/ListScheduler.java create mode 100644 src/main/java/com/seaofnodes/simple/codegen/Machine.java create mode 100644 src/main/java/com/seaofnodes/simple/codegen/RegAlloc.java create mode 100644 src/main/java/com/seaofnodes/simple/codegen/RegMask.java create mode 100644 src/main/java/com/seaofnodes/simple/node/CalleeSaveNode.java create mode 100644 src/main/java/com/seaofnodes/simple/node/SplitNode.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/arm/AddARM.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/arm/AddFARM.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/arm/AddIARM.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/arm/AndARM.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/arm/AndIARM.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/arm/AsrARM.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/arm/AsrIARM.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/arm/BranchARM.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/arm/CallARM.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/arm/CallEndARM.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/arm/CallRRARM.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/arm/CmpARM.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/arm/CmpFARM.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/arm/CmpIARM.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/arm/CmpMemARM.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/arm/DivARM.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/arm/DivFARM.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/arm/F8ToIARM.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/arm/FloatARM.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/arm/FunARM.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/arm/I2F8ARM.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/arm/IntARM.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/arm/LoadARM.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/arm/LslARM.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/arm/LslIARM.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/arm/LsrARM.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/arm/LsrIARM.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/arm/MemOpARM.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/arm/MulARM.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/arm/MulFARM.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/arm/MulIARM.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/arm/NewARM.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/arm/NotARM.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/arm/OrARM.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/arm/OrIARM.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/arm/ParmARM.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/arm/ProjARM.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/arm/RetARM.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/arm/SetARM.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/arm/SplitARM.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/arm/StoreARM.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/arm/SubARM.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/arm/SubFARM.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/arm/SubIARM.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/arm/TFPARM.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/arm/XorARM.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/arm/XorIARM.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/arm/arm.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/arm/i2f8.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/riscv/BranchRISC.java delete 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/CallEndRISC.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/riscv/CastRISC.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/riscv/CmpIRISC.java delete 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/ImmRISC.java delete 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/NotRISC.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/riscv/SetFRISC.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/riscv/SetIRISC.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/riscv/SplitRISC.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CastX86.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CmpFX86.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/NotX86.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SplitX86.java create mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/XorX86.java create mode 100644 src/main/java/com/seaofnodes/simple/print/ASMPrinter.java create mode 100644 src/main/java/com/seaofnodes/simple/print/IRPrinter.java (limited to 'src/main/java/com/seaofnodes/simple') diff --git a/src/main/java/com/seaofnodes/simple/ASMPrinter.java b/src/main/java/com/seaofnodes/simple/ASMPrinter.java deleted file mode 100644 index 3c4162ea..00000000 --- a/src/main/java/com/seaofnodes/simple/ASMPrinter.java +++ /dev/null @@ -1,51 +0,0 @@ -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 73ada8e0..f43179ad 100644 --- a/src/main/java/com/seaofnodes/simple/Ary.java +++ b/src/main/java/com/seaofnodes/simple/Ary.java @@ -29,6 +29,10 @@ public class Ary extends AbstractList implements List { range_check(i); return _es[i]; } + public E atX( int i ) { + if( i >= _len ) return null; + return _es[i]; + } /** @return last element */ public E last( ) { return at(_len-1); } @@ -76,6 +80,17 @@ public class Ary extends AbstractList implements List { return e; } + /** Slow, linear-time, element insertion. Preserves order. + * @param i where to insert + * @return element inserted */ + public E insert( E e, int i ) { + setLen(_len+1); + System.arraycopy(_es,i,_es,i+1,_len-i); + _es[i] = e; + return e; + } + + /** Remove all elements */ public void clear( ) { _len=0; } @@ -158,10 +173,6 @@ public class Ary extends AbstractList implements List { /** @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); } - /** Find the first matching element using ==, or -1 if none. Note that * most del calls shuffle the list, so the first element might be random. * @param e Element to find diff --git a/src/main/java/com/seaofnodes/simple/CodeGen.java b/src/main/java/com/seaofnodes/simple/CodeGen.java deleted file mode 100644 index 0a3a60e8..00000000 --- a/src/main/java/com/seaofnodes/simple/CodeGen.java +++ /dev/null @@ -1,305 +0,0 @@ - package com.seaofnodes.simple; - -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; - - // Inlining can insert CFG edges and invalidate cached depth ordering. - private char _iDepthVersion; - public char iDepthVersion() { return _iDepthVersion; } - public void invalidateIDepthCaches() { - assert _iDepthVersion < Character.MAX_VALUE : "Dominator cache version exceeds 65535"; - ++_iDepthVersion; - } - - - // Last created CodeGen as a global; used all over to avoid passing about a - // "context". - public static CodeGen CODE; - - public enum Phase { - 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++; } - - - // 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; - - // 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; - - // Parse ASCII text into Sea-of-Nodes IR - public CodeGen parse() { - assert _phase == null; - _phase = Phase.Parse; - - P.parse(); - if( _obs != null ) _obs.phase("Parse"); - 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; - - // 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.ordinal() <= Phase.Opto.ordinal(); - _phase = Phase.TypeCheck; - - Parser.ParseException err = _stop.walk(n -> { - Parser.ParseException msg = n.err(); - if( msg != null && _obs != null ) _obs.error(n, msg.getMessage()); - return msg; - }); - if( err != null ) - throw err; - if( _obs != null ) _obs.phase("TypeCheck"); - return this; - } - - - // --------------------------- - // 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.ordinal() <= Phase.InstructionSelection.ordinal(); - _phase = Phase.Schedule; - - // Build the loop tree, fix never-exit loops - _start.buildLoopTree(_stop); - GlobalCodeMotion.buildCFG(this); - - return this; - } - - // --------------------------- - // Local (basic block) scheduler phase, a classic list scheduler - public CodeGen localSched() { - assert _phase == Phase.Schedule; - _phase = Phase.LocalSched; - ListScheduler.sched(this); - return this; - } - - - // --------------------------- - 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); } - - // 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 deleted file mode 100644 index 95ada83d..00000000 --- a/src/main/java/com/seaofnodes/simple/GlobalCodeMotion.java +++ /dev/null @@ -1,302 +0,0 @@ - package com.seaofnodes.simple; - -import com.seaofnodes.simple.IterPeeps.WorkList; -import com.seaofnodes.simple.node.*; -import com.seaofnodes.simple.type.*; -import java.util.*; - -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( 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. - private static void breakUpGlobalConstants( StartNode start ) { - var globals = new IdentityHashMap(); - var cons = new ArrayList(); - for( Node con : start._outputs ) - if( con!=null && !(con instanceof CFGNode) && - (con.isConst() || (con instanceof MachNode mach && mach.outregmap()!=null)) ) { - globals.put(con,true); - cons.add(con.keep()); // Preserve original inputs while rewiring users. - } - - var copies = new IdentityHashMap>(); - for( Node con : cons ) - for( Node use : con._outputs.asAry() ) { - if( use==null || globals.containsKey(use) ) continue; - for( int i=0; i new IdentityHashMap<>()); - use.setDef(i,cloneGlobal(con,fun,globals,local)); - } - } - for( Node con : cons ) con.unkill(); - } - - private static Node cloneGlobal(Node con, FunNode fun, IdentityHashMap globals, - IdentityHashMap copies) { - Node copy = copies.get(con); - if( copy!=null ) return copy; - copies.put(con,copy=con.copyEmpty()); - copy.addDef(fun); - for( int i=1; i early.idepth() ) - early = n.in(i).cfg0(); // Latest/deepest input - n.setDef(0,early); - } - } - - // ------------------------------------------------------------------------ - 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[code.UID()]; - // Breadth-first scheduling - breadth(code._stop,ns,late,anti); - - // Copy the best placement choice into the control slot - for( int i=0; i work = new WorkList<>(); - work.push(stop); - Node n; - outer: - while( (n = work.pop()) != null ) { - assert late[n._nid]==null; // No double visit - // These I know the late schedule of, and need to set early for loops - if( n instanceof CFGNode cfg ) late[n._nid] = cfg.blockHead() ? cfg : cfg.cfg(0); - else if( n instanceof PhiNode phi ) late[n._nid] = phi.region(); - else if( n instanceof ProjNode && n.in(0) instanceof CFGNode cfg ) late[n._nid] = cfg; - else { - - // All uses done? - for( Node use : n._outputs ) - if( use!=null && late[use._nid]==null ) - continue outer; // Nope, await all uses done - - // Loads need their memory inputs' uses also done - if( n instanceof MemOpNode ld && !ld.isMem() ) - for( Node memuse : ld.antiDeps() ) - if( late[memuse._nid]==null ) - continue outer; - - // All uses done, schedule - _doSchedLate(n,ns,late,anti); - } - - // A use just finished; reconsider its inputs and waiting loads, - // even when the shared memory input was already scheduled. - for( Node def : n._inputs ) { - if( def==null ) continue; - if( late[def._nid]==null ) work.push(def); - wakeLoads(def,late,work,new BitSet()); - } - if( n instanceof LoopNode loop ) - for( Node phi : loop._outputs ) - if( phi instanceof PhiNode && late[phi._nid]==null ) - work.push(phi); - } - } - - private static void _doSchedLate(Node n, Node[] ns, CFGNode[] late, int[] anti) { - // Walk uses, gathering the LCA (Least Common Ancestor) of uses - CFGNode early = n.in(0) instanceof CFGNode cfg ? cfg : n.in(0).cfg0(); - assert early != null; - CFGNode lca = null; - for( Node use : n._outputs ) - if( use != null ) - lca = use_block(n,use, late).domLCA(lca,null); - - // Loads may need anti-dependencies, raising their LCA - if( n instanceof MemOpNode load && !load.isMem() ) - lca = find_anti_dep(lca,load,early,late,anti); - - // Walk up from the LCA to the early, looking for best place. This is - // the lowest execution frequency, approximated by least loop depth and - // deepest control flow. - CFGNode best = lca; - lca = lca.idom(); // Already found best for starting LCA - for( ; lca != early.idom(); lca = lca.idom() ) - if( better(lca,best) ) - best = lca; - assert !(best instanceof IfNode); - ns [n._nid] = n; - late[n._nid] = best; - } - - // Block of use. Normally from late[] schedule, except for Phis, which go - // to the matching Region input. - private static CFGNode use_block(Node n, Node use, CFGNode[] late) { - if( !(use instanceof PhiNode phi) ) - return late[use._nid]; - CFGNode found=null; - for( int i=1; i best.idepth() || - best instanceof IfNode; - } - - // An allocation may consume a load's memory through a partial aggregate. - private static void wakeLoads(Node def, CFGNode[] late, WorkList work, BitSet visit) { - if( visit.get(def._nid) ) return; - visit.set(def._nid); - for( Node out : def._outputs ) - if( out instanceof MemOpNode ld && !ld.isMem() && late[ld._nid]==null ) work.push(ld); - if( def instanceof MemMergeNode ) - for( int i=1; iearly, flagging Load's block location choices - for( CFGNode cfg=lca; early!=null && cfg!=early.idom(); cfg = cfg.idom() ) - anti[cfg._nid] = load._nid; - // Walk load->mem uses, looking for Stores causing an anti-dep - for( Node mem : load.antiDeps() ) { - switch( mem ) { - case MemOpNode st: - assert late[st._nid]!=null; - lca = anti_dep(load,late[st._nid],st.cfg0(),lca,st,anti); - break; - case CallNode st: - assert late[st._nid]!=null; - lca = anti_dep(load,late[st._nid],st.cfg0(),lca,st,anti); - break; - case PhiNode phi: - // Repeat anti-dep for matching Phi inputs. - // No anti-dep edges but may raise the LCA. - for( int i=1; i { - private static final com.seaofnodes.print.IRPrinter PRINT = - new com.seaofnodes.print.IRPrinter<>(new IRPrinter()); - @Override public String type(Node n) { return n._type==null ? "" : n._type.str(); } - @Override public String inputMark(Node n, Node def) { return n instanceof MemMergeNode && def instanceof MemMergeNode ? "^" : " "; } - @Override public Kind kind(Node n) { - if( n instanceof StartNode ) return Kind.START; - if( n instanceof StopNode ) return Kind.STOP; - if( n instanceof FunNode ) return Kind.FUN; - if( n instanceof LoopNode ) return Kind.LOOP; - if( n instanceof RegionNode ) return Kind.REGION; - if( n instanceof ParmNode ) return Kind.PARM; - if( n instanceof PhiNode ) return Kind.PHI; - if( n instanceof CallEndNode ) return Kind.CALL_END; - if( n instanceof CallNode ) return Kind.CALL; - if( n instanceof ReturnNode ) return Kind.RETURN; - if( n instanceof ConstantNode ) return Kind.CONSTANT; - if( n instanceof CProjNode ) return Kind.CPROJ; - if( n instanceof ProjNode ) return n instanceof CFGNode ? Kind.CPROJ : Kind.PROJ; - if( n instanceof CFGNode ) return Kind.CTRL; - if( n instanceof MultiNode ) return Kind.MULTI; - return Kind.DATA; - } - @Override public int index(Node n) { - if( n instanceof ProjNode p ) return p._idx; - if( n instanceof CProjNode p ) return p._idx; - return n._nid; - } - @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._name==null ? "" : fun._name)+" "+fun.sig().str(); - } - - public static String prettyPrint(Node n, int depth) { return PRINT.prettyPrint(n,depth); } - public static SB printLine(Node n, SB sb) { return sb.p(PRINT.line(n)); } - - public static String prettyPrint(CodeGen code) { - var units=new ArrayList>(); - var funs=new ArrayList(); - for( Node n : code._start._outputs ) if( n instanceof FunNode ) funs.add(n); - units.add(new com.seaofnodes.print.IRPrinter.Unit<>(null,null,null,funs)); - return PRINT.program(code._start,code._stop,units); - } -} diff --git a/src/main/java/com/seaofnodes/simple/IterPeeps.java b/src/main/java/com/seaofnodes/simple/IterPeeps.java index 087a990b..a21fb517 100644 --- a/src/main/java/com/seaofnodes/simple/IterPeeps.java +++ b/src/main/java/com/seaofnodes/simple/IterPeeps.java @@ -1,7 +1,7 @@ package com.seaofnodes.simple; +import com.seaofnodes.simple.codegen.CodeGen; import com.seaofnodes.simple.node.*; - import java.util.Arrays; import java.util.BitSet; import java.util.Random; @@ -46,7 +46,12 @@ public class IterPeeps { private final WorkList _work; - IterPeeps( long seed ) { _work = new WorkList<>(seed); } + private final WorkList _workInline; + + public IterPeeps( long seed ) { + _work = new WorkList<>(seed); + _workInline = new WorkList<>(seed); + } public N add( N n ) { return (N)_work.push(n); } @@ -56,12 +61,24 @@ public class IterPeeps { * Iterate peepholes to a fixed point */ public void iterate( CodeGen code ) { + while( true ) { + iteratePeeps(code); + CallEndNode cend; + while( (cend=_workInline.pop())!=null ) + if( cend.inlineSmall() ) break; + if( cend==null ) return; + } + } + + // Only try growing inlines once the shrinking peepholes reach a fixed point. + private void iteratePeeps( CodeGen code ) { assert progressOnList(code); int cnt=0; Node n; while( (n=_work.pop()) != null ) { if( n.isDead() ) continue; + if( n instanceof CallEndNode cend ) _workInline.push(cend); cnt++; // Useful for debugging, searching which peephole broke things var obs = CodeGen.CODE._obs; if( obs != null ) obs.before(n); @@ -141,7 +158,7 @@ public class IterPeeps { /* Useful stat - how many nodes are processed in the post parse iterative opt */ private long _totalWork = 0; - WorkList() { this(123); } + public WorkList() { this(123); } WorkList(long seed) { _es = new Node[1]; _len=0; @@ -182,7 +199,7 @@ public class IterPeeps { /** * Removes a random Node from the WorkList; null if WorkList is empty */ - E pop() { + public E pop() { if( _len == 0 ) return null; int idx = _R.nextInt(_len); E x = (E)_es[idx]; diff --git a/src/main/java/com/seaofnodes/simple/ListScheduler.java b/src/main/java/com/seaofnodes/simple/ListScheduler.java deleted file mode 100644 index efca945a..00000000 --- a/src/main/java/com/seaofnodes/simple/ListScheduler.java +++ /dev/null @@ -1,231 +0,0 @@ - package com.seaofnodes.simple; - -import com.seaofnodes.simple.IterPeeps.WorkList; -import com.seaofnodes.simple.node.*; -import com.seaofnodes.simple.type.*; -import java.util.*; - -public abstract class ListScheduler { - - // 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); } - - static XSched get(Node n) { return n==null ? null : XS.get(n); } - - private XSched init(CFGNode bb, Node n) { - _n = n; - _bcnt = 0; - _ruse = _rdef = _single = false; - - // Count block-locals and remote inputs - if( !(n instanceof PhiNode) ) - for( Node def : n._inputs ) - if( def != null && def != bb ) { - if( def.cfg0()==bb ) _bcnt++; // Raise block-local input count - else _ruse = true; - } - // Count remote outputs and allowed registers - if( n instanceof MultiNode ) - for( Node use : n._inputs ) - computeSingleRDef(bb,use); - else - computeSingleRDef(bb,n); - return this; - } - - private void computeSingleRDef(CFGNode bb, Node n) { - RegMaskRW rmask = n instanceof MachNode mach && mach.outregmap()!=null ? mach.outregmap().copy() : null; - for( Node use : n._outputs ) { - // Remote use, so this is a remote def - if( use!=null && use.cfg0()!=bb ) _rdef = true; - // And mask between n's def mask and all n's users masks' - if( use instanceof MachNode mach && rmask != null ) - for( int i=1; i 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; - - // 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 bcnts[]. Once - // that gets to 0, all the nodes using it are scheduled, so it's ready. - int sched = 0; - 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 ) - for( Node useuse : use._outputs ) - ready = ready(bb,useuse,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 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( Ary blk, int sched, int ready ) { - int pick = sched; - int score = score(blk.at(pick)); - for( int i=sched+1; i score ) - { score = nscore; pick = i; } - } - return pick; - } - - // Highest score wins. Max at 1000, min at 10, except specials. - 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=0; for( int i=0; i _obs; + + // Inlining can insert CFG edges and invalidate cached depth ordering. + private char _iDepthVersion; + public char iDepthVersion() { return _iDepthVersion; } + public void invalidateIDepthCaches() { + assert _iDepthVersion < Character.MAX_VALUE : "Dominator cache version exceeds 65535"; + ++_iDepthVersion; + } + + + // Last created CodeGen as a global; used all over to avoid passing about a + // "context". + public static CodeGen CODE; + + public enum Phase { + Parse, // Parse ASCII text into Sea-of-Nodes IR + Opto, // Run ideal optimizations + TypeCheck, // Last check for bad programs + InstSelect, // Convert to target hardware nodes + Schedule, // Global schedule (code motion) nodes + LocalSched, // Local schedule + RegAlloc, // Register allocation + Encoding, // Encoding + } + public Phase _phase; + + // --------------------------- + // Compilation source code + public final String _src; + // Compile-time known initial argument type + public final TypeInteger _arg; + + // --------------------------- + 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. + */ + public 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++; } + + + // 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; + + // 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; + + // Parse ASCII text into Sea-of-Nodes IR + public int _tParse; + public CodeGen parse() { + assert _phase == null; + _phase = Phase.Parse; + long t0 = System.currentTimeMillis(); + + P.parse(); + _tParse = (int)(System.currentTimeMillis() - t0); + if( _obs != null ) _obs.phase("Parse"); + 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 int _tOpto; + public CodeGen opto() { + assert _phase == Phase.Parse; + _phase = Phase.Opto; + long t0 = System.currentTimeMillis(); + + // Pessimistic peephole optimization on a worklist + _iter.iterate(this); + _tOpto = (int)(System.currentTimeMillis() - t0); + + // 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 + int _tTypeCheck; + public CodeGen typeCheck() { + // Demand phase Opto for cleaning up dead control flow at least, + // required for the following GCM. + assert _phase.ordinal() <= Phase.Opto.ordinal(); + _phase = Phase.TypeCheck; + long t0 = System.currentTimeMillis(); + + Parser.ParseException err = _stop.walk(n -> { + Parser.ParseException msg = n.err(); + if( msg != null && _obs != null ) _obs.error(n, msg.getMessage()); + return msg; + }); + _tTypeCheck = (int)(System.currentTimeMillis() - t0); + if( err != null ) + throw err; + if( _obs != null ) _obs.phase("TypeCheck"); + return this; + } + + + // --------------------------- + // Code generation CPU target + public Machine _mach; + // + public String _callingConv; + + // Convert to target hardware nodes + public int _tInsSel; + public CodeGen instSelect( String base, String cpu, String callingConv ) { + assert _phase.ordinal() <= Phase.TypeCheck.ordinal(); + _phase = Phase.InstSelect; + + _callingConv = callingConv; + + // Not timing the class load... + // Look for CPU in fixed named place: + // com.seaofnodes.simple.node.cpus."cpu"."cpu.class" + String clzFile = base+"."+cpu+"."+cpu; + try { _mach = ((Class) Class.forName( clzFile )).getDeclaredConstructor().newInstance(); } + catch( Exception e ) { throw new RuntimeException(e); } + + // Convert to machine ops + long t0 = System.currentTimeMillis(); + _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(); + _tInsSel = (int)(System.currentTimeMillis() - t0); + 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 MachNode already, 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 MachNode ) { + 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. + int _tGCM; + public Ary _cfg = new Ary<>(CFGNode.class); + + // Global schedule (code motion) nodes + public CodeGen GCM() { + assert _phase.ordinal() <= Phase.InstSelect.ordinal(); + _phase = Phase.Schedule; + long t0 = System.currentTimeMillis(); + + // Build the loop tree, fix never-exit loops + _start.buildLoopTree(_stop); + GlobalCodeMotion.buildCFG(this); + _tGCM = (int)(System.currentTimeMillis() - t0); + + return this; + } + + // --------------------------- + // Local (basic block) scheduler phase, a classic list scheduler + int _tLocal; + public CodeGen localSched() { + assert _phase == Phase.Schedule; + _phase = Phase.LocalSched; + long t0 = System.currentTimeMillis(); + ListScheduler.sched(this); + _tLocal = (int)(System.currentTimeMillis() - t0); + return this; + } + + + // --------------------------- + // Register Allocation + int _tRegAlloc; + public RegAlloc _regAlloc; + public CodeGen regAlloc() { + assert _phase == Phase.LocalSched; + _phase = Phase.RegAlloc; + long t0 = System.currentTimeMillis(); + _regAlloc = new RegAlloc(this); + _regAlloc.regAlloc(); + _tRegAlloc = (int)(System.currentTimeMillis() - t0); + return this; + } + public String reg(Node n) { + if( _phase.ordinal() >= Phase.RegAlloc.ordinal() ) { + String s = _regAlloc.reg(n); + if( s!=null ) return s; + } + return "N"+ n._nid; + } + + // --------------------------- + // Encoding + int _tEncode; + public CodeGen encode() { + assert _phase == Phase.RegAlloc; + _phase = Phase.Encoding; + long t0 = System.currentTimeMillis(); + + + //_tEncode = (int)(System.currentTimeMillis() - t0); + //throw Utils.TODO(); + return this; + } + // Encoded binary, no relocation info + public byte[] binary() { throw Utils.TODO(); } + + // --------------------------- + SB asm(SB sb) { return ASMPrinter.print(sb,this); } + public String asm() { return asm(new SB()).toString(); } + + + // Testing shortcuts + public Node ctrl() { return _stop.ret().ctrl(); } + public Node expr() { return _stop.ret().expr(); } + public String print() { return _stop.print(); } + + // Debugging helper + @Override public String toString() { + return _phase.ordinal() > Phase.Schedule.ordinal() + ? IRPrinter._prettyPrint( this ) + : IRPrinter.prettyPrint(this); + } + + // Debugging helper + public Node f(int idx) { return _stop.find(idx); } +} diff --git a/src/main/java/com/seaofnodes/simple/codegen/GlobalCodeMotion.java b/src/main/java/com/seaofnodes/simple/codegen/GlobalCodeMotion.java new file mode 100644 index 00000000..52906afb --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/codegen/GlobalCodeMotion.java @@ -0,0 +1,306 @@ +package com.seaofnodes.simple.codegen; + +import com.seaofnodes.simple.Ary; +import com.seaofnodes.simple.IterPeeps.WorkList; +import com.seaofnodes.simple.Utils; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.type.*; +import java.util.*; + +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( 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++ ) + rpo.swap(i,rpo.size()-1-i); + // Set global CFG + code._cfg = rpo; + + schedEarly(code); + + // Break up shared global constants by functions + 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 CallNode call ) call.unlink_all(); + if( !(use instanceof CFGNode cfg) || visit.get(cfg._nid) ) + return; // Been there, done that + if( def instanceof ReturnNode && use instanceof CallEndNode ) + return; + assert !( def instanceof CallNode && use instanceof FunNode ); + 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. + private static void breakUpGlobalConstants( StartNode start ) { + var globals = new IdentityHashMap(); + var cons = new ArrayList(); + for( Node con : start._outputs ) + if( con!=null && !(con instanceof CFGNode) && + (con.isConst() || (con instanceof MachNode mach && mach.outregmap()!=null)) ) { + globals.put(con,true); + cons.add(con.keep()); // Preserve original inputs while rewiring users. + } + + var copies = new IdentityHashMap>(); + for( Node con : cons ) + for( Node use : con._outputs.asAry() ) { + if( use==null || globals.containsKey(use) ) continue; + FunNode fun = useFun(use); + if( fun==null ) continue; // Global metadata has no function owner. + var local = copies.computeIfAbsent(fun,f -> new IdentityHashMap<>()); + Node copy = cloneGlobal(con,fun,globals,local); + for( int i=0; i globals, + IdentityHashMap copies) { + Node copy = copies.get(con); + if( copy!=null ) return copy; + copies.put(con,copy=con.copyEmpty()); + copy.addDef(fun); + for( int i=1; i early.idepth() ) + early = n.in(i).cfg0(); // Latest/deepest input + n.setDef(0,early); + } + } + + // ------------------------------------------------------------------------ + 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[code.UID()]; + // Breadth-first scheduling + breadth(code._stop,ns,late,anti); + + // Copy the best placement choice into the control slot + for( int i=0; i work = new WorkList<>(); + work.push(stop); + Node n; + outer: + while( (n = work.pop()) != null ) { + assert late[n._nid]==null; // No double visit + // These I know the late schedule of, and need to set early for loops + if( n instanceof CFGNode cfg ) late[n._nid] = cfg.blockHead() ? cfg : cfg.cfg(0); + else if( n instanceof PhiNode phi ) late[n._nid] = phi.region(); + else if( n instanceof ProjNode && n.in(0) instanceof CFGNode cfg ) late[n._nid] = cfg; + else { + + // All uses done? + for( Node use : n._outputs ) + if( use!=null && late[use._nid]==null ) + continue outer; // Nope, await all uses done + + // Loads need their memory inputs' uses also done + if( n instanceof MemOpNode ld && !ld.isMem() ) + for( Node memuse : ld.antiDeps() ) + if( late[memuse._nid]==null ) + continue outer; + + // All uses done, schedule + _doSchedLate(n,ns,late,anti); + } + + // A use just finished; reconsider its inputs and waiting loads, + // even when the shared memory input was already scheduled. + for( Node def : n._inputs ) { + if( def==null ) continue; + if( late[def._nid]==null ) work.push(def); + wakeLoads(def,late,work,new BitSet()); + } + if( n instanceof LoopNode loop ) + for( Node phi : loop._outputs ) + if( phi instanceof PhiNode && late[phi._nid]==null ) + work.push(phi); + } + } + + private static void _doSchedLate(Node n, Node[] ns, CFGNode[] late, int[] anti) { + // Walk uses, gathering the LCA (Least Common Ancestor) of uses + CFGNode early = n.in(0) instanceof CFGNode cfg ? cfg : n.in(0).cfg0(); + assert early != null; + CFGNode lca = null; + for( Node use : n._outputs ) + if( use != null ) + lca = use_block(n,use, late).domLCA(lca,null); + + // Loads may need anti-dependencies, raising their LCA + if( n instanceof MemOpNode load && !load.isMem() ) + lca = find_anti_dep(lca,load,early,late,anti); + + // Walk up from the LCA to the early, looking for best place. This is + // the lowest execution frequency, approximated by least loop depth and + // deepest control flow. + CFGNode best = lca; + lca = lca.idom(); // Already found best for starting LCA + for( ; lca != early.idom(); lca = lca.idom() ) + if( better(lca,best) ) + best = lca; + assert !(best instanceof IfNode); + ns [n._nid] = n; + late[n._nid] = best; + } + + // Block of use. Normally from late[] schedule, except for Phis, which go + // to the matching Region input. + private static CFGNode use_block(Node n, Node use, CFGNode[] late) { + if( use instanceof ParmNode ) return late[use._nid]; + if( !(use instanceof PhiNode phi) ) + return late[use._nid]; + CFGNode found=null; + for( int i=1; i best.idepth() || + best instanceof IfNode; + } + + // An allocation may consume a load's memory through a partial aggregate. + private static void wakeLoads(Node def, CFGNode[] late, WorkList work, BitSet visit) { + if( visit.get(def._nid) ) return; + visit.set(def._nid); + for( Node out : def._outputs ) + if( out instanceof MemOpNode ld && !ld.isMem() && late[ld._nid]==null ) work.push(ld); + if( def instanceof MemMergeNode ) + for( int i=1; iearly, flagging Load's block location choices + for( CFGNode cfg=lca; early!=null && cfg!=early.idom(); cfg = cfg.idom() ) + anti[cfg._nid] = load._nid; + // Walk load->mem uses, looking for Stores causing an anti-dep + for( Node mem : load.antiDeps() ) { + switch( mem ) { + case MemOpNode st: + assert late[st._nid]!=null; + lca = anti_dep(load,late[st._nid],st.cfg0(),lca,st,anti); + break; + case CallNode st: + assert late[st._nid]!=null; + lca = anti_dep(load,late[st._nid],st.cfg0(),lca,st,anti); + break; + case PhiNode phi: + // Repeat anti-dep for matching Phi inputs. + // No anti-dep edges but may raise the LCA. + for( int i=1; i> BBOUTS = new IdentityHashMap<>(); + static void resetBBLiveOut() { + for( IdentityHashMap bbout : BBOUTS.values() ) + bbout.clear(); + } + static final IdentityHashMap TMP = new IdentityHashMap<>(); + + // Inteference Graph: Array of Bitsets + private static final Ary IFG = new Ary<>(BitSet.class); + static void resetIFG() { + for( BitSet bs : IFG ) + if( bs!=null ) bs.clear(); + } + + // Set matching bit + private static void addIFG( LRG lrg0, LRG lrg1 ) { + short x0 = lrg0._lrg, x1 = lrg1._lrg; + // Triangulate + if( x0 < x1 ) _addIFG(x0,x1); + else _addIFG(x1,x0); + } + // Add lrg1 to lrg0's conflict set + private static void _addIFG( short x0, short x1 ) { + BitSet ifg = IFG.atX(x0); + if( ifg==null ) + IFG.setX(x0,ifg = new BitSet()); + ifg.set(x1); + } + + + private static final Ary WORK = new Ary<>(CFGNode.class); + static void pushWork(CFGNode bb) { + if( WORK.find(bb) == -1 ) + WORK.push(bb); + } + + // ------------------------------------------------------------------------ + // Visit all blocks, using the live-out LRGs per-block and doing a backwards + // walk over each block. At the end of the walk, push the live-out sets to + // prior blocks. Due to loops, no single visitation order suffices. This + // problem can be fairly efficiently solved with a proper LIVE calculation + // and bitsets. In the interests of simplicity (and assumption of smaller + // programs) I am skipping LIVE and directly computing it during the IFG + // building. + + // Start by setting the live-out of the exit block (to empty), and putting + // it on a worklist. + + // - Pull from worklist a block who's live-out has changed + // - Walk backwards, adding interferences and computing live. + // - At block head, "push" the new live-out to prior blocks. + // - - If they pick up new live-outs, put them on the worklist. + + public static boolean build( int round, RegAlloc alloc ) { + // Reset all to empty + resetBBLiveOut(); + resetIFG(); + + // Last block has nothing live out + assert WORK.isEmpty(); + for( CFGNode bb : alloc._code._cfg ) + if( bb.blockHead()) + WORK.push( bb ); + + // Process blocks until no more changes + while( !WORK.isEmpty() ) + do_block(round,alloc,WORK.pop()); + + return alloc.success(); + } + + // Walk one block backwards, compute live-in from live-out, and build IFG + static void do_block( int round, RegAlloc alloc, CFGNode bb ) { + assert bb.blockHead(); + IdentityHashMap live_out = BBOUTS.get(bb); + TMP.clear(); + if( live_out != null ) + TMP.putAll(live_out); // Copy bits to temp + + // A backwards walk over instructions in the basic block + for( int inum = bb.nOuts()-1; inum >= 0; inum-- ) { + Node n = bb.out(inum); + if( n.in(0) != bb ) continue; + // In a backwards walk, proj users come before the node itself + if( n instanceof MultiNode ) + for( Node proj : n.outs() ) + if( proj instanceof ProjNode ) + do_node(alloc,proj); + if( n instanceof MachNode ) + do_node(alloc,n); + } + + // The bb head kills register, e.g. a CallEnd and caller-save registers + if( bb instanceof MachNode mach ) + kills(alloc,mach); + + // Push live-sets backwards to priors in CFG. + if( bb instanceof RegionNode ) + for( int i=1; i lrgs = BBOUTS.computeIfAbsent( bb, k -> new IdentityHashMap<>() ); + + for( LRG lrg : TMP.keySet() ) { + Node def = TMP.get(lrg); + // Effective def comes from phi input from prior block + if( def instanceof PhiNode phi && phi.cfg0()==priorbb ) { + assert i!=0; + def = phi.in( i ); + } + // The synthetic exit carries no value on this Phi arm. + if( def._type==Type.TOP ) continue; + Node def_bb = lrgs.get(lrg); + if( def_bb==null ) { + lrgs.put(lrg,def); + pushWork(bb); + } else { + // Alive twice with different definitions; self-conflict + selfConflict(alloc,def,lrg,def_bb); + } + } + } + + + // ------------------------------------------------------------------------ + // Color the inference graph. + + // Coloring works by removing "trivial" live ranges from the IFG - those + // live ranges with fewer neighbors than available colors (registers). + // These are trivial because even if every neighbor takes a unique color, + // there's at least one more available to color this live range. + + // If we hit a clique of non-trivial live ranges, we pick one to be "at + // risk" of not-coloring. Good choices include live ranges with a large + // area and few hot uses. + + // Then we reverse and put-back live ranges - picking a color as we go. + // If there's no spare color we'll have to spill this at-risk live range. + + public static boolean color(int round, RegAlloc alloc) { + + // Convert the 2-D array of bits (a 1-D array of BitSets) into an + // adjacency matrix. + int maxlrg = alloc._LRGS.length, nlrgs=0; + for( int i=1; i=0; lrg=ifg.nextSetBit(lrg+1) ) { + LRG lrg1 = alloc._LRGS[lrg]; + lrg0.addNeighbor(lrg1); + lrg1.addNeighbor(lrg0); + } + } + } + + // Simplify + + // Walk all the LRGS looking for some trivial ones to start with. + // During this pass all LRGS are broken into 3 disjoint sets: + // - trivial, removed from IFG; color_stack[0 to sptr] + // - trivial, not (yet) removed from IFG; color_stack[sptr to swork] + // - unknown; color_stack[work to maxlrg] + + // Gather all not-unified (not-null); separate trivial and non-trivial set. + int sptr = 0, swork = 0; + LRG[] color_stack = new LRG[nlrgs]; + for( int i=0, j=0; i swork ) + swork = sptr; + + // Walk all neighbors and remove + if( lrg._adj != null ) { + for( LRG nlrg : lrg._adj ) { + // Remove and compress out neighbor + if( nlrg.removeCompress(lrg) ) { + // Neighbor is just exactly going trivial as 'lrg' is removed from IFG + // Find "j" position in the color_stack + int jx = swork; while( color_stack[jx] != nlrg ) jx++; + // Add trivial neighbor to trivial list. Pull lrg j out of + // unknown set, since its now in the trivial set + swap(color_stack, swork++, jx); + } + } + } + } + + // Reverse simplify (unstack the color stack), and set colors (registers) for live ranges + while( sptr > 0 ) { + LRG lrg = color_stack[--sptr]; + if( lrg==null ) continue; + RegMaskRW rmask = lrg._mask.copy(); + // Walk neighbors and remove adjacent colors + if( lrg._adj!=null ) { + for( LRG nlrg : lrg._adj ) { + nlrg.reinsert(lrg); + int reg = nlrg._reg; + if( reg!= -1 ) // Failed neighbors do not count + rmask.clr(reg); // Remove neighbor from my choices + } + } + // At-risk live-range did not color? + if( rmask.isEmpty() ) { + alloc.fail(lrg); + lrg._reg = -1; + } else { + // Pick first available register + short reg = rmask.firstReg(); + // Pick a "good" color from the choices. Typically, biased-coloring + // removes some over-spilling. + if( rmask.size() > 1 ) reg = biasColor(alloc,lrg,reg,rmask); + lrg._reg = reg; // Assign the register + } + } + + return alloc.success(); + } + + // Pick LRG from color stack + private static void pickColor(LRG[] color_stack, int sptr, int swork) { + // Out of trivial colorable, pick an at-risk to pull + if( sptr==swork ) + swap(color_stack,sptr,pickRisky(color_stack,sptr)); + // When coloring, we'd like to give more choices; so when coloring we'd + // like to see the single-def first (since no choices anyway), then + // non-split related (so more live ranges get colored), then + // split-related last, so they have more colors to bias towards. + + // Working in reverse, pick first split-related with many regs, then + // those with some regs, then single-def. + int bidx=sptr; + LRG best=color_stack[bidx]; + for( int idx = sptr+1; idx < swork; idx++ ) + if( betterLRG(best,color_stack[idx]) ) + best = color_stack[bidx=idx]; + if( bidx != sptr ) + swap(color_stack,sptr,bidx); // Pick best at sptr + } + + private static boolean betterLRG( LRG best, LRG lrg ) { + // If single-def varies, keep the not-single-def + if( best.size1() != lrg.size1() ) + return best.size1(); + // If hasSplit varies, keep the hasSplit + if( best.hasSplit() != lrg.hasSplit() ) + return lrg.hasSplit(); + // Keep large register count + return best.size() < lrg.size(); + } + + // Pick a live range that hasn't already spilled, or has a single-def- + // single-use that are not adjacent. + private static int pickRisky( LRG[] color_stack, int sptr ) { + int best=sptr; + int bestScore = pickRiskyScore(color_stack[best]); + for( int i=sptr+1; i bestScore ) + { best = i; bestScore = iScore; } + } + return best; + } + + // Pick a live range to pull, that might not color. + // + // Picking a live range with a very large span, with defs and uses outside + // loops means spilling a relative cheap live range and getting that + // register over a large area. + // + // Picking a live range that is very close to coloring might allow it to + // color despite being risky. + private static int pickRiskyScore( LRG lrg ) { + // Always pick callee-save registers as being very large area recovered + // and very cheap to spill. + if( lrg._machDef instanceof CalleeSaveNode ) + return 999998; + if( lrg._splitDef != null && lrg._splitDef.in(1) instanceof CalleeSaveNode && + lrg._splitUse != null && lrg._splitUse.out(0) instanceof ReturnNode ) + return 999999; + + // TODO: cost/benefit model. Perhaps counting loop-depth (freq) of def/use for cost + // and "area" for benefit + return 1000; + } + + private static short biasColor( RegAlloc alloc, LRG lrg, short reg, RegMask mask ) { + if( mask.size1() ) return reg; + // Check chain of splits up the def-chain. Take first allocated + // register, and if it's available in the mask, take it. + Node defSplit = lrg._splitDef, useSplit = lrg._splitUse; + int tidx, cnt=0; + + while( (tidx=biasable(defSplit)) != 0 || biasable(useSplit) != 0 ) { + if( cnt++ > 10 ) break; + + if( tidx != 0 ) { + short bias = biasColor( alloc, defSplit, mask ); + if( bias >= 0 ) return bias; // Good bias + if( bias == -2 ) defSplit = null; // Kill this side, no more searching + } else defSplit = null; + + if( biasable(useSplit) != 0 ) { + short bias = biasColor( alloc, useSplit, mask ); + if( bias >= 0 ) return bias; // Good bias + if( bias == -2 ) useSplit = null; // Kill this side, no more searching + } else useSplit = null; + + if( defSplit != null ) { + short bias = biasColorNeighbors( alloc, defSplit, mask ); + if( bias >= 0 ) return bias; + // Advance def side + defSplit = defSplit.in(tidx); + if( alloc.lrg(defSplit)==null ) defSplit=null; + } + + if( useSplit != null ) { + short bias = biasColorNeighbors( alloc, useSplit, mask ); + if( bias >= 0 ) return bias; + useSplit = useSplit.out(0); + } + + } + return mask.firstReg(); + } + + private static int biasable(Node split) { + if( split instanceof SplitNode ) return 1; // Yes biasable, advance is slot 1 + if( split instanceof PhiNode ) return 1; // Yes biasable, advance is slot 1 + if( !(split instanceof MachNode mach) ) return 0; // Not biasable + return mach.twoAddress(); // Only biasable if 2-addr + } + + // 3-way return: + // - good bias reg, take it & exit + // - this path is cutoff; do not search here anymore + // - advance this side + private static short biasColor( RegAlloc alloc, Node split, RegMask mask ) { + short bias = alloc.lrg(split)._reg; + if( bias != -1 ) { + if( mask.test(bias) ) return bias; // Good bias + else return -2; // Kill this side + } else return -1; // Advance this side + } + + // Check if we can match "split" color, or else trim "mask" to colors + // "split" might get. + private static short biasColorNeighbors( RegAlloc alloc, Node split, RegMask mask ) { + LRG slrg = alloc.lrg(split); + if( slrg._adj == null ) return -1; // No trimming + + // Can I limit my own choices to valid neighbor choices? + for( LRG alrg : slrg._adj ) { + int reg = alrg._reg; + if( reg == -1 && alrg._mask.size1() ) + reg = alrg._mask.firstReg(); + if( reg != -1 ) { + mask.clr(reg); + if( mask.size1() ) + return mask.firstReg(); + } + } + return -1; // No obvious color choice, but mask got trimmed + } + + private static void swap( LRG[] ary, int x, int y ) { + LRG tmp = ary[x]; ary[x] = ary[y]; ary[y] = tmp; + } +} diff --git a/src/main/java/com/seaofnodes/simple/codegen/LRG.java b/src/main/java/com/seaofnodes/simple/codegen/LRG.java new file mode 100644 index 00000000..5803cad8 --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/codegen/LRG.java @@ -0,0 +1,219 @@ +package com.seaofnodes.simple.codegen; + +import com.seaofnodes.simple.Ary; +import com.seaofnodes.simple.SB; +import com.seaofnodes.simple.node.*; + +import java.util.IdentityHashMap; + +/** A Live Range + *

+ * A live range is a set of nodes and edges which must get the same register. + * Live ranges form an interconnected web with almost no limits on their + * shape. Live ranges also gather the set of register constraints from all + * their parts. + *

+ * Most of the fields in this class are for spill heuristics, but there are a + * few key ones that define what a LRG is. LRGs have a unique dense integer + * `_lrg` number which names the LRG. New `_lrg` numbers come from the + * `RegAlloc._lrg_num` counter. LRGs can be unioned together - + * (this is the Union-Find algorithm) + * - and when this happens the lower numbered `_lrg` wins. Unioning only + * happens during `BuildLRG` and happens because either a `Phi` is forcing all + * its inputs and outputs into the same register, or because of a 2-address + * instruction. LRGs have matching `union` and `find` calls, and a set + * `_leader` field. + */ +public class LRG { + + // Dense live range numbers + final short _lrg; + + // U-F leader; null if leader + LRG _leader; + + // Choosen register + short _reg; + + // Count of single-register defs and uses + short _1regDefCnt, _1regUseCnt; + + // A sample MachNode def in the live range + public MachNode _machDef, _machUse; + short _uidx; // _machUse input + + // Some splits used in biased coloring + MachConcreteNode _splitDef, _splitUse; + + // All the self-conflicting defs for this live range + IdentityHashMap _selfConflicts; + + // AND of all masks involved; null if none have been applied yet + RegMask _mask; + + // Adjacent Live Range neighbors. Only valid during coloring + Ary _adj; + + void addNeighbor(LRG lrg) { + if( _adj==null ) _adj = new Ary<>(LRG.class); + _adj.push(lrg); + } + + // Remove and compress neighbor list; store ex-neighbor past + // end for unwinding Simplify. True if exactly going low-degree + boolean removeCompress( LRG lrg ) { + _adj.swap(_adj.find(lrg),_adj._len-1); + return _adj._len-- == _mask.size(); + } + + void reinsert( LRG lrg ) { + assert _adj._es[_adj._len]==lrg; + _adj._len++; + } + + // More registers than neighbors + boolean lowDegree() { return (_adj==null ? 0 : _adj._len) < _mask.size(); } + + LRG( short lrg ) { _lrg = lrg; _reg = -1; } + + boolean leader() { return _leader == null; } + + // Fast-path Find from the Union-Find algorithm + LRG find() { + if( _leader==null ) return this; // I am the leader + if( _leader._leader==null ) // I point to the leader + return _leader; + return _rollup(); + } + // Slow-path rollup of U-F + private LRG _rollup() { + LRG ldr = _leader._leader; + // Roll-up + while( ldr._leader!=null ) ldr = ldr._leader; + LRG l2 = this; + while( l2 != ldr ) { + LRG l3 = l2._leader; + l2._leader = ldr; + l2 = l3; + } + return ldr; + } + + // Union `this` and `lrg`, keeping the lower numbered _lrg. + // Includes a number of fast-path cutouts. + LRG union( LRG lrg ) { + assert leader(); + if( lrg==null ) return this; + lrg = lrg.find(); + if( lrg==this ) return this; + return _lrg < lrg._lrg ? _union(lrg) : lrg._union(this); + } + // Union `this` and `lrg`, folding together all stats. + private LRG _union( LRG lrg ) { + // Set U-F leader + lrg._leader = this; + // Fold together stats + if( _machDef==null ) { + _machDef = lrg._machDef; + } else if( lrg._machDef!=null ) { + if( _1regDefCnt==0 ) + _machDef = lrg._machDef; + else if( _machDef==lrg._machDef ) + _1regDefCnt--; + } + _1regDefCnt += lrg._1regDefCnt; + + if( _machUse==null ) { + _machUse = lrg._machUse; + _uidx = lrg._uidx; + } else if( lrg._machUse!=null ) { + if( _1regUseCnt==0 ) { + _machUse = lrg._machUse; + _uidx = lrg._uidx; + } + else if( _machUse==lrg._machUse ) + _1regUseCnt--; + } + _1regUseCnt += lrg._1regUseCnt; + + // Fold deepest Split + _splitDef = deepSplit(_splitDef,lrg._splitDef); + _splitUse = deepSplit(_splitUse,lrg._splitUse); + + // Fold together masks + RegMask mask = _mask.and(lrg._mask); + _mask = mask==null ? _mask.copy().and(lrg._mask) : mask; + return this; + } + + private static MachConcreteNode deepSplit( MachConcreteNode s0, MachConcreteNode s1 ) { + return s0==null || (s1!=null && s0.cfg0().loopDepth() < s1.cfg0().loopDepth()) ? s1 : s0; + } + + // Record any Mach def for spilling heuristics + LRG machDef( MachNode def, boolean size1 ) { + if( _machDef==null || size1 ) + _machDef = def; + if( size1 ) + _1regDefCnt++; + if( def instanceof SplitNode split ) + _splitDef = deepSplit(_splitDef,split); + return this; + } + + // Record any Mach use for spilling heuristics + LRG machUse( MachNode use, short uidx, boolean size1 ) { + if( _machUse==null || size1 ) + { _machUse = use; _uidx = uidx; } + if( size1 ) + _1regUseCnt++; + if( use instanceof SplitNode split ) + _splitUse = deepSplit(_splitUse,split); + return this; + } + + boolean hasSplit() { return _splitDef != null || _splitUse != null; } + short size() { return _mask.size(); } + boolean size1() { return _mask.size1(); } + + + void selfConflict( Node def ) { + if( _selfConflicts == null ) _selfConflicts = new IdentityHashMap<>(); + _selfConflicts.put(def,""); + } + + + // Record intersection of all register masks. + // True if still has registers + boolean and( RegMask mask ) { + RegMask mask2 = mask.and(_mask); + if( mask2==null ) + mask2 = _mask.copy().and(mask); + _mask = mask2; + return !_mask.isEmpty(); + } + // Remove this singular register + // True if still has registers + boolean clr( int reg ) { + if( _mask.clr(reg) ) return true; + _mask = _mask.copy(); // Need a mutable copy + return _mask.clr(reg); + } + // Subtract mask (AND complement) + // True if still has registers + boolean sub( RegMask mask ) { + RegMask mask2 = _mask.sub(mask); + if( mask2==null ) + mask2 = _mask.copy().sub(mask); + _mask = mask2; + return !_mask.isEmpty(); + } + + @Override public String toString() { return toString(new SB()).toString(); } + + public SB toString( SB sb ) { + sb.p("V").p(_lrg); + if( _mask!=null ) _mask.toString(sb); + return sb; + } +} diff --git a/src/main/java/com/seaofnodes/simple/codegen/ListScheduler.java b/src/main/java/com/seaofnodes/simple/codegen/ListScheduler.java new file mode 100644 index 00000000..1796ef7c --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/codegen/ListScheduler.java @@ -0,0 +1,258 @@ +package com.seaofnodes.simple.codegen; + +import com.seaofnodes.simple.Ary; +import com.seaofnodes.simple.IterPeeps.WorkList; +import com.seaofnodes.simple.Utils; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.type.*; +import java.util.*; + +public abstract class ListScheduler { + + // eXtra per-node stuff for scheduling. + // Nodes are all block-local, and projections fold into their parent. + private static final IdentityHashMap XS = new IdentityHashMap<>(); + private static class XSched { + + 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 + + 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); } + + static XSched get(Node n) { return n==null ? null : XS.get(n instanceof ProjNode && !(n.in(0) instanceof CallEndNode) ? n.in(0) : n); } + + private XSched init(CFGNode bb, Node n) { + _n = n; + _bcnt = 0; + _ruse = _rdef = _single = false; + + // Count block-locals and remote inputs + if( !(n instanceof PhiNode) ) + for( Node def : n._inputs ) + if( def != null && def != bb ) { + if( def.cfg0()==bb ) _bcnt++; // Raise block-local input count + else if( !def.isMem() ) _ruse = true; // Remote register user + } + // Count remote outputs and allowed registers + if( n instanceof MultiNode ) + for( Node use : n._outputs ) + computeSingleRDef(bb,use); + else + computeSingleRDef(bb,n); + return this; + } + + private void computeSingleRDef(CFGNode bb, Node n) { + // See if this is 2-input, and that input is single-def + if( n instanceof MachNode mach && mach.twoAddress() != 0 ) { + XSched xs = XS.get(n.in(mach.twoAddress())); + if( xs != null ) + _single = xs._single; + } + // Visit all outputs + RegMaskRW rmask = !_single && n instanceof MachNode mach && mach.outregmap() != null ? mach.outregmap().copy() : null; + for( Node use : n._outputs ) { + // Remote use, so this is a remote def + if( use!=null && use.cfg0()!=bb ) _rdef = true; + // And mask between n's def mask and all n's users masks' + if( use instanceof MachNode mach && rmask != null ) + for( int i=1; i 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(); + } + } + + + // List schedule every block + public static void sched( CodeGen code ) { + XS.clear(); + for( CFGNode bb : code._cfg ) + if( bb.blockHead() ) + local(bb,false); + } + + // Classic list scheduler + private static void local( CFGNode bb, boolean trace ) { + assert XS.isEmpty(); + int len = bb.nOuts(); + + // Count block-locals, remote inputs and outputs + 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 bcnts[]. Once + // that gets to 0, all the nodes using it are scheduled, so it's ready. + int sched = 0; + while( sched < len ) { + assert sched < ready; + int pick = best(bb._outputs,sched,ready,trace); // 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 ) + for( Node useuse : use._outputs ) + ready = ready(bb,useuse,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) && 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( Ary blk, int sched, int ready, boolean trace ) { + int pick = sched; + Node p = blk.at(pick); + int score = score(p); + if( trace ) { System.out.printf("%4d N%d ",score,p._nid); System.out.println(p); } + for( int i=sched+1; i score ) + { score = nscore; pick = i; p = n; } + } + if( trace ) { System.out.printf("Pick: N%d %s\n\n",p._nid,p); } + return pick; + } + + // Highest score wins. Max at 1000, min at 10, except specials. + 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 (delay) 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 defining a remote value, just generically stall alot. Value is + // used in a later block, can we delay until the end of this block? + if( xn._rdef ) score = 200; // If defining a remote value, just generically stall alot + boolean flags = false; + if( n instanceof MultiNode ) { + for( Node use : n._outputs ) + flags |= singleUseNotReady( use, xn._single ); + } else + flags |= singleUseNotReady( n, xn._single ); + score += -10 * Math.min( CNT[1], 2 ); + score += -100 * Math.min( CNT[2], 2 ); + if( flags ) return 10; + + // 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; i1 && n instanceof MachNode mach && mach.isClone() ) + return false; + for( Node use : n.outs() ) { + XSched xu = XSched.get(use); + if( xu != null && xu._n instanceof CFGNode ) return true; + if( xu != null && xu._bcnt > 0 ) + CNT[single ? 2 : 1]++; // Since bcnt>0 or CFG, stall until user is more ready + } + return false; + } + +} diff --git a/src/main/java/com/seaofnodes/simple/codegen/Machine.java b/src/main/java/com/seaofnodes/simple/codegen/Machine.java new file mode 100644 index 00000000..13d7abc1 --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/codegen/Machine.java @@ -0,0 +1,22 @@ +package com.seaofnodes.simple.codegen; + +import com.seaofnodes.simple.node.*; + +abstract public class Machine { + // Human readable machine name. Something like "x86-64" or "arm" or "risc5" + public abstract String name(); + // Human-readable name for a register number, e.g. "RAX" or "R0" + public abstract String reg( int reg ); + // Create a split op; any register to any register, including stack slots + public abstract SplitNode split( String kind, byte round, LRG lrg); + // List of caller-save registers + public abstract RegMask callerSave(); + // List of callee-save registers + public abstract RegMask calleeSave(); + // Return a MachNode unconditional branch + public abstract CFGNode jump(); + // Break an infinite loop + public abstract IfNode never( CFGNode ctrl ); + // Instruction select from ideal nodes + public abstract Node instSelect( Node n ); +} diff --git a/src/main/java/com/seaofnodes/simple/codegen/RegAlloc.java b/src/main/java/com/seaofnodes/simple/codegen/RegAlloc.java new file mode 100644 index 00000000..af28cbb3 --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/codegen/RegAlloc.java @@ -0,0 +1,557 @@ +package com.seaofnodes.simple.codegen; + +import com.seaofnodes.simple.Ary; +import com.seaofnodes.simple.Utils; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.type.Type; +import java.util.Arrays; +import java.util.IdentityHashMap; + +/** + * "Briggs/Chaitin/Click". + * Graph coloring. + *

+ * Every def and every use have a bit-set of allowed registers. + * Fully general to bizarre chips. + * Multiple outputs fully supported, e.g. add-with-carry or combined div/rem or "xor rax,rax" setting both rax and flags. + *

+ * Intel 2-address accumulator-style ops fully supported. + * Op-to-stack-spill supported. + * All addressing modes supported. + * Register pairs could be supported with some grief. + *

+ * Splitting instead of spilling (simpler implementation). + * Stack slots are "just another register" (tiny stack frames, simpler implementation). + * Stack/unstack during coloring with a marvelous trick (simpler implementation). + *

+ * Both bitset and adjacency list formats for the interference graph; one of + * the few times it's faster to change data structures mid-flight rather than + * just wrap one of the two. + *

+ * Liveness computation and interference graph built in the same one pass (one + * fewer passes per round of coloring). + *

+ * Single-register def or use live ranges deny neighbors their required + * register and thus do not interfere, vs interfering and denying the color + * and coloring time. Basically all function calls do this, but many oddball + * registers also, e.g. older div/mod/mul ops. (5x smaller IFG, the only + * O(n^2) part of this operation). + */ + +public class RegAlloc { + // Main Coloring Algorithm: + // Repeat until colored: + // Build Live Ranges (LRGs) + // - Intersect allowed registers + // If hard conflicts (LRGs with no allowed registers) + // - Pre-split conflicted LRGs, and repeat + // Build Interference Graph (IFG) + // - Self conflicts split now + // Color (remove trivial first then any until empty; reverse assign colors + // - If color fails: + // - - Split uncolorable LRGs + + // Top-level program graph structure + final CodeGen _code; + + public int _spills, _spillScaled; + + // ----------------------- + // Live ranges with self-conflicts or no allowed registers + private final IdentityHashMap _failed = new IdentityHashMap<>(); + void fail( LRG lrg ) { + assert lrg.leader(); + _failed.put(lrg,""); + } + boolean success() { return _failed.isEmpty(); } + + + // ----------------------- + // Map from Nodes to Live Ranges + private final IdentityHashMap _lrgs = new IdentityHashMap<>(); + short _lrg_num; + + // Define a new LRG, and assign n + LRG newLRG( Node n ) { + LRG lrg = lrg(n); + if( lrg!=null ) return lrg; + lrg = new LRG(_lrg_num++); + LRG old = _lrgs.put(n,lrg); assert old==null; + return lrg; + } + + // LRG for n + LRG lrg( Node n ) { + LRG lrg = _lrgs.get(n); + if( lrg==null ) return null; + LRG lrg2 = lrg.find(); + if( lrg != lrg2 ) + _lrgs.put(n,lrg2); + return lrg2; + } + + // Find LRG for n.in(idx), and also map n to it + LRG lrg2( Node n, int idx ) { + LRG lrg = lrg(n.in(idx)); + return union(lrg,n); + } + + // Union any lrg for n with lrg and map to the union + LRG union( LRG lrg, Node n ) { + LRG lrgn = _lrgs.get(n); + LRG lrg3 = lrg.union(lrgn); + _lrgs.put(n,lrg3); + return lrg3; + } + + // Force all unified to roll up; collect live ranges + LRG[] _LRGS; + void unify() { + Ary lrgs = new Ary<>(LRG.class); + for( Node n : _lrgs.keySet() ) { + LRG lrg = lrg(n); + lrgs.setX(lrg._lrg,lrg); + } + _LRGS = lrgs.asAry(); + // Remove unified lrgs from failed set also + for( LRG lrg : _failed.keySet().toArray(new LRG[0]) ) { + if( !lrg.leader() ) { + LRG lrg2 = lrg.find(); + _failed.remove(lrg); + _failed.put(lrg2,""); + } + } + } + + + // Diagnostic lookup: do not compress leaders or rewrite the node mapping. + private LRG _lrg(Node n) { + LRG lrg = _lrgs.get(n); + if( lrg != null ) + while( lrg._leader != null ) lrg = lrg._leader; + return lrg; + } + + public short regnum( Node n ) { + LRG lrg = _lrg(n); + return lrg==null ? -1 : lrg._reg; + } + + // Printable register number for node n + String reg( Node n ) { + LRG lrg = _lrg(n); + if( lrg==null ) return null; + if( lrg._reg == -1 ) return "V"+lrg._lrg; + return _code._mach.reg(lrg._reg); + } + + // ----------------------- + RegAlloc( CodeGen code ) { _code = code; } + + public void regAlloc() { + // Insert callee-save registers + for( CFGNode bb : _code._cfg ) + if( bb instanceof FunNode fun ) + insertCalleeSave(fun); + + // Optional diagnostic for values accidentally shared across functions. + //assert verifyFunctionLocalEdges(); + + // Top driver: repeated rounds of coloring and splitting. + byte round=0; + while( !graphColor(round) ) { + split(round); + assert round < 7 : "Register allocation made no progress after eight rounds"; + round++; + } + postColor(); // Remove no-op spills + } + + boolean verifyFunctionLocalEdges() { + for( CFGNode bb : _code._cfg ) { + if( bb instanceof StartNode ) continue; + FunNode fun = bb.fun(); + for( Node use : bb._outputs ) + if( use instanceof MachNode mach && !(use instanceof ParmNode) ) + for( int i=1; i "+use.uniqueName()+" input "+i; + } + } + return true; + } + + private boolean graphColor(byte round) { + _failed.clear(); + _lrgs.clear(); + _lrg_num = 1; + _LRGS=null; + + return + // Build Live Ranges + BuildLRG.run(round,this) && // if no hard register conflicts + // Build Interference Graph + IFG.build(round,this) && // If no self conflicts or uncolorable + // Color attempt + IFG.color(round,this); // If colorable + } + + // Insert callee-save registers. Walk the callee-save RegMask ignoring any + // Parms, then insert a Parm and an edge from the Ret to the Parm with the + // callee-save register. + private void insertCalleeSave( FunNode fun ) { + RegMask saves = _code._mach.calleeSave(); + ReturnNode ret = fun.ret(); + + for( short reg = saves.firstReg(); reg != -1; reg = saves.nextReg(reg) ) { + ret.addDef(new CalleeSaveNode(fun,reg,_code._mach.reg(reg))); + assert ((MachNode)ret).regmap(ret.nIns()-1).firstReg()==reg; + } + } + + + // ----------------------- + // Split conflicted live ranges. + void split(byte round) { + + // In C2, all splits are handling in one pass over the program. Here, + // in the name of clarity, we'll handle each failing live range + // independently... which generally requires a full pass over the + // program for each failing live range. i.e., might be a lot of + // passes. + LRG[] splits = _failed.keySet().toArray(new LRG[0]); + Arrays.sort(splits, (x,y) -> x._lrg - y._lrg); + for( LRG lrg : splits ) split(round,lrg); + } + + // Split this live range + boolean split( byte round, LRG lrg ) { + assert lrg.leader(); // Already rolled up + + if( lrg._selfConflicts != null ) + return splitSelfConflict(round,lrg); + + // Register mask when empty; split around defs and uses with limited + // register masks. + if( lrg._mask.isEmpty() ) { + if( lrg._1regDefCnt <= 1 && + lrg._1regUseCnt <= 1 && + (lrg._1regDefCnt + lrg._1regUseCnt) > 0 && + // A fixed-register clone with flexible uses must split at the uses. + !(lrg._1regDefCnt==1 && lrg._machDef.isClone() && lrg._1regUseCnt==0) ) + return splitEmptyMaskSimple(round,lrg); + // More complex conflicts use loop-boundary splitting. + } + + // Generic split-by-loop depth. + return splitByLoop(round,lrg); + } + + // Split live range with an empty mask. Specifically forces splits at + // single-register defs or uses and not elsewhere. + boolean splitEmptyMaskSimple( byte round, LRG lrg ) { + // Live range has a single-def single-register, and/or a single-use + // single-register. Split after the def and before the use. Does not + // require a full pass. + + // Split just after def + if( lrg._1regDefCnt==1 && !lrg._machDef.isClone() ) + // An earlier copy is reusable only if no intervening clobber. + // Being in the same block alone is insufficient. Example: + // alloc + // V1/rax - forced by alloc + // alloc + // V2/rax - kills prior RAX + // st4 [V1],len - No good, must split around + insertAfterAndReplace(makeSplit("def/empty1",round,lrg),(Node)lrg._machDef,false); + // Split just before use + if( lrg._1regUseCnt==1 ) + insertBefore((Node)lrg._machUse,lrg._uidx,"use/empty1",round,lrg); + return true; + } + + // Self conflicts require Phis (or two-address). + // Insert a split after every def. + boolean splitSelfConflict( byte round, LRG lrg ) { + // Sort conflict set, so we're deterministic + Node[] conflicts = lrg._selfConflicts.keySet().toArray(new Node[0]); + Arrays.sort(conflicts, (x,y) -> x._nid - y._nid ); + + // For all conflicts + for( Node def : conflicts ) { + assert lrg(def)==lrg; // Might be conflict use-side + // Split before each use that extends the live range; i.e. is a + // Phi or two-address + for( int i=0; i loop.idepth() ) ) || + (use instanceof MachNode mach && mach.twoAddress()!=0 && use.in(mach.twoAddress())==def) ) + insertBefore( use, use._inputs.find(def), "use/self/use",round,lrg ); + } + // Split after the Phi and before its entry. Also isolate inputs + // shared with another Phi: otherwise the shared input keeps the + // conflicting live ranges joined, even after splitting the entry. + if( def instanceof PhiNode phi && !(def instanceof ParmNode) ) { + SplitNode split = makeSplit("def/self",round,lrg); + insertAfterAndReplace(split,def,false); + if( split.nOuts()==0 ) + split.killOrdered(); + for( int i=1; i>32); + + + // If the minLoopDepth is less than the maxLoopDepth: for-all defs and + // uses, if at minLoopDepth or lower, split after def and before use. + for( Node n : _ns ) { + if( n instanceof SplitNode ) continue; // Ignoring splits; since spilling need to split in a deeper loop + if( n.isDead() ) continue; // Some Clonable went dead by other spill changes + // If this is a 2-address commutable op (e.g. AddX86, MulX86) and the rhs has only a single user, + // commute the inputs... which chops the LHS live ranges' upper bound to just the RHS. + if( n instanceof MachNode mach && lrg(n)==lrg && mach.twoAddress()==1 && mach.commutes() && n.in(2).nOuts()==1 ) + n.swap12(); + + if( lrg(n)==lrg && // This is a LRG def + // At loop boundary, or splitting in inner loop + (min==max || n.cfg0().loopDepth() <= min) ) { + // Cloneable constants will be cloned at uses, not after def + if( !(n instanceof MachNode mach && mach.isClone()) && + // Single user is already a split, with no intervening clobber + !(n.nOuts()==1 && n.out(0) instanceof SplitNode split && sameBlockNoClobber(split)) ) + // Split after def in min loop nest + insertAfterAndReplace(makeSplit("def/loop",round,lrg),n,true); + } + + // PhiNodes check all CFG inputs + if( n instanceof PhiNode phi && !(n instanceof ParmNode)) { + for( int i=1; i>32); + int d = cfg.loopDepth(); + min = Math.min(min,d); + max = Math.max(max,d); + return ((long)max<<32) | min; + } + + // Find all members of a live range, both defs and uses + private final Ary _ns = new Ary<>(Node.class); + void findAllLRG( LRG lrg ) { + _ns.clear(); + int wd = 0; + if( lrg._machDef!=null ) _ns.push((Node)lrg._machDef); + if( lrg._machUse!=null ) _ns.push((Node)lrg._machUse); + while( wd < _ns._len ) { + Node n = _ns.at(wd++); + if( lrg(n)!=lrg ) continue; + for( Node def : n._inputs ) + if( lrg(def)==lrg && _ns.find(def)== -1 ) + _ns.push(def); + for( Node use : n._outputs ) + if( _ns.find(use)== -1 ) + _ns.push(use); + } + for( Node n : _ns ) assert !n.isDead(); + } + + void insertBefore(Node n, int i, String kind, byte round, LRG lrg) { insertBefore(n,i,kind,round,lrg,true); } + void insertBefore(Node n, int i, String kind, byte round, LRG lrg, boolean skip) { + Node def = n.in(i); + // Phi splitting may visit the synthetic never-exit arm. + if( def._type==Type.TOP ) return; + // Effective block for use + CFGNode cfg = n instanceof PhiNode phi ? phi.region().cfg(i) : n.cfg0(); + // Def is a split ? + if( skip && def instanceof SplitNode ) { + boolean singleReg = n instanceof MachNode mach && mach.regmap(i)!=null && mach.regmap(i).size1(); + // Same block, multiple registers, split is only used by n, + // assume this is good enough and do not split again. + if( cfg==def.cfg0() && def.nOuts()==1 && !singleReg ) + return; + } + RegMask use = n instanceof MachNode mach ? mach.regmap(i) : null; + makeSplit(def,kind,round,lrg,use).insertBefore(n, i); + // Skip split-of-split same block + if( skip && def instanceof SplitNode && cfg==def.cfg0() ) + n.in(i).setDefOrdered(1,def.in(1)); + } + + // Replace uses of `def` with `split`, and insert `split` immediately after + // `def` in the basic block. + public void insertAfterAndReplace( Node split, Node def, boolean must ) { + split.insertAfter(def); + if( split.nIns()>1 ) split.setDef(1,def); + for( int j=def.nOuts()-1; j>=0; j-- ) { + Node use = def.out(j); + if( use==split ) continue; // Skip self + // Can we avoid a split of a split? 'this' split is used by + // another split in the same block. + if( !must && use instanceof SplitNode split2 && sameBlockNoClobber(split2) ) + continue; + int idx = use._inputs.find(def); + use.setDefOrdered(idx,split); + if( j < def.nOuts() ) j++; + } + } + + private Node makeSplit( Node def, String kind, byte round, LRG lrg, RegMask use ) { + // A clone must be able to satisfy the use's register class. + Node split = def instanceof MachNode mach && mach.isClone() && (use==null || mach.outregmap().overlap(use)) + ? mach.copy() + : _code._mach.split(kind,round,lrg); + _lrgs.put(split,lrg); + return split; + } + private SplitNode makeSplit( String kind, byte round, LRG lrg ) { + SplitNode split = _code._mach.split(kind,round,lrg); + _lrgs.put(split,lrg); + return split; + } + + + // ----------------------- + // POST PASS: Remove empty spills that biased-coloring made + private void postColor() { + for( CFGNode bb : _code._cfg ) { // For all ops + for( int j=0; j= 0; + } + } + } + + private boolean splitBypass( CFGNode bb, int j, Node lo, int defreg ) { + // Attempt to bypass split + Node hi = lo.in(1); + while( true ) { + if( !(hi instanceof SplitNode && lo.cfg0() == hi.cfg0()) ) + return false; + if( lrg(hi.in(1))._reg==defreg ) + break; + hi = hi.in(1); + } + // Walk backward from the later copy, including instructions without results. + for( int idx = j-1; bb.out(idx) != hi; idx--) { + Node n = bb.out(idx); + if( clobbers(n,defreg) ) + return false; // Clobbered + } + lo.setDefOrdered(1,hi.in(1)); + return true; + } + private boolean clobbers(Node n, int reg) { + LRG lrg = lrg(n); + return lrg!=null && (lrg._reg==reg || lrg._reg==-1 && lrg._mask.size1() && lrg._mask.firstReg()==reg) || + n instanceof MachNode mach && mach.killmap()!=null && mach.killmap().test(reg); + } + + public boolean sameBlockNoClobber( SplitNode split ) { + Node def = split.in(1); + CFGNode cfg = def.cfg0(); + if( cfg != split.cfg0() ) return false; // Not same block + // Get multinode head + Node def0 = def instanceof ProjNode ? def.in(0) : def; + int defreg = lrg(def)._reg; + if( defreg == -1 ) defreg = lrg(def)._mask.firstReg(); + if( defreg == -1 ) return false; // no allowed registers -> clobbered + for( int idx = cfg._outputs.find(split) -1; idx >= 0; idx-- ) { + Node n = cfg.out(idx); + if( n==def0 ) return true; // No clobbers + if( lrg(n) == lrg(def) ) return false; // Self conflict + if( clobbers(n,defreg) ) + return false; // Clobbered + } + throw Utils.TODO(); + } + +} diff --git a/src/main/java/com/seaofnodes/simple/codegen/RegMask.java b/src/main/java/com/seaofnodes/simple/codegen/RegMask.java new file mode 100644 index 00000000..5227c11e --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/codegen/RegMask.java @@ -0,0 +1,139 @@ +package com.seaofnodes.simple.codegen; + +import com.seaofnodes.simple.Utils; +import com.seaofnodes.simple.SB; + +/** RegMask + * A "register mask" - 1 bit set for each allowed register. In addition + * "stack slot" registers may be allowed, effectively making the set infinite. + *

+ * For smaller and simpler machines it suffices to make such masks an i64 or + * i128 (64 or 128 bit integers), and this presentation is by far the better + * way to go... if all register allocations can fit in this bit limitation. + * The allocator will need bits for stack-based parameters and for splits + * which cannot get a register. For a 32-register machine like the X86, add 1 + * for flags - gives 33 registers. Using a Java `long` has 64 bits, leaving + * 31 for spills and stack passing. This is adequate for nearly all + * allocations; only the largest allocations will run this out. However, if + * we move to a chip with 64 registers we'll immediately run out, and need at + * least a 128 bit mask. Since you cannot *return* a 128 bit value directly + * in Java, Simple will pick up a `RegMask` class object. +*/ +public class RegMask { + + long _bits0, _bits1; + + private static final RegMask EMPTY = new RegMask(0L); + public static final RegMask FULL = new RegMask(-1L); + + public RegMask(int bit) { + if( bit < 64 ) _bits0 = 1L<> reg)&1)==0; + if( reg < 128 ) return ((_bits1 >> reg)&1)==0; + return true; + } + + + public short firstReg() { + if( isEmpty() ) return -1; + return (short)(_bits0 != 0 + ? Long.numberOfTrailingZeros(_bits0) + : Long.numberOfTrailingZeros(_bits1)+64); + } + public short nextReg(short reg) { + if( reg < 63 ) { + long bits0 = _bits0 & (-1L << (reg+1)); + if( bits0!=0 ) return (short)Long.numberOfTrailingZeros(bits0); + } + long bits1 = reg < 64 ? _bits1 : reg < 127 ? _bits1 & (-1L << (reg-63)) : 0; + return bits1==0 ? -1 : (short)(64+Long.numberOfTrailingZeros(bits1)); + } + + boolean isEmpty() { return _bits0==0 && _bits1==0; } + + boolean test( int reg ) { + return ((reg<64 ? (_bits0 >> reg) : (_bits1 >> (reg-64))) & 1) != 0; + } + + // checks if the 2 masks have at least 1 bit in common + public boolean overlap( RegMask mask ) { return (_bits0 & mask._bits0)!=0 || (_bits1 & mask._bits1)!=0; } + + // Defensive writable copy + public RegMaskRW copy() { return new RegMaskRW( _bits0, _bits1 ); } + + // Has exactly 1 bit set + public boolean size1() { + return !isEmpty() && (((_bits0 & -_bits0)==_bits0 && _bits1==0) || + ((_bits1 & -_bits1)==_bits1 && _bits0==0)); + } + + // Cardinality + public short size() { return (short)(Long.bitCount(_bits0)+Long.bitCount(_bits1)); } + + @Override public String toString() { return toString(new SB()).toString(); } + public SB toString(SB sb) { + Machine mach = CodeGen.CODE._mach; + if( _bits0==0 && _bits1==0 ) return sb.p("[]"); + sb.p("["); + for( int i=0; i<64; i++ ) + if( ((_bits0 >> i)&1) != 0 ) + sb.p(mach.reg(i)).p(","); + for( int i=0; i<64; i++ ) + if( ((_bits1 >> i)&1) != 0 ) + sb.p(mach.reg(i+64)).p(","); + return sb.unchar().p("]"); + } +} + +// Mutable regmask(writable) +class RegMaskRW extends RegMask { + public RegMaskRW(long x, long y) { super(x,y); } + // clears bit at position r. Returns true if the mask is still not empty. + public boolean clr(int r) { + if( r < 64 ) _bits0 &= ~(1L<<(r )); + else _bits1 &= ~(1L<<(r-64)); + return _bits0!=0 || _bits1!=0; + } + @Override RegMaskRW and( RegMask mask ) { + if( mask==null ) return this; + _bits0 &= mask._bits0; + _bits1 &= mask._bits1; + return this; + } + @Override RegMaskRW sub( RegMask mask ) { + if( mask==null ) return this; + _bits0 &= ~mask._bits0; + _bits1 &= ~mask._bits1; + return this; + } +} diff --git a/src/main/java/com/seaofnodes/simple/node/AddNode.java b/src/main/java/com/seaofnodes/simple/node/AddNode.java index 2dfedbb5..0fc3bad6 100644 --- a/src/main/java/com/seaofnodes/simple/node/AddNode.java +++ b/src/main/java/com/seaofnodes/simple/node/AddNode.java @@ -4,6 +4,9 @@ package com.seaofnodes.simple.node; import com.seaofnodes.simple.Parser; import com.seaofnodes.simple.type.*; + +import static com.seaofnodes.simple.Parser.con; + public class AddNode extends Node { public AddNode(Node lhs, Node rhs) { super(null, lhs, rhs); } @@ -37,7 +40,8 @@ public class AddNode extends Node { public Node idealize () { Node lhs = in(1); Node rhs = in(2); - if( rhs.err()!=null ) return null; + if( rhs instanceof AddNode add && add.err()!=null ) + return null; Type t2 = rhs._type; // Add of 0. We do not check for (0+x) because this will already diff --git a/src/main/java/com/seaofnodes/simple/node/AndNode.java b/src/main/java/com/seaofnodes/simple/node/AndNode.java index 7cb16c71..b425a6ce 100644 --- a/src/main/java/com/seaofnodes/simple/node/AndNode.java +++ b/src/main/java/com/seaofnodes/simple/node/AndNode.java @@ -34,14 +34,17 @@ public class AndNode extends LogicalNode { Node rhs = in(2); Type t1 = lhs._type; Type t2 = rhs._type; + if( !(t1 instanceof TypeInteger && t2 instanceof TypeInteger t2i) ) + return null; // Malformed, e.g. (17 & 3.14) // And of -1. We do not check for (-1&x) because this will already - // canonicalize to (x&-1) - if( t2.isConstant() && t2 instanceof TypeInteger i && i.value()==-1 && lhs._type instanceof TypeInteger ) + // canonicalize to (x&-1). We do not check for zero, because + // the compute() call will return a zero already. + if( t2i.isConstant() && t2i.value()==-1 ) return lhs; // Move constants to RHS: con*arg becomes arg*con - if ( t1.isConstant() && !t2.isConstant() ) + if( t1.isConstant() && !t2.isConstant() ) return swap12(); // Do we have ((x & (phi cons)) & con) ? diff --git a/src/main/java/com/seaofnodes/simple/node/BoolNode.java b/src/main/java/com/seaofnodes/simple/node/BoolNode.java index c0ccfd9b..85bcae0f 100644 --- a/src/main/java/com/seaofnodes/simple/node/BoolNode.java +++ b/src/main/java/com/seaofnodes/simple/node/BoolNode.java @@ -44,6 +44,8 @@ abstract public class BoolNode extends Node { TypeInteger doOp(TypeInteger t1, TypeInteger t2) { throw Utils.TODO(); } boolean doOp(double lhs, double rhs) { throw Utils.TODO(); } + Node copyF(Node lhs, Node rhs) { return null; } + public boolean isFloat() { return false; } @Override public Node idealize() { @@ -84,6 +86,7 @@ abstract public class BoolNode extends Node { Node copy(Node lhs, Node rhs) { return new EQ(lhs,rhs); } Node copyF() { return new EQF(null,null); } } + public static class LT extends BoolNode { public LT(Node lhs, Node rhs) { super(lhs,rhs); } public String op() { return "<" ; } @@ -95,6 +98,7 @@ abstract public class BoolNode extends Node { Node copy(Node lhs, Node rhs) { return new LT(lhs,rhs); } Node copyF() { return new LTF(null,null); } } + public static class LE extends BoolNode { public LE(Node lhs, Node rhs) { super(lhs,rhs); } public String op() { return "<="; } @@ -107,9 +111,9 @@ abstract public class BoolNode extends Node { Node copyF() { return new LEF(null,null); } } - public static class EQF extends EQ { public EQF(Node lhs, Node rhs) { super(lhs,rhs); } boolean doOp(double lhs, double rhs) { return lhs == rhs; } } - 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; } } + public static class EQF extends EQ { public EQF(Node lhs, Node rhs) { super(lhs,rhs); } boolean doOp(double lhs, double rhs) { return lhs == rhs; } public boolean isFloat() { return true; } } + 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 boolean isFloat() { return true; } } + public static class LEF extends LE { public LEF(Node lhs, Node rhs) { super(lhs,rhs); } boolean doOp(double lhs, double rhs) { return lhs <= rhs; } public boolean isFloat() { return true; } } // Unsigned less that, for range checks. Not directly user writable. public static class ULT extends BoolNode { diff --git a/src/main/java/com/seaofnodes/simple/node/BulkMemPhiNode.java b/src/main/java/com/seaofnodes/simple/node/BulkMemPhiNode.java index 5d8a4aba..e9d4b897 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.CodeGen; +import com.seaofnodes.simple.codegen.CodeGen; import com.seaofnodes.simple.type.Type; import com.seaofnodes.simple.type.TypeMem; import java.util.BitSet; diff --git a/src/main/java/com/seaofnodes/simple/node/CFGNode.java b/src/main/java/com/seaofnodes/simple/node/CFGNode.java index 383f18d0..0e770659 100644 --- a/src/main/java/com/seaofnodes/simple/node/CFGNode.java +++ b/src/main/java/com/seaofnodes/simple/node/CFGNode.java @@ -1,6 +1,7 @@ package com.seaofnodes.simple.node; -import com.seaofnodes.simple.CodeGen; +import com.seaofnodes.simple.codegen.CodeGen; + import com.seaofnodes.simple.Ary; import com.seaofnodes.simple.Parser; import com.seaofnodes.simple.Utils; @@ -19,16 +20,27 @@ import java.util.HashSet; *

* loop_depth is computed after optimization as part of scheduling. * + * Start - Block head; has constants and trailing Funs + * Region - Block head; has Phis. Includes Fun and Parms. + * CallEnd - Block head; followed by CProj and Proj; also linked RPC Parms + * CProj - Block head + * + * If - Block tail; followed by CProj same block + * Call - Block tail; followed by CallEnd and linked Funs + * New/intrinsic - If followed by CProj: block tail else mid-block; followed by Proj + * */ public abstract class CFGNode extends Node { public CFGNode(Node... nodes) { super(nodes); } public CFGNode(CFGNode cfg) { super(cfg); - _idepth = cfg._idepth; - _idepthVersion = cfg._idepthVersion; - _ltree = cfg._ltree; - _pre = cfg._pre; + if( cfg != null ) { + _idepth = cfg._idepth; + _idepthVersion = cfg._idepthVersion; + _ltree = cfg._ltree; + _pre = cfg._pre; + } } public CFGNode cfg(int idx) { return (CFGNode)in(idx); } @@ -85,6 +97,14 @@ public abstract class CFGNode extends Node { return lhs; } + // Find nearest enclosing FunNode + public FunNode fun() { + CFGNode cfg = this; + while( !(cfg instanceof FunNode fun) ) + cfg = cfg.idom(); + return fun; + } + // ------------------------------------------------------------------------ // Loop nesting public LoopNode loop() { return _ltree._head; } @@ -169,12 +189,11 @@ public abstract class CFGNode extends Node { return pre; } - boolean skip(CFGNode usecfg) { + private boolean skip(CFGNode usecfg) { // Only walk control users that are alive. // Do not walk from a Call to linked Fun's. return usecfg instanceof XCtrlNode || (this instanceof CallNode && usecfg instanceof FunNode) || (this instanceof ReturnNode && usecfg instanceof CallEndNode); } - } diff --git a/src/main/java/com/seaofnodes/simple/node/CProjNode.java b/src/main/java/com/seaofnodes/simple/node/CProjNode.java index c338ff4c..bfdcb04b 100644 --- a/src/main/java/com/seaofnodes/simple/node/CProjNode.java +++ b/src/main/java/com/seaofnodes/simple/node/CProjNode.java @@ -3,6 +3,7 @@ package com.seaofnodes.simple.node; import com.seaofnodes.print.ExprPrinter; import com.seaofnodes.simple.Parser; +import com.seaofnodes.simple.Utils; import com.seaofnodes.simple.type.Type; import com.seaofnodes.simple.type.TypeMem; import com.seaofnodes.simple.type.TypeTuple; @@ -26,7 +27,6 @@ public class CProjNode extends CFGNode { @Override protected ExprPrinter _print1(ExprPrinter p) { return p.p(_label); } - @Override public boolean isMultiTail() { return in(0).isMultiHead(); } @Override public boolean blockHead() { return true; } public CFGNode ctrl() { return cfg(0); } diff --git a/src/main/java/com/seaofnodes/simple/node/CallEndNode.java b/src/main/java/com/seaofnodes/simple/node/CallEndNode.java index 83c98b97..0ee3d418 100644 --- a/src/main/java/com/seaofnodes/simple/node/CallEndNode.java +++ b/src/main/java/com/seaofnodes/simple/node/CallEndNode.java @@ -3,6 +3,7 @@ package com.seaofnodes.simple.node; import com.seaofnodes.print.ExprPrinter; import com.seaofnodes.simple.*; +import com.seaofnodes.simple.codegen.CodeGen; import com.seaofnodes.simple.type.*; /** @@ -13,13 +14,12 @@ public class CallEndNode extends CFGNode implements MultiNode { // When set true, this Call/CallEnd/Fun/Return is being trivially inlined private boolean _folding; public boolean folding() { return _folding; } + public final TypeRPC _rpc; - public CallEndNode(CallNode call) { super(new Node[]{call}); } - public CallEndNode(CallEndNode cend) { super(cend); } + public CallEndNode(CallNode call) { super(new Node[]{call}); _rpc = TypeRPC.constant(_nid); } + public CallEndNode(CallEndNode cend) { super(cend); _rpc = cend._rpc; } - @Override - public String label() { return "CallEnd"; } - @Override public boolean isMultiHead() { return true; } + @Override public String label() { return "CallEnd"; } @Override public boolean blockHead() { return true; } public CallNode call() { return (CallNode)in(0); } @@ -37,7 +37,17 @@ public class CallEndNode extends CFGNode implements MultiNode { public Type compute() { if( !(in(0) instanceof CallNode call) ) return TypeTuple.RET.dual(); - Type ret = call.fptr().addDep(this)._type instanceof TypeFunPtr tfp ? tfp.ret() : Type.BOTTOM; + Type ret = Type.BOTTOM; + TypeMem mem = TypeMem.BOT; + if( call.fptr().addDep(this)._type instanceof TypeFunPtr tfp ) { + ret = tfp.ret(); + // Here, if I can figure out I've found *all* callers, then I can meet + // across the linked returns and join with the function return type. + if( tfp.isConstant() && nIns()>1 ) { + assert nIns()==2; // Linked exactly once for a constant + ret = ((TypeTuple)in(1)._type).ret(); // Return type + } + } return TypeTuple.make(call._type,TypeMem.BOT,ret); } @@ -86,6 +96,99 @@ public class CallEndNode extends CFGNode implements MultiNode { return null; } + // Clone only tiny, straight-line boilerplate, after ordinary peeps settle. + // Larger bodies still use the existing single-caller, non-cloning inline. + public boolean inlineSmall() { + if( isDead() || _folding || nIns()!=2 || !(in(0) instanceof CallNode call) ) return false; + Node fptr = call.fptr(); + if( !(fptr instanceof ConstantNode) || + !(fptr._type instanceof TypeFunPtr tfp && tfp.notNull() && tfp.isConstant()) ) + { fptr.addDep(this); return false; } + if( call.err()!=null ) return false; + ReturnNode ret = (ReturnNode)in(1); + FunNode fun = ret.fun(); + if( fun._folding ) return false; + int len = smallBody(fun); + if( len==0 ) return false; + // All callers consume the three normal return projections. + for( Node use : _outputs ) + if( !((use instanceof ProjNode p && p._idx>=0 && p._idx<3) || + (use instanceof CProjNode cp && cp._idx==0)) ) return false; + + // Substitute control and arguments directly; no new function identity, + // parameter Phis, or return address is needed for this straight line. + Node[] copy = new Node[len]; + for( int i=0; i1 ) { + Node use = out(0)==null ? out(1) : out(0); + int idx = use instanceof ProjNode p ? p._idx : ((CProjNode)use)._idx; + CodeGen.CODE.addAll(use._outputs); + use.subsume(result[idx]); + } + unkill(); + for( Node n : result ) n.unkill(); + CodeGen.CODE.invalidateIDepthCaches(); + return true; + } + + private static Node mapped(Node n, Node[] copy) { + for( int i=0; iTINYBODY.length ) return 0; + for( Node use : n._outputs ) { + if( use==null ) continue; + int j=0; + while( j _print1(ExprPrinter p) { String fname = name(); @@ -45,9 +44,9 @@ public class CallNode extends CFGNode { Node ctrl() { return in(0); } Node mem () { return in(1); } // Args mapped 1-to-1 on inputs, so conceptually start at 2 - Node arg(int idx) { return in(idx); } + public Node arg(int idx) { return in(idx); } // Same arg accounting as TFPs, although the numbering starts at 2 - int nargs() { return nIns()-3; } // Minus control, memory, fptr + public int nargs() { return nIns()-3; } // Minus control, memory, fptr // args from input 2 to last; last is function input public Node fptr() { return _inputs.last(); } // Error if not a TFP @@ -124,12 +123,28 @@ public class CallNode extends CFGNode { fun.addDef(this); for( Node use : fun._outputs ) if( use instanceof ParmNode parm ) - parm.addDef(parm._idx==0 ? cend() : arg(parm._idx)); + parm.addDef(parm._idx==0 ? new ConstantNode(cend()._rpc).peephole() : arg(parm._idx)); // Call end points to function return CodeGen.CODE.add(cend()).addDef(fun.ret()); assert linked(fun); } + // Unlink all linked functions + public void unlink_all() { + for( int i=0; i<_outputs._len; i++ ) + if( out(i) instanceof FunNode fun ) { + assert linked(fun); + int idx = fun._inputs.find(this); + for( Node use : fun._outputs ) + if( use instanceof ParmNode parm ) + use.delDef(idx); + fun.delDef(idx); + cend().delDef(cend()._inputs.find(fun.ret())); + assert !linked(fun); + i--; + } + } + @Override public Parser.ParseException err() { if( !(fptr()._type instanceof TypeFunPtr tfp) ) diff --git a/src/main/java/com/seaofnodes/simple/node/CalleeSaveNode.java b/src/main/java/com/seaofnodes/simple/node/CalleeSaveNode.java new file mode 100644 index 00000000..d4a937bd --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/CalleeSaveNode.java @@ -0,0 +1,25 @@ +package com.seaofnodes.simple.node; + +import com.seaofnodes.simple.Utils; +import com.seaofnodes.simple.codegen.RegMask; +import com.seaofnodes.simple.type.Type; +import java.util.BitSet; +import java.io.ByteArrayOutputStream; + +public class CalleeSaveNode extends ProjNode implements MachNode { + final RegMask _mask; + public CalleeSaveNode(FunNode fun, int reg, String label) { + super(fun,reg,label); + fun._outputs.pop(); + int i=0; + while( fun.out(i) instanceof PhiNode ) i++; + fun._outputs.insert(this,i); + _mask = new RegMask(reg); + } + @Override public RegMask regmap(int i) { return null; } + @Override public RegMask outregmap() { return _mask; } + @Override public int encoding(ByteArrayOutputStream bytes) { return 0; } + + @Override public Type compute() { return Type.BOTTOM; } + @Override public Node idealize() { return null; } +} diff --git a/src/main/java/com/seaofnodes/simple/node/CastNode.java b/src/main/java/com/seaofnodes/simple/node/CastNode.java index 6bf8ee90..9aedf53e 100644 --- a/src/main/java/com/seaofnodes/simple/node/CastNode.java +++ b/src/main/java/com/seaofnodes/simple/node/CastNode.java @@ -10,13 +10,19 @@ import com.seaofnodes.simple.type.TypeInteger; // Upcast (join) the input to a t. Used after guard test to lift an input. // Can also be used to make a type-assertion if ctrl is null. public class CastNode extends Node { - private final Type _t; + public Type _t; public CastNode(Type t, Node ctrl, Node in) { super(ctrl, in); _t = t; setType(compute()); } + public CastNode(CastNode c) { + super(c.in(0), c.in(1)); // Call parent constructor + this._t = c._t; // Copy the Type field + setType(compute()); // Ensure type is recomputed + } + @Override public String label() { return "("+_t.str()+")"; } diff --git a/src/main/java/com/seaofnodes/simple/node/ConstantNode.java b/src/main/java/com/seaofnodes/simple/node/ConstantNode.java index 89014205..cadb6511 100644 --- a/src/main/java/com/seaofnodes/simple/node/ConstantNode.java +++ b/src/main/java/com/seaofnodes/simple/node/ConstantNode.java @@ -2,7 +2,7 @@ package com.seaofnodes.simple.node; import com.seaofnodes.print.ExprPrinter; -import com.seaofnodes.simple.CodeGen; +import com.seaofnodes.simple.codegen.CodeGen; import com.seaofnodes.simple.Parser; import com.seaofnodes.simple.SB; import com.seaofnodes.simple.type.Type; @@ -27,7 +27,8 @@ public class ConstantNode extends Node { super(new Node[]{CodeGen.CODE._start}); _con = _type = type; } - public ConstantNode( ConstantNode con ) { super(con); _con = con._type; } + public ConstantNode( Node con, Type t ) { super(con); _con = t; } + public ConstantNode( ConstantNode con ) { this(con,con._type); } public static Node make( Type type ) { if( type==Type. CONTROL ) return new CtrlNode(); @@ -51,7 +52,6 @@ public class ConstantNode extends Node { return p.p(_con.print(new SB())); } - @Override public boolean isMultiTail() { return true; } @Override public boolean isConst() { return true; } @Override diff --git a/src/main/java/com/seaofnodes/simple/node/CtrlNode.java b/src/main/java/com/seaofnodes/simple/node/CtrlNode.java index e647a68a..cf630ad4 100644 --- a/src/main/java/com/seaofnodes/simple/node/CtrlNode.java +++ b/src/main/java/com/seaofnodes/simple/node/CtrlNode.java @@ -2,7 +2,7 @@ package com.seaofnodes.simple.node; import com.seaofnodes.print.ExprPrinter; -import com.seaofnodes.simple.CodeGen; +import com.seaofnodes.simple.codegen.CodeGen; import com.seaofnodes.simple.Parser; import com.seaofnodes.simple.type.Type; diff --git a/src/main/java/com/seaofnodes/simple/node/DivFNode.java b/src/main/java/com/seaofnodes/simple/node/DivFNode.java index a3f4318d..149499f9 100644 --- a/src/main/java/com/seaofnodes/simple/node/DivFNode.java +++ b/src/main/java/com/seaofnodes/simple/node/DivFNode.java @@ -24,14 +24,9 @@ public class DivFNode extends Node { @Override public Node idealize() { - Node lhs = in(1); - Node rhs = in(2); - Type t1 = lhs._type; - Type t2 = rhs._type; - - // Div of 1. - if ( t2.isConstant() && t2 instanceof TypeFloat i && i.value()==1 ) - return lhs; + // Div of constant + if( in(2)._type instanceof TypeFloat f && f.isConstant() ) + return new MulFNode(in(1),new ConstantNode(TypeFloat.constant(1.0/f.value())).peephole()); return null; } diff --git a/src/main/java/com/seaofnodes/simple/node/FunNode.java b/src/main/java/com/seaofnodes/simple/node/FunNode.java index d24f4829..2c91d1fc 100644 --- a/src/main/java/com/seaofnodes/simple/node/FunNode.java +++ b/src/main/java/com/seaofnodes/simple/node/FunNode.java @@ -3,8 +3,10 @@ package com.seaofnodes.simple.node; import com.seaofnodes.simple.*; import com.seaofnodes.simple.type.Type; import com.seaofnodes.simple.type.TypeFunPtr; +import com.seaofnodes.simple.type.TypeTuple; + import java.util.BitSet; -import static com.seaofnodes.simple.CodeGen.CODE; +import static com.seaofnodes.simple.codegen.CodeGen.CODE; public class FunNode extends RegionNode { @@ -18,10 +20,19 @@ public class FunNode extends RegionNode { 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; } + public FunNode( FunNode fun ) { + super( fun, fun==null ? null : fun._loc ); + if( fun!=null ) { + _sig = fun.sig(); + _name = fun._name; + } else { + _sig = TypeFunPtr.BOT; + _name = ""; + } + } @Override - public String label() { return _name == null ? "$fun" : _name; } + public String label() { return _name == null ? "$fun"+_sig.fidx() : _name; } // Find the one CFG user from Fun. It's not always the Return, but always // the Return *is* a CFG user of Fun. @@ -44,11 +55,11 @@ public class FunNode extends RegionNode { // Cannot create the Return and Fun at the same time; one has to be first. // So setting the return requires a second step. public void setRet(ReturnNode ret) { _ret=ret; } - ReturnNode ret() { assert _ret!=null; return _ret; } + public ReturnNode ret() { assert _ret!=null; return _ret; } // Signature can improve over time public TypeFunPtr sig() { return _sig; } - void setSig( TypeFunPtr sig ) { + public void setSig( TypeFunPtr sig ) { assert sig.isa(_sig); if( _sig != sig ) { CODE.add(this); @@ -73,6 +84,10 @@ public class FunNode extends RegionNode { return progress; } + // Upgrade inferred or user-written return type to actual + if( _ret!=null && _ret._type instanceof TypeTuple tt && tt.ret() != _sig.ret() ) + throw Utils.TODO(); + // 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. diff --git a/src/main/java/com/seaofnodes/simple/node/IfNode.java b/src/main/java/com/seaofnodes/simple/node/IfNode.java index a01eb8bd..2c4cad70 100644 --- a/src/main/java/com/seaofnodes/simple/node/IfNode.java +++ b/src/main/java/com/seaofnodes/simple/node/IfNode.java @@ -1,7 +1,7 @@ package com.seaofnodes.simple.node; -import com.seaofnodes.simple.CodeGen; +import com.seaofnodes.simple.codegen.CodeGen; import com.seaofnodes.simple.IterPeeps; import com.seaofnodes.simple.type.*; import java.util.HashSet; @@ -19,8 +19,6 @@ public class IfNode extends CFGNode implements MultiNode { @Override protected String format() { return "if( %1 )"; } - @Override public boolean isMultiHead() { return true; } - public Node ctrl() { return in(0); } public Node pred() { return in(1); } diff --git a/src/main/java/com/seaofnodes/simple/node/LoadNode.java b/src/main/java/com/seaofnodes/simple/node/LoadNode.java index a06d1291..b9905912 100644 --- a/src/main/java/com/seaofnodes/simple/node/LoadNode.java +++ b/src/main/java/com/seaofnodes/simple/node/LoadNode.java @@ -2,7 +2,7 @@ package com.seaofnodes.simple.node; import com.seaofnodes.print.ExprPrinter; -import com.seaofnodes.simple.CodeGen; +import com.seaofnodes.simple.codegen.CodeGen; import com.seaofnodes.simple.Parser; import com.seaofnodes.simple.Utils; import com.seaofnodes.simple.type.*; @@ -227,9 +227,9 @@ public class LoadNode extends MemOpNode { 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); + if( shf._type==TypeInteger.ZERO ) + return val; Node shl = new ShlNode(null,val,shf.keep()).peephole(); return new SarNode(null,shl,shf.unkeep()); } diff --git a/src/main/java/com/seaofnodes/simple/node/LogicalNode.java b/src/main/java/com/seaofnodes/simple/node/LogicalNode.java index c59bd948..6ac16d31 100644 --- a/src/main/java/com/seaofnodes/simple/node/LogicalNode.java +++ b/src/main/java/com/seaofnodes/simple/node/LogicalNode.java @@ -20,8 +20,8 @@ public abstract class LogicalNode extends Node { @Override public Parser.ParseException err() { if( in(1)._type.isHigh() || in(2)._type.isHigh() ) return null; - if( !(in(1)._type instanceof TypeInteger) ) return Parser.error("Cannot '"+op()+"' " + in(1)._type.glb(),_loc); - if( !(in(2)._type instanceof TypeInteger) ) return Parser.error("Cannot '"+op()+"' " + in(2)._type.glb(), _loc); + if( !(in(1)._type instanceof TypeInteger) ) return Parser.error("Cannot '"+op()+"' " + in(1)._type,_loc); + if( !(in(2)._type instanceof TypeInteger) ) return Parser.error("Cannot '"+op()+"' " + in(2)._type,_loc); return null; } } diff --git a/src/main/java/com/seaofnodes/simple/node/MachConcreteNode.java b/src/main/java/com/seaofnodes/simple/node/MachConcreteNode.java index c1f007e8..4f5d9151 100644 --- a/src/main/java/com/seaofnodes/simple/node/MachConcreteNode.java +++ b/src/main/java/com/seaofnodes/simple/node/MachConcreteNode.java @@ -8,7 +8,7 @@ import java.io.ByteArrayOutputStream; // Generic machine-specific class, has a few Node implementations that have to // exist (abstract) but are not useful past the optimizer. -public abstract class MachConcreteNode extends Node implements MachNode{ +public abstract class MachConcreteNode extends Node implements MachNode { public MachConcreteNode(Node node) { super(node); } public MachConcreteNode(Node[]nodes) { super(nodes); } diff --git a/src/main/java/com/seaofnodes/simple/node/MachNode.java b/src/main/java/com/seaofnodes/simple/node/MachNode.java index 9f2fee85..ecdbc651 100644 --- a/src/main/java/com/seaofnodes/simple/node/MachNode.java +++ b/src/main/java/com/seaofnodes/simple/node/MachNode.java @@ -1,6 +1,9 @@ package com.seaofnodes.simple.node; import com.seaofnodes.simple.*; +import com.seaofnodes.simple.codegen.Machine; +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.codegen.RegMask; import java.io.ByteArrayOutputStream; public interface MachNode { @@ -12,13 +15,16 @@ public interface MachNode { default void postSelect() { } // Register mask allowed on input i. 0 for no register. - abstract public RegMask regmap(int i); + RegMask regmap( int i ); // Register mask allowed as a result. 0 for no register. - abstract public RegMask outregmap(); + RegMask outregmap(); // Multi-reg-defining machine op; idx comes from Proj. // Sometimes these Projs have no uses, and just exist to kill a register. default RegMask outregmap(int idx) { throw Utils.TODO(); } + // Set of killed registers + default RegMask killmap() { return null; } + // If this op updates the same register as input 'idx', e.g. many X86 "R1 += // R2" style ops. This forces the output live range to equal the input live // range, so they both get the same register. @@ -32,11 +38,17 @@ public interface MachNode { // Returns either 0 for not-two-adr or the updated input; for projections // this is their Multi's updated input. default int twoAddress( ) { return 0; } + // Ok to switch arguments + default boolean commutes() { return false; } // Instructions cheaper to recreate than to spill, such as loading small constants default boolean isClone() { return false; } + + // Make a clone of a cheap instruction + default Node copy() { return null; } + // Encoding is appended into the byte array; size is returned - abstract public int encoding(ByteArrayOutputStream bytes); + int encoding(ByteArrayOutputStream bytes); // Human-readable form appended to the SB. Things like the encoding, // indentation, leading address or block labels not printed here. diff --git a/src/main/java/com/seaofnodes/simple/node/MemMergeNode.java b/src/main/java/com/seaofnodes/simple/node/MemMergeNode.java index 5f4cf128..c8a76714 100644 --- a/src/main/java/com/seaofnodes/simple/node/MemMergeNode.java +++ b/src/main/java/com/seaofnodes/simple/node/MemMergeNode.java @@ -2,9 +2,10 @@ package com.seaofnodes.simple.node; import com.seaofnodes.print.ExprPrinter; -import com.seaofnodes.simple.CodeGen; -import com.seaofnodes.simple.type.Type; -import com.seaofnodes.simple.type.TypeMem; +import com.seaofnodes.simple.*; +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.type.*; +import java.util.*; /** A default slice and explicit alias overrides. A null default means partial * memory: absent aliases are not covered, rather than having unknown contents. diff --git a/src/main/java/com/seaofnodes/simple/node/MemOpNode.java b/src/main/java/com/seaofnodes/simple/node/MemOpNode.java index e9fccc74..4f404846 100644 --- a/src/main/java/com/seaofnodes/simple/node/MemOpNode.java +++ b/src/main/java/com/seaofnodes/simple/node/MemOpNode.java @@ -36,14 +36,14 @@ public abstract class MemOpNode extends Node { } public MemOpNode( Node mach, MemOpNode mop ) { super(mach); - _name = mop._name; - _alias = mop._alias; - _declaredType = mop._declaredType; - _loc = mop._loc; + _name = mop==null ? null : mop._name; + _alias = mop==null ? 0 : mop._alias; + _loc = mop==null ? null : mop._loc; + _declaredType = mop==null ? Type.BOTTOM : mop._declaredType; } // - static String mlabel(String name) { return name.equals("[]") ? "ary" : (name.equals("#") ? "len" : name); } + static String mlabel(String name) { return "[]".equals(name) ? "ary" : ("#".equals(name) ? "len" : name); } String mlabel() { return mlabel(_name); } public Node mem() { return in(1); } diff --git a/src/main/java/com/seaofnodes/simple/node/MemPhiNode.java b/src/main/java/com/seaofnodes/simple/node/MemPhiNode.java index 38cbcd5b..df2c72f7 100644 --- a/src/main/java/com/seaofnodes/simple/node/MemPhiNode.java +++ b/src/main/java/com/seaofnodes/simple/node/MemPhiNode.java @@ -1,6 +1,6 @@ package com.seaofnodes.simple.node; -import com.seaofnodes.simple.CodeGen; +import com.seaofnodes.simple.codegen.CodeGen; import com.seaofnodes.simple.type.Type; import com.seaofnodes.simple.type.TypeMem; diff --git a/src/main/java/com/seaofnodes/simple/node/MulNode.java b/src/main/java/com/seaofnodes/simple/node/MulNode.java index 720f15d7..ac97725c 100644 --- a/src/main/java/com/seaofnodes/simple/node/MulNode.java +++ b/src/main/java/com/seaofnodes/simple/node/MulNode.java @@ -73,8 +73,8 @@ public class MulNode extends Node { @Override Node copyF() { return new MulFNode(null,null); } @Override public Parser.ParseException err() { if( in(1)._type.isHigh() || in(2)._type.isHigh() ) return null; - if( !(in(1)._type instanceof TypeInteger) ) return Parser.error("Cannot '"+label()+"' " + in(1)._type,null); - if( !(in(2)._type instanceof TypeInteger) ) return Parser.error("Cannot '"+label()+"' " + in(2)._type,null); + if( !(in(1)._type instanceof TypeInteger) ) return Parser.error("Cannot '"+label()+"' " + in(1)._type.glb(),null); + if( !(in(2)._type instanceof TypeInteger) ) return Parser.error("Cannot '"+label()+"' " + in(2)._type.glb(),null); return null; } } diff --git a/src/main/java/com/seaofnodes/simple/node/Node.java b/src/main/java/com/seaofnodes/simple/node/Node.java index dd37b051..e038d351 100644 --- a/src/main/java/com/seaofnodes/simple/node/Node.java +++ b/src/main/java/com/seaofnodes/simple/node/Node.java @@ -2,14 +2,15 @@ package com.seaofnodes.simple.node; import com.seaofnodes.print.BaseNode; -import com.seaofnodes.simple.CodeGen; import com.seaofnodes.simple.*; +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.print.IRPrinter; 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; +import static com.seaofnodes.simple.codegen.CodeGen.CODE; /** * All Nodes in the Sea of Nodes IR inherit from the Node class. @@ -58,10 +59,10 @@ public abstract class Node extends BaseNode implements Cloneable { // 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()); + assert CodeGen.CODE._phase.ordinal() >= CodeGen.Phase.InstSelect.ordinal(); + _inputs = new Ary<>(n==null ? new Node[0] : n._inputs.asAry()); _outputs = new Ary<>(Node.class); - _type = n._type; + _type = n==null ? Type.BOTTOM : n._type; _deps = null; _hash = 0; } @@ -71,9 +72,7 @@ public abstract class Node extends BaseNode implements Cloneable { public String p(int depth) { return IRPrinter.prettyPrint(this,depth); } - public boolean isMultiHead() { return false; } - public boolean isMultiTail() { return false; } - public boolean isConst () { return false; } + public boolean isConst() { return false; } // ------------------------------------------------------------------------ // Graph Node & Edge manipulation @@ -133,12 +132,16 @@ public abstract class Node extends BaseNode implements Cloneable { // Return new_def for easy flow-coding return new_def; } + public N setDefX(int idx, N new_def ) { + while( nIns() <= idx ) addDef(null); + return setDef(idx,new_def); + } // Remove the numbered input, compressing the inputs in-place. This // shuffles the order deterministically - which is suitable for Region and // Phi, but not for every Node. If the def goes dead, it is recursively // killed, which may include 'this' Node. - Node delDef(int idx) { + public Node delDef(int idx) { unlock(); Node old_def = in(idx); _inputs.del(idx); @@ -244,6 +247,67 @@ public abstract class Node extends BaseNode implements Cloneable { kill(); } + // insert `this` immediately after `def` in the same basic block. + public void insertAfter( Node def ) { + CFGNode cfg = def.cfg0(); + int i = cfg._outputs.find(def)+1; + if( cfg instanceof CallEndNode ) { + cfg = cfg.uctrl(); i=0; + } else if( def.in(0) instanceof MultiNode ) { + assert i==0; + i = cfg._outputs.find(def.in(0))+1; + } + + while( cfg.out(i) instanceof PhiNode || cfg.out(i) instanceof CalleeSaveNode ) i++; + cfg._outputs.insert(this,i); + _inputs.set(0,cfg); + } + + // Insert this in front of use.in(uidx) with this, and insert this + // immediately before use in the basic block. + public void insertBefore( Node use, int uidx ) { + CFGNode cfg = use.cfg0(); + int i; + if( use instanceof PhiNode phi ) { + cfg = phi.region().cfg(uidx); + i = cfg.nOuts()-1; + } else { + i = cfg._outputs.find(use); + } + cfg._outputs.insert(this,i); + _inputs.set(0,cfg); + if( _inputs._len > 1 && this instanceof SplitNode ) + setDefOrdered(1,use.in(uidx)); + use.setDefOrdered(uidx,this); + } + + public void setDefOrdered(int idx, Node def) { + // If old is dying, remove from CFG ordered + Node old = in(idx); + if( old!=null && old.nOuts()==1 ) { + CFGNode cfg = old.cfg0(); + if( cfg!=null ) { + cfg._outputs.remove(cfg._outputs.find(old)); + old._inputs.set(0,null); + } + } + setDef(idx,def); + } + + public void killOrdered() { + CFGNode cfg = cfg0(); + cfg._outputs.remove(cfg._outputs.find(this)); + _inputs.set(0,null); + kill(); + } + + public void removeSplit() { + CFGNode cfg = cfg0(); + cfg._outputs.remove(cfg._outputs.find(this)); + _inputs.set(0,null); + if( _inputs._len > 1 ) subsume(in(1)); + } + // ------------------------------------------------------------------------ // Graph-based optimizations @@ -531,7 +595,7 @@ public abstract class Node extends BaseNode implements Cloneable { // Peephole utilities // Swap inputs without letting either input go dead during the swap. - Node swap12() { + public Node swap12() { unlock(); // Hash is order dependent _inputs.swap(1,2); return this; @@ -600,7 +664,14 @@ public abstract class Node extends BaseNode implements Cloneable { /** * Debugging utility to find a Node by index */ - public Node find(int nid) { - return walk( n -> n._nid==nid ? n : null ); + public Node find(int nid) { return _find(nid, new BitSet()); } + private Node _find(int nid, BitSet bs) { + if( bs.get(_nid) ) return null; // Been there, done that + bs.set(_nid); + if( _nid==nid ) return this; + Node x; + for( Node def : _inputs ) if( def != null && (x = def._find(nid,bs)) != null ) return x; + for( Node use : _outputs ) if( use != null && (x = use._find(nid,bs)) != null ) return x; + return null; } } diff --git a/src/main/java/com/seaofnodes/simple/node/PhiNode.java b/src/main/java/com/seaofnodes/simple/node/PhiNode.java index 6fb315b4..a752977b 100644 --- a/src/main/java/com/seaofnodes/simple/node/PhiNode.java +++ b/src/main/java/com/seaofnodes/simple/node/PhiNode.java @@ -41,7 +41,6 @@ public class PhiNode extends Node { } public CFGNode region() { return (CFGNode)in(0); } - @Override public boolean isMultiTail() { return true; } @Override public boolean isMem() { return _declaredType instanceof TypeMem; } @Override @@ -72,6 +71,11 @@ public class PhiNode extends Node { if (live != null) return live; + // No bother if region is going to fold dead paths soon + for( int i=1; i op(Phi(a,c),Phi(b,d),...). Node down; if( same_op() && (down=drop_same_op())!=null ) return down; @@ -168,6 +172,9 @@ public class PhiNode extends Node { } // Factoring must not widen the result (e.g. correlated And operands). if( cp.compute().isa(compute()) ) return cp; + for( int i=1; i _print1(ExprPrinter p) { p.p("return "); - p.n(expr()); + if( expr()==null ) p.p("----"); + else p.n(expr()); return p.p(";"); } @@ -40,7 +41,7 @@ public class ReturnNode extends CFGNode { @Override public Type compute() { if( inProgress () ) return TypeTuple.RET; // In progress - if( _fun.isDead() ) return TypeTuple.RET.dual(); // Dead another way + // Inlining can delete the entry while callers still use this return. return TypeTuple.make(ctrl()._type,TypeMem.BOT,expr()._type); } diff --git a/src/main/java/com/seaofnodes/simple/node/ScopeNode.java b/src/main/java/com/seaofnodes/simple/node/ScopeNode.java index 881f65d6..8d3e3089 100644 --- a/src/main/java/com/seaofnodes/simple/node/ScopeNode.java +++ b/src/main/java/com/seaofnodes/simple/node/ScopeNode.java @@ -3,6 +3,7 @@ package com.seaofnodes.simple.node; import com.seaofnodes.print.ExprPrinter; import com.seaofnodes.simple.*; +import com.seaofnodes.simple.codegen.CodeGen; import com.seaofnodes.simple.type.*; import java.util.*; diff --git a/src/main/java/com/seaofnodes/simple/node/SplitNode.java b/src/main/java/com/seaofnodes/simple/node/SplitNode.java new file mode 100644 index 00000000..07166a7c --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/SplitNode.java @@ -0,0 +1,26 @@ +package com.seaofnodes.simple.node; + +import com.seaofnodes.print.ExprPrinter; + +import com.seaofnodes.simple.SB; +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.type.Type; + +public abstract class SplitNode extends MachConcreteNode { + public final String _kind; // Kind of split + public final byte _round; + public SplitNode(String kind, byte round, Node[] nodes) { super(nodes); _kind = kind; _round = round; } + @Override public String op() { return "mov"; } + @Override protected ExprPrinter _print1(ExprPrinter p) { + p.p("mov("); + if( in(1)==null ) p.p("---"); + else p.n(in(1)); + return p.close(); + } + @Override public Type compute() { return in(0)._type; } + @Override public Node idealize() { return null; } + @Override public void asm(CodeGen code, SB sb) { + sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))); + } + @Override public String comment() { return _kind + " #"+ _round; } +} diff --git a/src/main/java/com/seaofnodes/simple/node/StartNode.java b/src/main/java/com/seaofnodes/simple/node/StartNode.java index d1151090..cbaefa6a 100644 --- a/src/main/java/com/seaofnodes/simple/node/StartNode.java +++ b/src/main/java/com/seaofnodes/simple/node/StartNode.java @@ -2,7 +2,7 @@ package com.seaofnodes.simple.node; import com.seaofnodes.print.ExprPrinter; -import com.seaofnodes.simple.CodeGen; +import com.seaofnodes.simple.codegen.CodeGen; import com.seaofnodes.simple.Parser; import com.seaofnodes.simple.type.*; import java.util.HashSet; @@ -14,7 +14,7 @@ public class StartNode extends LoopNode implements MultiNode { final Type _arg; public StartNode(Type arg) { super((Parser.Lexer)null,null); _arg = arg; _type = compute(); } - public StartNode(StartNode start) { super(start); _arg = start._arg; } + public StartNode(StartNode start) { super(start); _arg = start==null ? null : start._arg; } @Override public String label() { return "Start"; } @@ -22,7 +22,6 @@ public class StartNode extends LoopNode implements MultiNode { return p.p(label()); } - @Override public boolean isMultiHead() { return true; } @Override public boolean blockHead() { return true; } @Override public CFGNode cfg0() { return null; } diff --git a/src/main/java/com/seaofnodes/simple/node/StopNode.java b/src/main/java/com/seaofnodes/simple/node/StopNode.java index 01199e9b..524c6a9e 100644 --- a/src/main/java/com/seaofnodes/simple/node/StopNode.java +++ b/src/main/java/com/seaofnodes/simple/node/StopNode.java @@ -14,7 +14,7 @@ public class StopNode extends CFGNode { _src = src; _type = compute(); } - public StopNode(StopNode stop) { super(stop); _src = stop._src; } + public StopNode(StopNode stop) { super(stop); _src = stop==null ? null : stop._src; } @Override public String label() { diff --git a/src/main/java/com/seaofnodes/simple/node/StoreNode.java b/src/main/java/com/seaofnodes/simple/node/StoreNode.java index 86c759bd..9e6689f0 100644 --- a/src/main/java/com/seaofnodes/simple/node/StoreNode.java +++ b/src/main/java/com/seaofnodes/simple/node/StoreNode.java @@ -2,6 +2,7 @@ package com.seaofnodes.simple.node; import com.seaofnodes.print.ExprPrinter; +import com.seaofnodes.simple.codegen.*; import com.seaofnodes.simple.*; import com.seaofnodes.simple.type.*; diff --git a/src/main/java/com/seaofnodes/simple/node/XCtrlNode.java b/src/main/java/com/seaofnodes/simple/node/XCtrlNode.java index 891291d0..477b9af8 100644 --- a/src/main/java/com/seaofnodes/simple/node/XCtrlNode.java +++ b/src/main/java/com/seaofnodes/simple/node/XCtrlNode.java @@ -2,7 +2,7 @@ package com.seaofnodes.simple.node; import com.seaofnodes.print.ExprPrinter; -import com.seaofnodes.simple.CodeGen; +import com.seaofnodes.simple.codegen.CodeGen; import com.seaofnodes.simple.Parser; import com.seaofnodes.simple.type.Type; @@ -11,7 +11,6 @@ public class XCtrlNode extends CFGNode { @Override public String label() { return "Xctrl"; } @Override protected ExprPrinter _print1(ExprPrinter p) { return p.p("Xctrl"); } @Override public boolean isConst() { return true; } - @Override public boolean isMultiTail() { return true; } @Override public Type compute() { return Type.XCONTROL; } @Override public Node idealize() { return null; } } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/AddARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/AddARM.java new file mode 100644 index 00000000..c4014571 --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/AddARM.java @@ -0,0 +1,34 @@ +package com.seaofnodes.simple.node.cpus.arm; + +import com.seaofnodes.simple.*; +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.codegen.RegMask; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.type.TypeInteger; +import java.io.ByteArrayOutputStream; + +public class AddARM extends MachConcreteNode implements MachNode { + + AddARM( Node add) {super(add); } + + // Register mask allowed on input i. + @Override public RegMask regmap(int i) { + assert i== 1 || i == 2 || i == 3; + return arm.RMASK; + } + // Register mask allowed as a result. 0 for no register. + @Override public RegMask outregmap() { return arm.RMASK; } + + // 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/arm/AddFARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/AddFARM.java new file mode 100644 index 00000000..098ab8c1 --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/AddFARM.java @@ -0,0 +1,31 @@ +package com.seaofnodes.simple.node.cpus.arm; + + +import com.seaofnodes.simple.*; +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.codegen.RegMask; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.type.TypeInteger; +import java.io.ByteArrayOutputStream; + +public class AddFARM extends MachConcreteNode implements MachNode { + AddFARM(Node addf) { super(addf); } + + // Register mask allowed on input i. + @Override public RegMask regmap(int i) { assert i==1 || i==2; return arm.DMASK; } + // Register mask allowed as a result. 0 for no register. + @Override public RegMask outregmap() { return arm.DMASK; } + + // 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: "vadd.32 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/arm/AddIARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/AddIARM.java new file mode 100644 index 00000000..c8f5f8dd --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/AddIARM.java @@ -0,0 +1,51 @@ +package com.seaofnodes.simple.node.cpus.arm; + +import com.seaofnodes.simple.*; +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.codegen.RegMask; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.type.TypeInteger; +import java.io.ByteArrayOutputStream; + +public class AddIARM extends MachConcreteNode implements MachNode { + final TypeInteger _ti; + + AddIARM(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 arm.RMASK; + } + + // Register mask allowed as a result. 0 for no register. + @Override + public RegMask outregmap() { + return arm.RMASK; + } + + // 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"); + + } + +} \ No newline at end of file diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/AndARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/AndARM.java new file mode 100644 index 00000000..7655df40 --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/AndARM.java @@ -0,0 +1,38 @@ +package com.seaofnodes.simple.node.cpus.arm; + +import com.seaofnodes.simple.SB; +import com.seaofnodes.simple.Utils; +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.codegen.RegMask; +import com.seaofnodes.simple.node.MachConcreteNode; +import com.seaofnodes.simple.node.MachNode; +import com.seaofnodes.simple.node.Node; + +import java.io.ByteArrayOutputStream; + +public class AndARM extends MachConcreteNode implements MachNode { + AndARM(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 arm.RMASK; + } + + // Register mask allowed as a result. 0 for no register. + @Override public RegMask outregmap() { return arm.RMASK; } + + // 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))).p(" & ").p(code.reg(in(2))); + } +} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/AndIARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/AndIARM.java new file mode 100644 index 00000000..ee05654b --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/AndIARM.java @@ -0,0 +1,56 @@ +package com.seaofnodes.simple.node.cpus.arm; + + +import com.seaofnodes.simple.*; +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.codegen.RegMask; +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 AndIARM extends MachConcreteNode implements MachNode { + final TypeInteger _ti; + + AndIARM(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 arm.RMASK; + } + + // Register mask allowed as a result. 0 for no register. + @Override + public RegMask outregmap() { + return arm.RMASK; + } + + // 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"; + } + +} \ No newline at end of file diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/AsrARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/AsrARM.java new file mode 100644 index 00000000..a79de49b --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/AsrARM.java @@ -0,0 +1,40 @@ +package com.seaofnodes.simple.node.cpus.arm; + +import com.seaofnodes.simple.*; +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.codegen.RegMask; +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; + +// Arithmetic Shift Right (register) +public class AsrARM extends MachConcreteNode implements MachNode { + AsrARM(Node asr) {super(asr);} + // Register mask allowed on input i. + // This is the normal calling convention + @Override public RegMask regmap(int i) { + // assert i==1; + return arm.RMASK; } + // Register mask allowed as a result. 0 for no register. + @Override public RegMask outregmap() { return arm.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: "asr 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 "asr"; } +} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/AsrIARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/AsrIARM.java new file mode 100644 index 00000000..099c3699 --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/AsrIARM.java @@ -0,0 +1,41 @@ +package com.seaofnodes.simple.node.cpus.arm; + + +import com.seaofnodes.simple.*; +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.codegen.RegMask; +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; + +// Arithmetic Shift Right (immediate) +public class AsrIARM extends MachConcreteNode implements MachNode { + final TypeInteger _ti; + AsrIARM(Node asri, TypeInteger ti) {super(asri); _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 arm.RMASK; } + + // Register mask allowed as a result. 0 for no register. + @Override public RegMask outregmap() { return arm.RMASK; } + + // Encoding is appended into the byte array; size is returned + @Override public int encoding(ByteArrayOutputStream bytes) { + throw Utils.TODO(); + } + + // General form + // General form: "asri 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 "asri"; } +} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/BranchARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/BranchARM.java new file mode 100644 index 00000000..1a5108cf --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/BranchARM.java @@ -0,0 +1,48 @@ +package com.seaofnodes.simple.node.cpus.arm; + +import com.seaofnodes.simple.*; +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.codegen.RegMask; +import com.seaofnodes.simple.node.*; + +import java.io.ByteArrayOutputStream; + + +// Jump on flags, uses flags +public class BranchARM extends IfNode implements MachNode{ + final String _bop; + BranchARM(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 SetARM) + _inputs.set(1,cmp); + else + throw Utils.TODO(); + } + + @Override public RegMask regmap(int i) { assert i==1; return arm.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/arm/CallARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/CallARM.java new file mode 100644 index 00000000..0f883847 --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/CallARM.java @@ -0,0 +1,45 @@ +package com.seaofnodes.simple.node.cpus.arm; + +import com.seaofnodes.simple.*; +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.codegen.RegMask; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.type.TypeFunPtr; +import java.io.ByteArrayOutputStream; + +public class CallARM extends CallNode implements MachNode { + final TypeFunPtr _tfp; + final String _name; + + CallARM(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 arm.callInMask(_tfp,i); // Normal argument + } + @Override public RegMask outregmap() { return null; } + + @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).p(" "); + for( int i=0; i>> ").p(code.reg(in(2))); + } + + @Override public String op() { return "lsr"; } +} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/LsrIARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/LsrIARM.java new file mode 100644 index 00000000..8fb20cb2 --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/LsrIARM.java @@ -0,0 +1,41 @@ +package com.seaofnodes.simple.node.cpus.arm; + + +import com.seaofnodes.simple.*; +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.codegen.RegMask; +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 Shift Right (immediate) +public class LsrIARM extends MachConcreteNode implements MachNode{ + final TypeInteger _ti; + LsrIARM(Node lsri, TypeInteger ti) {super(lsri); _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 arm.RMASK; } + + // Register mask allowed as a result. 0 for no register. + @Override public RegMask outregmap() { return arm.RMASK; } + + // Encoding is appended into the byte array; size is returned + @Override public int encoding(ByteArrayOutputStream bytes) { + throw Utils.TODO(); + } + + // General form + // General form: "lsrl 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 "lsrl"; } +} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/MemOpARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/MemOpARM.java new file mode 100644 index 00000000..c22e71f6 --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/MemOpARM.java @@ -0,0 +1,61 @@ +package com.seaofnodes.simple.node.cpus.arm; + +import com.seaofnodes.print.ExprPrinter; + +import com.seaofnodes.simple.*; +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.codegen.RegMask; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.type.*; +import java.io.ByteArrayOutputStream; +import java.lang.StringBuilder; + + +public class MemOpARM extends MemOpNode implements MachNode { + final int _off; // Limit 32 bits + final int _imm; // Limit 32 bits + final char _sz = (char)('0'+(1<<_declaredType.log_size())); + MemOpARM(MemOpNode mop, Node base, Node idx, int off, int imm) { + super(mop,mop); + assert base._type instanceof TypeMemPtr && !(base instanceof AddNode); + assert ptr() == base; + _inputs.setX(3,idx); + _off = off; + _imm = imm; + } + + // Store-based flavors have a value edge + MemOpARM( MemOpNode mop, Node base, Node idx, int off, int imm, Node val ) { + this(mop,base,idx,off,imm); + _inputs.setX(4,val); + } + + Node idx() { return in(3); } + Node val() { return in(4); } // Only for stores, includin + + @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) { + sb.p("[").p(code.reg(ptr())).p("+"); + if( idx() != null ) sb.p(code.reg(idx())); + else sb.p(_off); + return sb.p("]"); + } +} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/MulARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/MulARM.java new file mode 100644 index 00000000..582b97d4 --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/MulARM.java @@ -0,0 +1,34 @@ +package com.seaofnodes.simple.node.cpus.arm; + +import com.seaofnodes.simple.*; +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.codegen.RegMask; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.type.TypeInteger; +import java.io.ByteArrayOutputStream; + + +// mulh signed multiply instruction(no-imm form) +public class MulARM extends MachConcreteNode implements MachNode{ + MulARM(Node mul) {super(mul);} + + // Register mask allowed on input i. + @Override public RegMask regmap(int i) { assert i==1 || i==2; return arm.RMASK; } + // Register mask allowed as a result. 0 for no register. + @Override public RegMask outregmap() { return arm.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/arm/MulFARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/MulFARM.java new file mode 100644 index 00000000..b71b412d --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/MulFARM.java @@ -0,0 +1,30 @@ +package com.seaofnodes.simple.node.cpus.arm; + +import com.seaofnodes.simple.*; +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.codegen.RegMask; +import com.seaofnodes.simple.node.*; +import java.io.ByteArrayOutputStream; + +public class MulFARM extends MachConcreteNode implements MachNode{ + MulFARM(Node mulf) {super(mulf);} + + // Register mask allowed on input i. + @Override public RegMask regmap(int i) { assert i==1 || i==2; return arm.DMASK; } + // Register mask allowed as a result. 0 for no register. + @Override public RegMask outregmap() { return arm.DMASK; } + + // 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: "VMUL.f32 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/arm/MulIARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/MulIARM.java new file mode 100644 index 00000000..c118b00a --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/MulIARM.java @@ -0,0 +1,43 @@ +package com.seaofnodes.simple.node.cpus.arm; + +import com.seaofnodes.simple.*; +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.codegen.RegMask; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.type.TypeInteger; +import java.io.ByteArrayOutputStream; + +public class MulIARM extends MachConcreteNode implements MachNode{ + final TypeInteger _ti; + MulIARM(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 arm.RMASK; } + + // Register mask allowed as a result. 0 for no register. + @Override public RegMask outregmap() { return arm.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: "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/arm/NewARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/NewARM.java new file mode 100644 index 00000000..c145f348 --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/NewARM.java @@ -0,0 +1,31 @@ +package com.seaofnodes.simple.node.cpus.arm; + + +import com.seaofnodes.simple.*; +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.codegen.RegMask; +import com.seaofnodes.simple.node.*; + +import java.io.ByteArrayOutputStream; + +public class NewARM extends NewNode implements MachNode { + // A pre-zeroed chunk of memory. + NewARM(NewNode nnn) { super(nnn); } + // Size and pointer result in standard calling convention; null for all the + // memory and ordering edges + @Override public RegMask regmap(int i) { return i == 2 ? arm. X0_MASK : null; } + @Override public RegMask outregmap(int i) { return i == 0 ? arm. X0_MASK : null; } + @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(); + } + + // 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/arm/NotARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/NotARM.java new file mode 100644 index 00000000..28ebd60d --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/NotARM.java @@ -0,0 +1,26 @@ +package com.seaofnodes.simple.node.cpus.arm; + +import com.seaofnodes.simple.*; +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.codegen.LRG; +import com.seaofnodes.simple.codegen.RegMask; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.type.TypeInteger; +import java.io.ByteArrayOutputStream; + +import com.seaofnodes.simple.node.MachConcreteNode; + +public class NotARM extends MachConcreteNode implements MachNode{ + NotARM(NotNode not) {super(not);} + @Override public RegMask regmap(int i) { return arm.RMASK; } + @Override public RegMask outregmap() { return arm.RMASK; } + + @Override public int twoAddress( ) { return 0; } + + @Override public int encoding(ByteArrayOutputStream bytes) { + throw Utils.TODO(); + } + + @Override public void asm(CodeGen code, SB sb) { sb.p(code.reg(this)); } + @Override public String op() { return "not"; } +} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/OrARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/OrARM.java new file mode 100644 index 00000000..12ea8265 --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/OrARM.java @@ -0,0 +1,42 @@ +package com.seaofnodes.simple.node.cpus.arm; + +import com.seaofnodes.simple.SB; +import com.seaofnodes.simple.Utils; +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.codegen.RegMask; +import com.seaofnodes.simple.node.MachConcreteNode; +import com.seaofnodes.simple.node.MachNode; +import com.seaofnodes.simple.node.Node; + +import java.io.ByteArrayOutputStream; + +// Not using the ORRS variant. +public class OrARM extends MachConcreteNode implements MachNode { + OrARM(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 arm.RMASK; + } + + // Register mask allowed as a result. 0 for no register. + @Override public RegMask outregmap() { return arm.RMASK; } + + // 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))).p(" | ").p(code.reg(in(2))); + } + + @Override public String op() { return "orr"; } +} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/OrIARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/OrIARM.java new file mode 100644 index 00000000..f257a867 --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/OrIARM.java @@ -0,0 +1,40 @@ +package com.seaofnodes.simple.node.cpus.arm; + +import com.seaofnodes.simple.SB; +import com.seaofnodes.simple.Utils; +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.codegen.RegMask; +import com.seaofnodes.simple.node.MachConcreteNode; +import com.seaofnodes.simple.node.MachNode; +import com.seaofnodes.simple.node.Node; + +import com.seaofnodes.simple.type.Type; +import com.seaofnodes.simple.type.TypeInteger; +import java.io.ByteArrayOutputStream; + +public class OrIARM extends MachConcreteNode implements MachNode{ + final TypeInteger _ti; + OrIARM(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 arm.RMASK; + } + // Register mask allowed as a result. 0 for no register. + @Override public RegMask outregmap() { return arm.RMASK; } + + // 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/arm/ParmARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/ParmARM.java new file mode 100644 index 00000000..c3fa132c --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/ParmARM.java @@ -0,0 +1,25 @@ +package com.seaofnodes.simple.node.cpus.arm; + +import com.seaofnodes.simple.SB; +import com.seaofnodes.simple.Utils; +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.codegen.RegMask; +import com.seaofnodes.simple.node.MachNode; +import com.seaofnodes.simple.node.ParmNode; +import java.io.ByteArrayOutputStream; + +public class ParmARM extends ParmNode implements MachNode { + final RegMask _rmask; + ParmARM(ParmNode parm) { + super(parm); + // Assume int + _rmask = arm.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/arm/ProjARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/ProjARM.java new file mode 100644 index 00000000..b2add015 --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/ProjARM.java @@ -0,0 +1,23 @@ +package com.seaofnodes.simple.node.cpus.arm; + +import com.seaofnodes.simple.Utils; +import com.seaofnodes.simple.codegen.RegMask; +import com.seaofnodes.simple.node.MachNode; +import com.seaofnodes.simple.node.ProjNode; +import java.io.ByteArrayOutputStream; + +public class ProjARM extends ProjNode implements MachNode { + ProjARM( 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/arm/RetARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/RetARM.java new file mode 100644 index 00000000..c7d0dd4c --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/RetARM.java @@ -0,0 +1,49 @@ +package com.seaofnodes.simple.node.cpus.arm; + +import com.seaofnodes.simple.*; +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.codegen.RegMask; +import com.seaofnodes.simple.node.FunNode; +import com.seaofnodes.simple.node.ReturnNode; +import com.seaofnodes.simple.node.MachNode; +import com.seaofnodes.simple.type.TypeFloat; + +import java.io.ByteArrayOutputStream; + + +public class RetARM extends ReturnNode implements MachNode { + RetARM(ReturnNode ret, FunNode fun) { super(ret, fun); fun.setRet(this); } + + // Register mask allowed on input i. + // This is the normal calling convention + @Override public RegMask regmap(int i) { return arm.retMask(_fun.sig(),i); } + // 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) { + // Post code-gen, just print the "ret" + if( code._phase.ordinal() <= CodeGen.Phase.RegAlloc.ordinal() ) + // Prints return reg (either X0 or D0), RPC (always R30) and + // then the callee-save registers. + for( int i=2; i addf(addf); + case AddNode add -> add(add); + case AndNode and -> and(and); + case BoolNode bool -> cmp(bool); + case CallNode call -> call(call); + case CastNode cast -> new CastNode(cast); + case CallEndNode cend -> new CallEndARM(cend); + case CProjNode c -> new CProjNode(c); + case ConstantNode con -> con(con); + case DivFNode divf -> new DivFARM(divf); + case DivNode div -> new DivARM(div); + case FunNode fun -> new FunARM(fun); + case IfNode iff -> jmp(iff); + case LoadNode ld -> ld(ld); + case MemMergeNode mem -> new MemMergeNode(mem); + case MulFNode mulf -> new MulFARM(mulf); + case MulNode mul -> mul(mul); + case NewNode nnn -> new NewARM(nnn); + case OrNode or -> or(or); + case ParmNode parm -> new ParmARM(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 RetARM(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 SubFARM(subf); + case SubNode sub -> sub(sub); + case ToFloatNode tfn -> i2f8(tfn); + case ToIntegerNode cvt -> new F8ToIARM(cvt); + case XorNode xor -> xor(xor); + + case LoopNode loop -> new LoopNode(loop); + case RegionNode region-> new RegionNode(region); + default -> throw Utils.TODO(); + }; + } + + private Node mul(MulNode mul) { + Node rhs = mul.in(2); + if( rhs instanceof ConstantNode off && off._con instanceof TypeInteger toff ) { + return new MulIARM(mul, toff); + } + return new MulARM(mul); + } + private Node cmp(BoolNode bool){ + Node cmp = _cmp(bool); + return new SetARM(cmp, bool.op()); + } + + private Node i2f8(ToFloatNode tfn) { + assert tfn.in(1)._type instanceof TypeInteger ti; + return new I2F8ARM(tfn); + } + + private Node ld(LoadNode ld) { + return new LoadARM(address(ld), ld.ptr(), idx, off); + } + + 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 BranchARM(iff, ((BoolNode)iff.in(1)).op()); + } + + private Node _cmp(BoolNode bool) { + if( bool instanceof BoolNode.EQF || + bool instanceof BoolNode.LTF || + bool instanceof BoolNode.LEF ) + return new CmpFARM(bool); + + + return bool.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti + ? new CmpIARM(bool, ti) + : new CmpARM(bool); + } + + + private Node addf(AddFNode addf) { + return new AddFARM(addf); + } + + private Node add(AddNode add) { + Node rhs = add.in(2); + if( rhs instanceof ConstantNode off && off._con instanceof TypeInteger toff ) { + return new AddIARM(add, toff); + } + return new AddARM(add); + } + + private Node sub(SubNode sub) { + return sub.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti + ? new AddIARM(sub, TypeInteger.constant(-ti.value())) + : new SubARM(sub); + } + + private Node and(AndNode and) { + if( and.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti ) + return new AndIARM(and, ti); + return new AndARM(and); + } + + private Node or(OrNode or) { + if(or.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti) + return new OrIARM(or, ti); + + return new OrARM(or); + } + + private Node xor(XorNode xor) { + if(xor.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti) + return new XorIARM(xor, ti); + return new XorARM(xor); + } + + private Node con(ConstantNode con) { + if( !con._con.isConstant() ) return new ConstantNode( con ); // Default unknown caller inputs + return switch( con._con ) { + case TypeInteger ti -> new IntARM(con); + case TypeFloat tf -> new FloatARM(con); + case TypeFunPtr tfp -> new TFPARM(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 sar(SarNode sar){ + if( sar.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti) + return new AsrIARM(sar, ti); + return new AsrARM(sar); + } + + private Node shl(ShlNode shl) { + if( shl.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti) + return new LslIARM(shl, ti); + return new LslARM(shl); + + } + + private Node shr(ShrNode shr) { + if( shr.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti) + return new LsrIARM(shr, ti); + return new LsrARM(shr); + } + + private Node call(CallNode call){ + if(call.fptr() instanceof ConstantNode con && con._con instanceof TypeFunPtr tfp) + return new CallARM(call, tfp); + return new CallRRARM(call); + } + + private Node prj(ProjNode prj) { + return new ProjARM(prj); + } + + + private static int off; + private static Node idx; + private Node st(StoreNode st) { + int imm=0; + Node xval = st.val(); + // e.g store this s.cs[0] = 67; // C + if( xval instanceof ConstantNode con && con._con instanceof TypeInteger ti ) { + xval = null; + imm = (int)ti.value(); + assert imm == ti.value(); // In 32-bit range + } + return new StoreARM(address(st),st.ptr(),idx,off,imm,xval); + } + + // Gather 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 N address( N mop ) { + off = 0; // Reset + idx = 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 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; + } + + +} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/i2f8.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/i2f8.java new file mode 100644 index 00000000..e0792669 --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/i2f8.java @@ -0,0 +1,4 @@ +package com.seaofnodes.simple.node.cpus.arm; + +public class i2f8 { +} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AddFRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AddFRISC.java index bc959977..2417aeb6 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AddFRISC.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AddFRISC.java @@ -1,6 +1,8 @@ package com.seaofnodes.simple.node.cpus.riscv; import com.seaofnodes.simple.*; +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.codegen.RegMask; import com.seaofnodes.simple.node.*; import com.seaofnodes.simple.type.TypeInteger; import java.io.ByteArrayOutputStream; @@ -12,8 +14,6 @@ public class AddFRISC extends MachConcreteNode implements MachNode{ @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) { diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AddIRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AddIRISC.java index af4bf08d..e92a7b08 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AddIRISC.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AddIRISC.java @@ -1,39 +1,11 @@ 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"); - } +import com.seaofnodes.simple.Utils; +import com.seaofnodes.simple.node.Node; + +public class AddIRISC extends ImmRISC { + AddIRISC( Node add, int imm ) { super(add,imm); } + @Override int opcode() { throw Utils.TODO(); } + @Override public String glabel() { return "+"; } + @Override public String op() { return "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 index 482b7f11..344c217d 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AddRISC.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AddRISC.java @@ -1,25 +1,20 @@ package com.seaofnodes.simple.node.cpus.riscv; - import com.seaofnodes.simple.*; +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.codegen.RegMask; 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); } + @Override public String op() { return "add"; } // Register mask allowed on input i. - @Override public RegMask regmap(int i) { - assert i== 1 || i == 2 || i == 3; - return riscv.RMASK; - } + @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; } + @Override public RegMask outregmap() { return riscv.WMASK; } // Encoding is appended into the byte array; size is returned @Override public int encoding(ByteArrayOutputStream bytes) { @@ -31,6 +26,4 @@ public class AddRISC extends MachConcreteNode implements MachNode { 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 index 29c2a686..ddbf5aa4 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AndIRISC.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AndIRISC.java @@ -1,40 +1,11 @@ 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); - } +import com.seaofnodes.simple.Utils; +import com.seaofnodes.simple.node.Node; +public class AndIRISC extends ImmRISC { + AndIRISC( Node and, int imm) { super(and,imm); } + @Override int opcode() { throw Utils.TODO(); } + @Override public String glabel() { return "&"; } @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 index e697de36..ca00bc98 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AndRISC.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AndRISC.java @@ -1,9 +1,9 @@ package com.seaofnodes.simple.node.cpus.riscv; - import com.seaofnodes.simple.*; +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.codegen.RegMask; 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; @@ -23,10 +23,7 @@ public class AndRISC extends MachConcreteNode implements MachNode{ } // 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; } + @Override public RegMask outregmap() { return riscv.WMASK; } // Encoding is appended into the byte array; size is returned @Override public int encoding(ByteArrayOutputStream bytes) { @@ -36,6 +33,6 @@ public class AndRISC extends MachConcreteNode implements MachNode{ // 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))); + sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" & ").p(code.reg(in(2))); } } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/BranchRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/BranchRISC.java new file mode 100644 index 00000000..30dfd7fe --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/BranchRISC.java @@ -0,0 +1,48 @@ +package com.seaofnodes.simple.node.cpus.riscv; + +import com.seaofnodes.print.ExprPrinter; + +import com.seaofnodes.simple.*; +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.codegen.RegMask; +import com.seaofnodes.simple.node.*; +import java.io.ByteArrayOutputStream; + +// Conditional branch such as: BEQ +public class BranchRISC extends IfNode implements MachNode { + final String _bop; + // label is obtained implicitly + BranchRISC( IfNode iff, String bop, Node n1, Node n2 ) { + super(iff); + _bop = bop; + _inputs.setX(1,n1); + _inputs.setX(2,n2); + } + + @Override public String label() { return op(); } + + @Override public RegMask regmap(int i) { return riscv.RMASK; } + @Override public RegMask outregmap() { return null; } + + @Override protected ExprPrinter _print1(ExprPrinter p) { + p.p("if( ").n(in(1)).p(_bop); + if( in(2)==null ) p.p("0"); + else p.n(in(2)); + return p.p(" )"); + } + + // 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(in(1))).p(" ").p(_bop).p(" ").p(in(2)==null ? "#0" : code.reg(in(2))); + } + + @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/CBranchRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CBranchRISC.java deleted file mode 100644 index d7e23c6d..00000000 --- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CBranchRISC.java +++ /dev/null @@ -1,49 +0,0 @@ -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/CallEndRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CallEndRISC.java new file mode 100644 index 00000000..fd1ba7c3 --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CallEndRISC.java @@ -0,0 +1,35 @@ +package com.seaofnodes.simple.node.cpus.riscv; + +import com.seaofnodes.simple.SB; +import com.seaofnodes.simple.Utils; +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.codegen.RegMask; +import com.seaofnodes.simple.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 CallEndRISC extends CallEndNode implements MachNode { + final TypeFunPtr _tfp; + CallEndRISC( 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() { return null; } + @Override public RegMask outregmap(int idx) { return idx == 2 ? riscv.retMask(_tfp,2) : null; } + @Override public RegMask killmap() { return riscv.riscCallerSave(); } + + // 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/riscv/CallRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CallRISC.java index 7f062104..54f4fba9 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CallRISC.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CallRISC.java @@ -1,6 +1,8 @@ package com.seaofnodes.simple.node.cpus.riscv; import com.seaofnodes.simple.*; +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.codegen.RegMask; import com.seaofnodes.simple.node.*; import com.seaofnodes.simple.type.TypeFunPtr; import java.io.ByteArrayOutputStream; @@ -8,6 +10,7 @@ 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 @@ -18,11 +21,12 @@ public class CallRISC extends CallNode implements MachNode{ @Override public String label() { return op(); } @Override public RegMask regmap(int i) { - return riscv.callInMaskInt(i); // Normal argument + return riscv.callInMask(_tfp,i); // Normal argument } - @Override public RegMask outregmap() { return riscv.RET_MASK; } + @Override public RegMask outregmap() { return null; } @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) { @@ -30,7 +34,10 @@ public class CallRISC extends CallNode implements MachNode{ } @Override public void asm(CodeGen code, SB sb) { - sb.p(_name); + sb.p(_name).p(" "); + for( int i=0; i _print1(ExprPrinter p) { + return p.p("( ").n(in(1)).p(String.format(" %s #%d )",glabel(),_imm)); + } + + abstract int opcode(); + + // 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(" ").p(glabel()).p(" #").p(_imm); + } +} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/IntRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/IntRISC.java index bf170b22..e792b055 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/IntRISC.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/IntRISC.java @@ -1,22 +1,23 @@ package com.seaofnodes.simple.node.cpus.riscv; import com.seaofnodes.simple.*; +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.codegen.RegMask; 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); - } - + IntRISC(ConstantNode con) { super(con); } + @Override public String op() { return "ldi"; } // 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; } + @Override public RegMask outregmap() { return riscv.WMASK; } + + @Override public boolean isClone() { return true; } + @Override public IntRISC copy() { return new IntRISC(this); } // Encoding is appended into the byte array; size is returned @Override public int encoding(ByteArrayOutputStream bytes) { @@ -29,15 +30,6 @@ public class IntRISC extends ConstantNode implements MachNode { // 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 + _con.print(sb.p(reg).p(" = #")); } } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/LoadRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/LoadRISC.java index 846e313c..3fe8f44e 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/LoadRISC.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/LoadRISC.java @@ -1,22 +1,29 @@ package com.seaofnodes.simple.node.cpus.riscv; - import com.seaofnodes.simple.*; +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.codegen.RegMask; 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); + +// Load memory addressing on RISC +// Support imm, reg(direct), or reg+off(indirect) addressing +// Base = base - base pointer, offset is added to base +// idx = null +// off = off - offset added to base + +public class LoadRISC extends MemOpRISC { + LoadRISC(LoadNode ld, Node base, Node idx, int off) { + super(ld, base, idx, off, 0); } @Override public RegMask regmap(int i) { - // wider mask, encode it differently, immediate handled with hard split(in RA) - return riscv.RMASK.or(riscv.FMASK); + return riscv.RMASK; } - // Register mask allowed as a result. 0 for no register. - @Override public RegMask outregmap() { return riscv.RMASK; } + // Wide mask loads both ints and floats; encoding varies. + @Override public RegMask outregmap() { return riscv.MEM_MASK; } // Encoding is appended into the byte array; size is returned @@ -25,10 +32,9 @@ public class LoadRISC extends MemOpRISC{ } @Override public void asm(CodeGen code, SB sb) { + sb.p(code.reg(this)).p(","); + asm_address(code,sb); } - @Override public String op() { - return "lw" +_sz; - } + @Override public String op() { return "ld" +_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 index 3bf1cf09..19869285 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/MemOpRISC.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/MemOpRISC.java @@ -3,18 +3,35 @@ package com.seaofnodes.simple.node.cpus.riscv; import com.seaofnodes.print.ExprPrinter; import com.seaofnodes.simple.*; +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.codegen.RegMask; 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{ +public class MemOpRISC extends MemOpNode implements MachNode { + final int _off; // Limit 32 bits + final int _imm; // Limit 32 bits final char _sz = (char)('0'+(1<<_declaredType.log_size())); - MemOpRISC(Node op, MemOpNode mop) { - super(op, mop); + MemOpRISC( MemOpNode mop, Node base, Node idx, int off, int imm) { + super(mop,mop); + assert base._type instanceof TypeMemPtr && !(base instanceof AddNode); + assert ptr() == base; + _inputs.setX(3,idx); + _off = off; + _imm = imm; } + // Store-based flavors have a value edge + MemOpRISC( MemOpNode mop, Node base, Node idx, int off, int imm, Node val ) { + this(mop,base,idx,off,imm); + _inputs.setX(4,val); + } + + Node idx() { return in(3); } + Node val() { return in(4); } // Only for stores, including op-to-memory + @Override protected ExprPrinter _printMach(ExprPrinter p) { return p.p(".").p(_name); } @Override public boolean isMem() { return _type instanceof TypeMem; } @@ -23,6 +40,7 @@ public class MemOpRISC extends MemOpNode implements MachNode{ @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(); @@ -34,6 +52,9 @@ public class MemOpRISC extends MemOpNode implements MachNode{ SB asm_address(CodeGen code, SB sb) { - return sb; + sb.p("[").p(code.reg(ptr())).p("+"); + if( idx() != null ) sb.p(code.reg(idx())); + else sb.p(_off); + return sb.p("]"); } } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/MulFRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/MulFRISC.java index 43197b50..8a066ab1 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/MulFRISC.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/MulFRISC.java @@ -1,6 +1,8 @@ package com.seaofnodes.simple.node.cpus.riscv; import com.seaofnodes.simple.*; +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.codegen.RegMask; import com.seaofnodes.simple.node.*; import java.io.ByteArrayOutputStream; @@ -11,8 +13,6 @@ public class MulFRISC extends MachConcreteNode implements MachNode{ @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) { @@ -22,7 +22,7 @@ public class MulFRISC extends MachConcreteNode implements MachNode{ // 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))); + 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 deleted file mode 100644 index 8735068e..00000000 --- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/MulIRISC.java +++ /dev/null @@ -1,35 +0,0 @@ -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 index 8713486e..0e2d35a3 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/MulRISC.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/MulRISC.java @@ -1,8 +1,9 @@ package com.seaofnodes.simple.node.cpus.riscv; import com.seaofnodes.simple.*; +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.codegen.RegMask; 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; @@ -14,9 +15,7 @@ public class MulRISC extends MachConcreteNode implements MachNode{ // 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; } + @Override public RegMask outregmap() { return riscv.WMASK; } // Encoding is appended into the byte array; size is returned @Override public int encoding(ByteArrayOutputStream bytes) { diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/NewRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/NewRISC.java index 2b8ec4f2..e8d32570 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/NewRISC.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/NewRISC.java @@ -1,34 +1,19 @@ package com.seaofnodes.simple.node.cpus.riscv; import com.seaofnodes.simple.*; +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.codegen.RegMask; 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{ - +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; - } + NewRISC( NewNode nnn ) { super(nnn); } + // Size and pointer result in standard calling convention; null for all the + // memory and ordering edges + @Override public RegMask regmap(int i) { return i == 2 ? riscv.A0_MASK : null; } + @Override public RegMask outregmap(int i) { return i == 0 ? riscv.A0_MASK : null; } + @Override public RegMask outregmap() { return null; } // Encoding is appended into the byte array; size is returned @Override public int encoding(ByteArrayOutputStream bytes) { diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/NotRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/NotRISC.java new file mode 100644 index 00000000..6d30cd1c --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/NotRISC.java @@ -0,0 +1,25 @@ +package com.seaofnodes.simple.node.cpus.riscv; + +import com.seaofnodes.simple.*; +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.codegen.LRG; +import com.seaofnodes.simple.codegen.RegMask; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.node.MachConcreteNode; +import com.seaofnodes.simple.type.TypeInteger; +import java.io.ByteArrayOutputStream; + +public class NotRISC extends MachConcreteNode implements MachNode { + NotRISC(NotNode not) {super(not);} + @Override public RegMask regmap(int i) { return riscv.RMASK; } + @Override public RegMask outregmap() { return riscv.RMASK; } + + @Override public int twoAddress( ) { return 0; } + + @Override public int encoding(ByteArrayOutputStream bytes) { + throw Utils.TODO(); + } + + @Override public void asm(CodeGen code, SB sb) { sb.p(code.reg(this)); } + @Override public String op() { return "not"; } +} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/OrIRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/OrIRISC.java index f3f79ed1..f41d7162 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/OrIRISC.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/OrIRISC.java @@ -1,43 +1,11 @@ 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); - } +public class OrIRISC extends ImmRISC { + OrIRISC( Node and, int imm) { super(and,imm); } + @Override int opcode() { throw Utils.TODO(); } + @Override public String glabel() { return "|"; } @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 index 8f1e9305..7f808f90 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/OrRISC.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/OrRISC.java @@ -1,14 +1,12 @@ 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.codegen.CodeGen; +import com.seaofnodes.simple.codegen.RegMask; +import com.seaofnodes.simple.node.MachConcreteNode; +import com.seaofnodes.simple.node.MachNode; import com.seaofnodes.simple.node.Node; -import com.seaofnodes.simple.node.cpus.x86_64_v2.x86_64_v2; import java.io.ByteArrayOutputStream; public class OrRISC extends MachConcreteNode implements MachNode { @@ -24,10 +22,7 @@ public class OrRISC extends MachConcreteNode implements MachNode { } // 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; } + @Override public RegMask outregmap() { return riscv.WMASK; } // Encoding is appended into the byte array; size is returned @Override public int encoding(ByteArrayOutputStream bytes) { @@ -37,7 +32,8 @@ public class OrRISC extends MachConcreteNode implements MachNode { // 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))); + sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" | ").p(code.reg(in(2))); } + @Override public String op() { return "or"; } } 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 index 2e20f96f..75674f37 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/ParmRISC.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/ParmRISC.java @@ -1,22 +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.ParmNode; +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.codegen.RegMask; import com.seaofnodes.simple.node.MachNode; +import com.seaofnodes.simple.node.ParmNode; import java.io.ByteArrayOutputStream; -public class ParmRISC extends ParmNode implements MachNode{ +public class ParmRISC extends ParmNode implements MachNode { final RegMask _rmask; ParmRISC(ParmNode parm) { super(parm); // Assume int - _rmask = riscv.callInMaskInt(_idx); + _rmask = riscv.callInMask(fun().sig(),_idx); } // Register mask allowed on input i. 0 for no register. - @Override public RegMask regmap(int i) { return null; } + @Override public RegMask regmap(int i) { return _rmask; } // Register mask allowed as a result. Calling convention register @Override public RegMask outregmap() { return _rmask; } 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 index a1312c4c..42d5dc9b 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/ProjRISC.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/ProjRISC.java @@ -1,20 +1,22 @@ 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.codegen.CodeGen; +import com.seaofnodes.simple.codegen.RegMask; import com.seaofnodes.simple.node.MachNode; +import com.seaofnodes.simple.node.ProjNode; import java.io.ByteArrayOutputStream; -public class ProjRISC extends ProjNode implements MachNode{ - ProjRISC(ProjNode p) {super(p);} +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; } + // Register mask allowed as a result. + @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) { diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/RetRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/RetRISC.java index c92e6989..c9fa7fac 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/RetRISC.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/RetRISC.java @@ -1,26 +1,21 @@ package com.seaofnodes.simple.node.cpus.riscv; import com.seaofnodes.simple.*; +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.codegen.RegMask; import com.seaofnodes.simple.node.FunNode; import com.seaofnodes.simple.node.ReturnNode; import com.seaofnodes.simple.node.MachNode; +import com.seaofnodes.simple.type.TypeFloat; 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(); - }; - } + @Override public RegMask regmap(int i) { return riscv.retMask(_fun.sig(),i); } // Register mask allowed as a result. 0 for no register. @Override public RegMask outregmap() { return null; } @@ -29,12 +24,17 @@ public class RetRISC extends ReturnNode implements MachNode{ 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))); + // Post code-gen, just print the "ret" + if( code._phase.ordinal() <= CodeGen.Phase.RegAlloc.ordinal() ) + // Prints return reg (either A0 or FA0), RPC (always R1) and then + // the callee-save registers. + for( int i=2; i> #"); - _ti.print(sb); - } +import com.seaofnodes.simple.Utils; +import com.seaofnodes.simple.node.Node; +public class SraIRISC extends ImmRISC { + SraIRISC( Node and, int imm) { super(and,imm); } + @Override int opcode() { throw Utils.TODO(); } + @Override public String glabel() { return ">>"; } @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 index 2d50ceaa..0726e292 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SraRISC.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SraRISC.java @@ -1,14 +1,13 @@ 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.codegen.CodeGen; +import com.seaofnodes.simple.codegen.RegMask; import com.seaofnodes.simple.node.MachConcreteNode; import com.seaofnodes.simple.node.MachNode; import com.seaofnodes.simple.node.Node; import com.seaofnodes.simple.type.TypeInteger; - import java.io.ByteArrayOutputStream; // Right Shift Arithmetic @@ -22,10 +21,7 @@ public class SraRISC extends MachConcreteNode implements MachNode { // 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; } + @Override public RegMask outregmap() { return riscv.WMASK; } // Encoding is appended into the byte array; size is returned @Override public int encoding(ByteArrayOutputStream bytes) { @@ -35,7 +31,7 @@ public class SraRISC extends MachConcreteNode implements MachNode { // 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))); + sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" >> ").p(code.reg(in(2))); } @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 index f7ad6c25..a4eb99bc 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SrlIRISC.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SrlIRISC.java @@ -1,43 +1,11 @@ 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); - } +import com.seaofnodes.simple.Utils; +import com.seaofnodes.simple.node.Node; +public class SrlIRISC extends ImmRISC { + SrlIRISC( Node and, int imm) { super(and,imm); } + @Override int opcode() { throw Utils.TODO(); } + @Override public String glabel() { return ">>>"; } @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 index 25d62688..d1435540 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SrlRISC.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SrlRISC.java @@ -1,6 +1,8 @@ package com.seaofnodes.simple.node.cpus.riscv; import com.seaofnodes.simple.*; +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.codegen.RegMask; import com.seaofnodes.simple.node.*; import com.seaofnodes.simple.type.Type; import com.seaofnodes.simple.type.TypeInteger; @@ -18,10 +20,7 @@ public class SrlRISC extends MachConcreteNode implements MachNode { // 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; } + @Override public RegMask outregmap() { return riscv.WMASK; } // Encoding is appended into the byte array; size is returned @Override public int encoding(ByteArrayOutputStream bytes) { @@ -31,7 +30,8 @@ public class SrlRISC extends MachConcreteNode implements MachNode { // 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))); + + sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" >>> ").p(code.reg(in(2))); } @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 index 1bd40d0c..22e443fd 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/StoreRISC.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/StoreRISC.java @@ -1,26 +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.codegen.CodeGen; +import com.seaofnodes.simple.codegen.RegMask; import com.seaofnodes.simple.node.MachConcreteNode; import com.seaofnodes.simple.node.MachNode; import com.seaofnodes.simple.node.Node; import com.seaofnodes.simple.node.StoreNode; - import java.io.ByteArrayOutputStream; import java.util.BitSet; +// Store memory addressing on ARM +// Support imm, reg(direct), or reg+off(indirect) addressing +// Base = base - base pointer, offset is added to base +// idx = null +// off = off - offset added to base) +// imm = imm - immediate value to store +// val = Node of immediate value to store(null if its a constant immediate) + +//e.g s.cs[0] = 67; // C +// base = s.cs, off = 4, imm = 67, val = null // sw rs2,offset(rs1) public class StoreRISC extends MemOpRISC { - StoreRISC( StoreNode st) { - super(st, st); + StoreRISC( StoreNode st, Node base, Node idx, int off, int imm, Node val ) { + super(st, base, idx, off, imm, val); } + // Wider mask to store both GPRs and FPRs @Override public RegMask regmap(int i) { - return i>=2 && i<=4 ? riscv.RMASK : null; + if( i==1 ) return riscv.MEM_MASK; + if( i==2 ) return riscv.RMASK; + if( i==3 ) return riscv.RMASK; + if( i==4 ) return riscv.RMASK; + return null; // Trailing anti-dependence edges } @@ -33,7 +47,9 @@ public class StoreRISC extends MemOpRISC { } @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())); } @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 index e6a0b8e4..e6fe3012 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SubFRISC.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SubFRISC.java @@ -1,6 +1,8 @@ package com.seaofnodes.simple.node.cpus.riscv; import com.seaofnodes.simple.*; +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.codegen.RegMask; import com.seaofnodes.simple.node.*; import com.seaofnodes.simple.type.TypeInteger; import java.io.ByteArrayOutputStream; @@ -12,8 +14,6 @@ public class SubFRISC extends MachConcreteNode implements MachNode{ @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) { diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SubRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SubRISC.java index 6fb0e0ef..c37aea98 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SubRISC.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SubRISC.java @@ -1,6 +1,8 @@ package com.seaofnodes.simple.node.cpus.riscv; import com.seaofnodes.simple.*; +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.codegen.RegMask; import com.seaofnodes.simple.node.*; import com.seaofnodes.simple.type.TypeInteger; import java.io.ByteArrayOutputStream; @@ -11,9 +13,7 @@ public class SubRISC extends MachConcreteNode implements MachNode { // 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; } + @Override public RegMask outregmap() { return riscv.WMASK; } // Encoding is appended into the byte array; size is returned @Override public int encoding(ByteArrayOutputStream bytes) { diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/TFPRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/TFPRISC.java index b508541a..23fb6a74 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/TFPRISC.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/TFPRISC.java @@ -1,22 +1,24 @@ package com.seaofnodes.simple.node.cpus.riscv; - import com.seaofnodes.simple.*; +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.codegen.RegMask; 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);} + @Override public String op() { return _con == Type.NIL ? "xor" : "ldx"; } // 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; } + @Override public RegMask outregmap() { return riscv.WMASK; } + + @Override public boolean isClone() { return true; } + @Override public TFPRISC copy() { return new TFPRISC(this); } // Encoding is appended into the byte array; size is returned @Override public int encoding(ByteArrayOutputStream bytes) { @@ -34,10 +36,4 @@ public class TFPRISC extends ConstantNode implements MachNode{ 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 index 3d53ee64..a2e32f06 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/XorIRISC.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/XorIRISC.java @@ -1,45 +1,11 @@ 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); - } +public class XorIRISC extends ImmRISC { + XorIRISC( Node and, int imm) { super(and,imm); } + @Override int opcode() { throw Utils.TODO(); } + @Override public String glabel() { return "^"; } @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 index 2f34529e..f4ecb430 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/XorRISC.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/XorRISC.java @@ -1,14 +1,13 @@ 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.codegen.CodeGen; +import com.seaofnodes.simple.codegen.RegMask; import com.seaofnodes.simple.node.MachConcreteNode; import com.seaofnodes.simple.node.MachNode; import com.seaofnodes.simple.node.Node; import com.seaofnodes.simple.type.TypeInteger; - import java.io.ByteArrayOutputStream; public class XorRISC extends MachConcreteNode implements MachNode { @@ -25,10 +24,7 @@ public class XorRISC extends MachConcreteNode implements MachNode { } // 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; } + @Override public RegMask outregmap() { return riscv.WMASK; } // Encoding is appended into the byte array; size is returned @Override public int encoding(ByteArrayOutputStream bytes) { @@ -38,6 +34,8 @@ public class XorRISC extends MachConcreteNode implements MachNode { // 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))); + + sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" ^ ").p(code.reg(in(2))); + } } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/riscv.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/riscv.java index ad6e1c0f..0542085c 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/riscv.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/riscv.java @@ -1,63 +1,84 @@ 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.codegen.LRG; +import com.seaofnodes.simple.codegen.Machine; +import com.seaofnodes.simple.codegen.RegMask; import com.seaofnodes.simple.node.*; -import com.seaofnodes.simple.node.cpus.x86_64_v2.*; import com.seaofnodes.simple.type.*; -public class riscv extends Machine{ +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; + static final int RPC= 1, SP = 2, GP = 3, TP = 4, T0 = 5, T1 = 6, T2 = 7; + static final int S0 = 8, S1 = 9, A0 = 10, A1 = 11, A2 = 12, A3 = 13, A4 = 14, A5 = 15; + static final int A6 = 16, A7 = 17, S2 = 18, S3 = 19, S4 = 20, S5 = 21, S6 = 22, S7 = 23; + static final 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; + static final int F0 = 32, F1 = 33, F2 = 34, F3 = 35, F4 = 36, F5 = 37, F6 = 38, F7 = 39; + static final int FS0 = 40, FS1 = 41, FA0 = 42, FA1 = 43, FA2 = 44, FA3 = 45, FA4 = 46, FA5 = 47; + static final int FA6 = 48, FA7 = 49, FS2 = 50, FS3 = 51, FS4 = 52, FS5 = 53, FS6 = 54, FS7 = 55; + static final int FS8 = 56, FS9 = 57, FS10 = 58, FS11 = 59, FT8 = 60, FT9 = 61, FT10 = 62, FT11 = 63; + + static final int MAX_REG = 61; + + static final String[] REGS = new String[] { + "zero", "r1" , "rsp" , "r3" , "r4" , "r5" , "r6" , "r7" , + "r8" , "r9" , "r10" , "r11" , "r12" , "r13" , "r14" , "r15" , + "r16" , "r17" , "r18" , "r19" , "r20" , "r21" , "r22" , "r23" , + "r24" , "r25" , "r26" , "r27" , "r28" , "r29" , "r30" , "r31" , + "f0" , "f1" , "f2" , "f3" , "f4" , "f5" , "f6" , "f7" , + "fs0" , "fs1" , "fa0" , "fa1" , "fa2" , "fa3" , "fa4" , "fa5" , + "fa6" , "fa7" , "fs2" , "fs3" , "fs4" , "fs5" , "fs6" , "fs7" , + "fs8" , "fs9" , "fs10", "fs11", "ft8" , "ft9" , "ft10", "ft11" + }; + @Override public String reg( int reg ) { + return reg < REGS.length ? REGS[reg] : "[rsp+"+(reg-REGS.length)*4+"]"; + } - 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); + static final long RD_BITS = 0b11111111111111111111111111111111L; // All the GPRs + static final RegMask RMASK = new RegMask(RD_BITS); + + static final long WR_BITS = 0b11111111111111111111111111111010L; // All the GPRs, minus ZERO and SP + static final RegMask WMASK = new RegMask(WR_BITS); + // Float mask from(f0-ft10). TODO: ft10,ft11 needs a larger RegMask + static final long FP_BITS = 0b11111111111111111111111111111111L<>52) == ti.value(); + } + + @Override public Node instSelect( Node n ) { return switch (n) { - case AddFNode addf -> addf(addf); - case AddNode add -> add(add); - case AndNode and -> and(and); - case BoolNode bool -> cmp(bool); - case CallEndNode cend -> new CallEndNode((CallNode) cend.in(0)); - case CallNode call -> call(call); - case CProjNode c -> new CProjNode(c); - case ConstantNode con -> con(con); - case DivFNode divf -> new DivFRISC(divf); - case DivNode div -> new DivRISC(div); - case FunNode fun -> new FunRISC(fun); - case IfNode iff -> jmp(iff); - case LoadNode ld -> ld(ld); - case MemMergeNode mem -> new MemMergeNode(mem); - case MulFNode mulf -> new MulFRISC(mulf); - case MulNode mul -> mul(mul); - case NewNode nnn -> new NewRISC(nnn); - case OrNode or -> or(or); - case ParmNode parm -> new ParmRISC(parm); - case BulkMemPhiNode phi -> new BulkMemPhiNode(phi); - case MemPhiNode phi -> new MemPhiNode(phi); - case PhiNode phi -> new PhiNode(phi); - case ProjNode prj -> prj(prj); - case ReadOnlyNode read -> new ReadOnlyNode(read); - case ReturnNode ret -> new RetRISC(ret, ret.fun()); - case SarNode sar -> sra(sar); - case ShlNode shl -> sll(shl); - case ShrNode shr -> srl(shr); - case StartNode start -> new StartNode(start); - case StopNode stop -> new StopNode(stop); - case StoreNode st -> st(st); - case SubFNode subf -> new SubFRISC(subf); - case SubNode sub -> sub(sub); - case ToFloatNode tfn -> i2f8(tfn); + case AddFNode addf -> addf(addf); + case AddNode add -> add(add); + case AndNode and -> and(and); + case BoolNode bool -> cmp(bool); + case CallNode call -> call(call); + case CastNode cast -> new CastRISC(cast); + case CallEndNode cend -> new CallEndRISC(cend); + 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 -> new MulRISC(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 XorNode xor -> xor(xor); - case LoopNode loop -> new LoopNode(loop); - case RegionNode region -> new RegionNode(region); - default -> throw Utils.TODO(); + case LoopNode loop -> new LoopNode(loop); + case RegionNode region -> new RegionNode(region); + default -> throw Utils.TODO(); }; } @@ -216,94 +235,86 @@ public class riscv extends Machine{ } 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); - } + if( add.in(2) instanceof ConstantNode off && off._con instanceof TypeInteger ti && imm12(ti) ) + return new AddIRISC(add, (int)ti.value()); 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); + if( and.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti && imm12(ti) ) + return new AndIRISC(and, (int)ti.value()); 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 cmp(BoolNode bool) { + if( bool.in(2) instanceof ConstantNode off && off._con instanceof TypeInteger toff ) + return new SetIRISC(bool, (int)toff.value()); + // Float variant + if( bool.isFloat() ) + return new SetFRISC(bool); + return new SetRISC(bool); } private Node con( ConstantNode con ) { + if( !con._con.isConstant() ) return new ConstantNode( con ); // Default unknown caller inputs 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); + case TypeInteger ti -> new IntRISC(con); + case TypeFloat tf -> new FltRISC(con); + case TypeFunPtr tfp -> new TFPRISC(con); + case TypeMemPtr tmp -> throw Utils.TODO(); + case TypeNil tn -> throw Utils.TODO(); + // TOP, BOTTOM, XCtrl, Ctrl, etc. Never any executable code. + case Type t -> t==Type.NIL ? new IntRISC(con) : 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()); + if( iff.in(1) instanceof BoolNode bool && !bool.isFloat() ) + return new BranchRISC(iff, bool.op(), bool.in(1), bool.in(2)); + // Vs zero + return new BranchRISC(iff, "==", iff.in(1), null); } private Node or(OrNode or) { - if(or.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti) - return new OrIRISC(or, ti); - + if( or.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti && imm12(ti) ) + return new OrIRISC(or, (int)ti.value()); 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); + if( xor.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti && imm12(ti) ) + return new XorIRISC(xor, (int)ti.value()); 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); + if( sar.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti && imm12(ti) ) + return new SraIRISC(sar, (int)ti.value()); 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); + if( shr.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti && imm12(ti) ) + return new SrlIRISC(shr, (int)ti.value()); 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); + if( sll.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti && imm12(ti) ) + return new SllIRISC(sll, (int)ti.value()); 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); + return sub.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti && imm12(ti) + ? new AddIRISC(sub, (int)(-ti.value())) + : new SubRISC(sub); } private Node i2f8(ToFloatNode tfn) { @@ -316,17 +327,38 @@ public class riscv extends Machine{ } private Node ld(LoadNode ld) { - return new LoadRISC(ld); + return new LoadRISC(address(ld),ld.ptr(),idx,off); } private Node st(StoreNode st) { - return new StoreRISC(st); + int imm=0; + Node xval = st.val(); + if( xval instanceof ConstantNode con && con._con instanceof TypeInteger ti && imm12(ti) ) { + xval = null; + imm = (int)ti.value(); + assert imm == ti.value(); // In 32-bit range + } + return new StoreRISC(address(st),st.ptr(),idx,off,imm,xval); } - private Node mul(MulNode mul) { - return mul.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti - ? new MulIRISC(mul, ti) - : new MulRISC(mul); + // Gather 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; + private static Node idx; + private N address( N mop ) { + off = 0; // Reset + idx = 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 ConstantNode con && con._con instanceof TypeInteger ti && imm12(ti) ) { + off = (int)ti.value(); + } else { + idx = mop.off(); + } + return mop; } -} +} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddFMemX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddFMemX86.java index 4bb24fa3..ffc6966e 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddFMemX86.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddFMemX86.java @@ -1,9 +1,9 @@ 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.Utils; +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.codegen.RegMask; import com.seaofnodes.simple.node.*; public class AddFMemX86 extends MemOpX86 { @@ -12,10 +12,10 @@ public class AddFMemX86 extends MemOpX86 { } @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 + 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(); } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddFX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddFX86.java index a3f5c7b3..d4ea9162 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddFX86.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddFX86.java @@ -1,6 +1,8 @@ package com.seaofnodes.simple.node.cpus.x86_64_v2; import com.seaofnodes.simple.*; +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.codegen.RegMask; import com.seaofnodes.simple.node.*; import com.seaofnodes.simple.type.TypeInteger; import java.io.ByteArrayOutputStream; @@ -14,6 +16,8 @@ public class AddFX86 extends MachConcreteNode implements MachNode { @Override public RegMask outregmap() { return x86_64_v2.XMASK; } // Output is same register as input#1 @Override public int twoAddress() { return 1; } + // Ok to swap arguments + @Override public boolean commutes() { return true; } // Encoding is appended into the byte array; size is returned @Override public int encoding(ByteArrayOutputStream bytes) { diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddIX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddIX86.java index 6a332ff5..c7f92ff1 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddIX86.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddIX86.java @@ -1,6 +1,8 @@ package com.seaofnodes.simple.node.cpus.x86_64_v2; import com.seaofnodes.simple.*; +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.codegen.RegMask; import com.seaofnodes.simple.node.*; import com.seaofnodes.simple.type.TypeInteger; import java.io.ByteArrayOutputStream; diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddMemX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddMemX86.java index 7d45d757..3d4fac41 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddMemX86.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddMemX86.java @@ -1,8 +1,8 @@ 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.codegen.CodeGen; +import com.seaofnodes.simple.codegen.RegMask; import com.seaofnodes.simple.node.*; public class AddMemX86 extends MemOpX86 { @@ -14,7 +14,8 @@ public class AddMemX86 extends MemOpX86 { // Register mask allowed as a result. 0 for no register. @Override public RegMask outregmap() { return x86_64_v2.WMASK; } - + // Output is same register as val() + @Override public int twoAddress() { return 4; } // General form: "add dst = src + [base + idx<<2 + 12]" @Override public void asm(CodeGen code, SB 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 index 61aa5a9e..9553d499 100644 --- 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 @@ -1,11 +1,13 @@ package com.seaofnodes.simple.node.cpus.x86_64_v2; import com.seaofnodes.simple.*; +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.codegen.RegMask; import com.seaofnodes.simple.node.*; import com.seaofnodes.simple.type.TypeInteger; import java.io.ByteArrayOutputStream; -public class AddX86 extends MachConcreteNode implements MachNode { +public class AddX86 extends MachConcreteNode { AddX86( Node add ) { super(add); } // Register mask allowed on input i. @@ -14,6 +16,8 @@ public class AddX86 extends MachConcreteNode implements MachNode { @Override public RegMask outregmap() { return x86_64_v2.WMASK; } // Output is same register as input#1 @Override public int twoAddress() { return 1; } + // Ok to swap arguments + @Override public boolean commutes() { return true; } // Encoding is appended into the byte array; size is returned @Override public int encoding(ByteArrayOutputStream bytes) { 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 index f2ee1c66..a7f1801e 100644 --- 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 @@ -1,6 +1,8 @@ package com.seaofnodes.simple.node.cpus.x86_64_v2; import com.seaofnodes.simple.*; +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.codegen.RegMask; import com.seaofnodes.simple.node.*; import com.seaofnodes.simple.type.Type; import com.seaofnodes.simple.type.TypeInteger; 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 index 45cf2b22..d7fb3613 100644 --- 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 @@ -1,6 +1,8 @@ package com.seaofnodes.simple.node.cpus.x86_64_v2; import com.seaofnodes.simple.*; +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.codegen.RegMask; import com.seaofnodes.simple.node.*; import java.io.ByteArrayOutputStream; @@ -23,11 +25,10 @@ public class AndX86 extends MachConcreteNode implements MachNode{ } // General form - // General form: "and dst & #imm" + // General form: "and dst & src" @Override public void asm(CodeGen code, SB sb) { - sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))); + sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" & ").p(code.reg(in(2)));; } @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 index 55ab90c9..b9cc5659 100644 --- 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 @@ -2,8 +2,8 @@ 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.codegen.CodeGen; +import com.seaofnodes.simple.codegen.RegMask; import com.seaofnodes.simple.node.CallEndNode; import com.seaofnodes.simple.node.MachNode; import com.seaofnodes.simple.type.TypeFloat; @@ -14,16 +14,14 @@ public class CallEndX86 extends CallEndNode implements MachNode { final TypeFunPtr _tfp; CallEndX86( CallEndNode cend ) { super(cend); - _tfp = (TypeFunPtr)cend.call().fptr()._type; + _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; - } + @Override public RegMask outregmap() { return null; } + @Override public RegMask outregmap(int idx) { return idx == 2 ? x86_64_v2.retMask(_tfp,2) : null; } + @Override public RegMask killmap() { return x86_64_v2.x86CallerSave(); } // Encoding is appended into the byte array; size is returned @Override public int encoding(ByteArrayOutputStream bytes) { diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CallRX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CallRX86.java index 99260a27..ac35d17b 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CallRX86.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CallRX86.java @@ -1,9 +1,11 @@ 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 com.seaofnodes.simple.SB; +import com.seaofnodes.simple.Utils; +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.codegen.RegMask; +import com.seaofnodes.simple.node.CallNode; +import com.seaofnodes.simple.node.MachNode; import java.io.ByteArrayOutputStream; public class CallRX86 extends CallNode implements MachNode { @@ -15,17 +17,24 @@ public class CallRX86 extends CallNode implements MachNode { ? 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; } + @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(); + bytes.write(0xe8); + int beforeSize = bytes.size(); + // address + //TODO: relocs + bytes.write(0x00); + return bytes.size() - beforeSize; } @Override public void asm(CodeGen code, SB sb) { - sb.p(code.reg(fptr())); + sb.p(code.reg(fptr())).p(" "); + for( int i=0; i _printMach(ExprPrinter p) { return p.p(".").p(_name); } + @Override protected ExprPrinter _printMach(ExprPrinter p) { + return p.p(".").p(_name); + } @Override public boolean isMem() { return _type instanceof TypeMem; } @@ -62,14 +66,12 @@ public abstract class MemOpX86 extends MemOpNode implements MachNode { // 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 + if( i==1 ) return null; // Memory + if( i==2 ) return x86_64_v2.RMASK; // base in GPR + if( i==3 ) return x86_64_v2.RMASK; // index in GPR + if( i==4 ) return _sz >= '4' ? x86_64_v2.MEM_MASK : x86_64_v2.RMASK; // Narrow stores need GPRs + 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(); } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MulFX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MulFX86.java index aef813e1..9e996130 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MulFX86.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MulFX86.java @@ -1,6 +1,8 @@ package com.seaofnodes.simple.node.cpus.x86_64_v2; import com.seaofnodes.simple.*; +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.codegen.RegMask; import com.seaofnodes.simple.node.*; import com.seaofnodes.simple.type.TypeFloat; import com.seaofnodes.simple.type.TypeInteger; @@ -15,6 +17,8 @@ public class MulFX86 extends MachConcreteNode implements MachNode { @Override public RegMask outregmap() { return x86_64_v2.XMASK; } // Output is same register as input#1 @Override public int twoAddress() { return 1; } + // Ok to swap arguments + @Override public boolean commutes() { return true; } // Encoding is appended into the byte array; size is returned @Override public int encoding(ByteArrayOutputStream bytes) { diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MulIX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MulIX86.java index e9eb17fc..b41a5720 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MulIX86.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MulIX86.java @@ -1,7 +1,12 @@ package com.seaofnodes.simple.node.cpus.x86_64_v2; -import com.seaofnodes.simple.*; -import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.SB; +import com.seaofnodes.simple.Utils; +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.codegen.RegMask; +import com.seaofnodes.simple.node.MachConcreteNode; +import com.seaofnodes.simple.node.MachNode; +import com.seaofnodes.simple.node.Node; import com.seaofnodes.simple.type.TypeInteger; import java.io.ByteArrayOutputStream; @@ -12,7 +17,7 @@ public class MulIX86 extends MachConcreteNode implements MachNode { // Register mask allowed on input i. // This is the normal calling convention @Override public RegMask regmap(int i) { - assert i==1; + // 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; } diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MulX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MulX86.java index 15fd9496..d880208e 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MulX86.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MulX86.java @@ -1,12 +1,13 @@ package com.seaofnodes.simple.node.cpus.x86_64_v2; import com.seaofnodes.simple.*; +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.codegen.RegMask; 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. @@ -15,6 +16,8 @@ public class MulX86 extends MachConcreteNode implements MachNode { @Override public RegMask outregmap() { return x86_64_v2.WMASK; } // Output is same register as input#1 @Override public int twoAddress() { return 1; } + // Ok to swap arguments + @Override public boolean commutes() { return true; } // Encoding is appended into the byte array; size is returned @Override public int encoding(ByteArrayOutputStream bytes) { diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/NewX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/NewX86.java index c70e1c54..f4803b2e 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/NewX86.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/NewX86.java @@ -1,30 +1,19 @@ package com.seaofnodes.simple.node.cpus.x86_64_v2; import com.seaofnodes.simple.*; +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.codegen.RegMask; 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; - } + NewX86( NewNode nnn ) { super(nnn); } + // Size and pointer result in standard calling convention; null for all the + // memory and ordering edges + @Override public RegMask regmap(int i) { return i == 2 ? x86_64_v2.RDI_MASK : null; } + @Override public RegMask outregmap(int i) { return i == 0 ? x86_64_v2.RAX_MASK : null; } + @Override public RegMask outregmap() { return null; } // Encoding is appended into the byte array; size is returned @Override public int encoding(ByteArrayOutputStream bytes) { diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/NotX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/NotX86.java new file mode 100644 index 00000000..a4b82de8 --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/NotX86.java @@ -0,0 +1,26 @@ +package com.seaofnodes.simple.node.cpus.x86_64_v2; + +import com.seaofnodes.simple.SB; +import com.seaofnodes.simple.Utils; +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.codegen.LRG; +import com.seaofnodes.simple.codegen.RegMask; +import com.seaofnodes.simple.node.*; +import java.io.ByteArrayOutputStream; + +public class NotX86 extends MachConcreteNode implements MachNode { + NotX86(NotNode not) {super(not);} + @Override public RegMask regmap(int i) { return x86_64_v2.RMASK; } + @Override public RegMask outregmap() { return x86_64_v2.RMASK; } + + @Override public int twoAddress( ) { return 0; } + + // NotNode(BoolNode) = Inverted BoolNode(1) + // NotNode(NotNode(BoolNode)) = BoolNode(2) + // anything else to test & setz(3) + @Override public int encoding(ByteArrayOutputStream bytes) { + throw Utils.TODO(); + } + @Override public void asm(CodeGen code, SB sb) { sb.p(code.reg(this)); } + @Override public String op() { return "not"; } +} 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 index d3940838..949ac6c2 100644 --- 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 @@ -1,14 +1,13 @@ 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.codegen.CodeGen; +import com.seaofnodes.simple.codegen.RegMask; import com.seaofnodes.simple.node.MachConcreteNode; import com.seaofnodes.simple.node.MachNode; import com.seaofnodes.simple.node.Node; import com.seaofnodes.simple.type.TypeInteger; - import java.io.ByteArrayOutputStream; public class OrIX86 extends MachConcreteNode implements MachNode { @@ -19,7 +18,7 @@ public class OrIX86 extends MachConcreteNode implements MachNode { // Register mask allowed on input i. // This is the normal calling convention @Override public RegMask regmap(int i) { - assert i==1; + //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; } 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 index 7c25bdae..6d99fe3b 100644 --- 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 @@ -1,6 +1,8 @@ package com.seaofnodes.simple.node.cpus.x86_64_v2; import com.seaofnodes.simple.*; +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.codegen.RegMask; import com.seaofnodes.simple.node.*; import java.io.ByteArrayOutputStream; @@ -24,7 +26,7 @@ public class OrX86 extends MachConcreteNode implements MachNode{ } // General form - // General form: "and dst & #imm" + // General form: "or dst | src" @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/x86_64_v2/ParmX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ParmX86.java index 0fcc922d..69ebb74e 100644 --- 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 @@ -1,23 +1,27 @@ package com.seaofnodes.simple.node.cpus.x86_64_v2; -import com.seaofnodes.simple.RegMask; -import com.seaofnodes.simple.node.ParmNode; +import com.seaofnodes.simple.SB; +import com.seaofnodes.simple.Utils; +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.codegen.RegMask; import com.seaofnodes.simple.node.MachNode; +import com.seaofnodes.simple.node.ParmNode; 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; } + @Override public RegMask regmap(int i) { return _rmask; } // 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; } + @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 index 77fd7d34..7ac014e6 100644 --- 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 @@ -1,14 +1,12 @@ 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.codegen.RegMask; import com.seaofnodes.simple.node.MachNode; +import com.seaofnodes.simple.node.ProjNode; 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. @@ -20,6 +18,6 @@ public class ProjX86 extends ProjNode implements MachNode { // Encoding is appended into the byte array; size is returned @Override public int encoding(ByteArrayOutputStream bytes) { - throw Utils.TODO(); + return 0; } } 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 index 21db209c..433f64cc 100644 --- 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 @@ -1,9 +1,12 @@ package com.seaofnodes.simple.node.cpus.x86_64_v2; import com.seaofnodes.simple.*; +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.codegen.RegMask; import com.seaofnodes.simple.node.FunNode; import com.seaofnodes.simple.node.ReturnNode; import com.seaofnodes.simple.node.MachNode; +import com.seaofnodes.simple.type.TypeFloat; import java.io.ByteArrayOutputStream; // Return @@ -19,29 +22,27 @@ public class RetX86 extends ReturnNode implements MachNode { // 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(); - }; - } + @Override public RegMask regmap(int i) { return x86_64_v2.retMask(_fun.sig(),i); } // 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(); + // Without imm form + // Near return + int beforeSize = bytes.size(); + bytes.write(0xC3); // opcode + + return bytes.size() - beforeSize; } - // 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))); + // Post code-gen, just print the "ret" + if( code._phase.ordinal() <= CodeGen.Phase.RegAlloc.ordinal() ) + // Prints return reg (either RAX or XMM0), RPC (always [rsp-4]) and + // then the callee-save registers. + for( int i=2; i> src" @Override public void asm(CodeGen code, SB sb) { - sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))); + 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 index 25367e03..53237538 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SetX86.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SetX86.java @@ -1,6 +1,8 @@ package com.seaofnodes.simple.node.cpus.x86_64_v2; import com.seaofnodes.simple.*; +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.codegen.RegMask; import com.seaofnodes.simple.node.*; import com.seaofnodes.simple.type.Type; import com.seaofnodes.simple.type.TypeInteger; @@ -31,7 +33,7 @@ public class SetX86 extends MachConcreteNode implements MachNode { @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); + 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 index 61e0d262..c18007cd 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShlIX86.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShlIX86.java @@ -1,6 +1,8 @@ package com.seaofnodes.simple.node.cpus.x86_64_v2; import com.seaofnodes.simple.*; +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.codegen.RegMask; import com.seaofnodes.simple.node.*; import com.seaofnodes.simple.type.Type; import com.seaofnodes.simple.type.TypeInteger; diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShlX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShlX86.java index b95f9f46..a3de5347 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShlX86.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShlX86.java @@ -1,13 +1,13 @@ package com.seaofnodes.simple.node.cpus.x86_64_v2; import com.seaofnodes.simple.*; +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.codegen.RegMask; import com.seaofnodes.simple.node.*; import java.io.ByteArrayOutputStream; public class ShlX86 extends MachConcreteNode implements MachNode { - ShlX86(Node shl) { - super(shl); - }; + ShlX86(Node shl) { super(shl); }; // Register mask allowed on input i. // This is the normal calling convention @@ -32,7 +32,7 @@ public class ShlX86 extends MachConcreteNode implements MachNode { // 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))); + 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 index 80115ff2..d17ee734 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShrIX86.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShrIX86.java @@ -1,6 +1,8 @@ package com.seaofnodes.simple.node.cpus.x86_64_v2; import com.seaofnodes.simple.*; +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.codegen.RegMask; import com.seaofnodes.simple.node.*; import com.seaofnodes.simple.type.Type; import com.seaofnodes.simple.type.TypeInteger; @@ -8,7 +10,6 @@ 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;} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShrX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShrX86.java index 43fc730b..757d9b8e 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShrX86.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShrX86.java @@ -1,6 +1,8 @@ package com.seaofnodes.simple.node.cpus.x86_64_v2; import com.seaofnodes.simple.*; +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.codegen.RegMask; import com.seaofnodes.simple.node.*; import java.io.ByteArrayOutputStream; @@ -31,7 +33,7 @@ public class ShrX86 extends MachConcreteNode implements MachNode { // 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))); + 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/SplitX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SplitX86.java new file mode 100644 index 00000000..ea283a87 --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SplitX86.java @@ -0,0 +1,21 @@ +package com.seaofnodes.simple.node.cpus.x86_64_v2; + +import com.seaofnodes.simple.Utils; +import com.seaofnodes.simple.codegen.RegMask; +import com.seaofnodes.simple.node.Node; +import com.seaofnodes.simple.node.SplitNode; +import java.io.ByteArrayOutputStream; + +public class SplitX86 extends SplitNode { + SplitX86( String kind, byte round ) { super(kind,round, new Node[2]); } + + // Register mask allowed on input i. + @Override public RegMask regmap(int i) { return x86_64_v2.SPLIT_MASK; } + // Register mask allowed as a result. 0 for no register. + @Override public RegMask outregmap() { return x86_64_v2.SPLIT_MASK; } + + // 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/StoreX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/StoreX86.java index 76bd2afb..08d481fa 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/StoreX86.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/StoreX86.java @@ -2,9 +2,9 @@ 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.codegen.CodeGen; +import com.seaofnodes.simple.codegen.RegMask; import com.seaofnodes.simple.node.Node; import com.seaofnodes.simple.node.StoreNode; @@ -16,7 +16,11 @@ public class StoreX86 extends MemOpX86 { @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(";"); + Node val = val(); + p.p(".").p(_name).p("="); + if( val==null ) p.p(_imm); + else p.n(val); + return p.p(";"); } // Register mask allowed as a result. 0 for no register. diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SubFX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SubFX86.java index ed19ad79..ceaf0636 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SubFX86.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SubFX86.java @@ -1,6 +1,8 @@ package com.seaofnodes.simple.node.cpus.x86_64_v2; import com.seaofnodes.simple.*; +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.codegen.RegMask; import com.seaofnodes.simple.node.*; import java.io.ByteArrayOutputStream; 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 index cac59d3b..da44a838 100644 --- 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 @@ -1,6 +1,8 @@ package com.seaofnodes.simple.node.cpus.x86_64_v2; import com.seaofnodes.simple.*; +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.codegen.RegMask; import com.seaofnodes.simple.node.*; import com.seaofnodes.simple.type.TypeInteger; import java.io.ByteArrayOutputStream; diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/TFPX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/TFPX86.java index c7edfcc4..2b4de2ca 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/TFPX86.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/TFPX86.java @@ -1,6 +1,8 @@ package com.seaofnodes.simple.node.cpus.x86_64_v2; import com.seaofnodes.simple.*; +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.codegen.RegMask; import com.seaofnodes.simple.node.ConstantNode; import com.seaofnodes.simple.node.MachNode; import com.seaofnodes.simple.type.Type; @@ -11,10 +13,13 @@ import java.io.ByteArrayOutputStream; // Function constants public class TFPX86 extends ConstantNode implements MachNode { TFPX86( ConstantNode con ) { super(con); } + @Override public String op() { return _con == Type.NIL ? "xor" : "ldx"; } // 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; } + @Override public boolean isClone() { return true; } + @Override public TFPX86 copy() { return new TFPX86(this); } // Encoding is appended into the byte array; size is returned @Override public int encoding(ByteArrayOutputStream bytes) { @@ -32,10 +37,4 @@ public class TFPX86 extends ConstantNode implements MachNode { 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 index d71a49dc..47c6be73 100644 --- 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 @@ -1,17 +1,16 @@ 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.codegen.CodeGen; +import com.seaofnodes.simple.codegen.RegMask; import com.seaofnodes.simple.node.MachConcreteNode; import com.seaofnodes.simple.node.MachNode; import com.seaofnodes.simple.node.Node; import com.seaofnodes.simple.type.TypeInteger; - import java.io.ByteArrayOutputStream; -public class XorIX86 extends MachConcreteNode implements MachNode { +public class XorIX86 extends MachConcreteNode implements MachNode { final TypeInteger _ti; XorIX86(Node xor, TypeInteger ti) {super(xor); _inputs.pop(); _ti = ti;} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/XorX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/XorX86.java new file mode 100644 index 00000000..3322d951 --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/XorX86.java @@ -0,0 +1,36 @@ +package com.seaofnodes.simple.node.cpus.x86_64_v2; + +import com.seaofnodes.simple.SB; +import com.seaofnodes.simple.Utils; +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.codegen.LRG; +import com.seaofnodes.simple.codegen.RegMask; +import com.seaofnodes.simple.node.MachConcreteNode; +import com.seaofnodes.simple.node.MachNode; +import com.seaofnodes.simple.node.Node; +import com.seaofnodes.simple.type.TypeInteger; +import java.io.ByteArrayOutputStream; + +public class XorX86 extends MachConcreteNode implements MachNode{ + XorX86(Node xor) {super(xor);} + + @Override public RegMask regmap(int i) { 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: "xor ^ reg + @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 "xor"; } + +} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/x86_64_v2.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/x86_64_v2.java index e7e0b874..c66aed9f 100644 --- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/x86_64_v2.java +++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/x86_64_v2.java @@ -1,9 +1,10 @@ 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.codegen.CodeGen; +import com.seaofnodes.simple.codegen.LRG; +import com.seaofnodes.simple.codegen.Machine; +import com.seaofnodes.simple.codegen.RegMask; import com.seaofnodes.simple.node.*; import com.seaofnodes.simple.type.*; @@ -11,119 +12,66 @@ 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; + static final int RAX = 0, RCX = 1, RDX = 2, RBX = 3, RSP = 4, RBP = 5, RSI = 6, RDI = 7; + static final int R08 = 8, R09 = 9, R10 = 10, R11 = 11, R12 = 12, R13 = 13, R14 = 14, R15 = 15; - 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; + static final int XMM0 = 16, XMM1 = 17, XMM2 = 18, XMM3 = 19, XMM4 = 20, XMM5 = 21, XMM6 = 22, XMM7 = 23; + static final int XMM8 = 24, XMM9 = 25, XMM10 = 26, XMM11 = 27, XMM12 = 28, XMM13 = 29, XMM14 = 30, XMM15 = 31; + static final int FLAGS = 32; + static final int MAX_REG = 33; + static final int RPC = 33; // General purpose register mask: pointers and ints, not floats - public static RegMask RMASK = new RegMask(0b1111111111111111); + static final long RD_BITS = 0b1111111111111111L; // All the GPRs + static RegMask RMASK = new RegMask(RD_BITS); // No RSP in the *write* general set. - public static RegMask WMASK = new RegMask(0b1111111111101111); + static final long WR_BITS = 0b1111111111101111L; // All the GPRs minus RSP + static RegMask WMASK = new RegMask(WR_BITS); // 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 + static RegMask[] CALLINMASK_WIN64 = new RegMask[] { 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 + // SystemV(param passing) + static RegMask[] CALLINMASK_SYSTEMV = new RegMask[] { RDI_MASK, RSI_MASK, RDX_MASK, @@ -131,32 +79,128 @@ public class x86_64_v2 extends Machine { R08_MASK, R09_MASK, }; + + // Limit of float args passed in registers + static RegMask[] XMMS4 = new RegMask[]{ + new RegMask(XMM0), new RegMask(XMM1), new RegMask(XMM2), new RegMask(XMM3), + }; + + // Map from function signature and argument index to register. + // Used to set input registers to CallNodes, and ParmNode outputs. + static RegMask callInMask( TypeFunPtr tfp, int idx ) { + if( idx==0 ) return RPC_MASK; + if( idx==1 ) return null; + // Count floats in signature up to index + int fcnt=0; + for( int i=2; i CALLINMASK_SYSTEMV; + case "Win64" -> CALLINMASK_WIN64; + default -> throw new IllegalArgumentException("Unknown calling convention: "+CodeGen.CODE._callingConv); + }; + if( idx-2-fcnt < cargs.length ) + return cargs[idx-2-fcnt]; + } + throw Utils.TODO(); // Pass on stack slot + } + // caller saved(systemv) + static final long SYSTEM5_CALLER_SAVE = + (1L<< RAX) | (1L<< RCX) | (1L<< RDX) | + (1L<< RDI) | (1L<< RSI) | + (1L<< R08) | (1L<< R09) | (1L<< R10) | (1L<< R11) | + (1L< SYSTEM5_CALLER_SAVE_MASK; + case "Win64" -> WIN64_CALLER_SAVE_MASK; + default -> throw new IllegalArgumentException("Unknown calling convention: "+CodeGen.CODE._callingConv); + }; + } + @Override public RegMask callerSave() { return x86CallerSave(); } + + static final RegMask SYSTEM5_CALLEE_SAVE_MASK; + static final RegMask WIN64_CALLEE_SAVE_MASK; + static { + long callee = ~SYSTEM5_CALLER_SAVE; + // Remove the spills + callee &= (1L< SYSTEM5_CALLEE_SAVE_MASK; + case "Win64" -> WIN64_CALLEE_SAVE_MASK; + default -> throw new IllegalArgumentException("Unknown calling convention: "+CodeGen.CODE._callingConv); + }; + } + @Override public RegMask calleeSave() { return x86CalleeSave(); } - 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; + static final RegMask[] WIN64_RET_MASKS, SYS5_RET_MASKS; + static { + WIN64_RET_MASKS = makeRetMasks( WIN64_CALLEE_SAVE_MASK); + SYS5_RET_MASKS = makeRetMasks(SYSTEM5_CALLEE_SAVE_MASK); + } + private static RegMask[] makeRetMasks(RegMask mask) { + int nSaves = mask.size(); + RegMask[] masks = new RegMask[4 + nSaves]; + masks[0] = null; + masks[1] = null; // Memory + masks[2] = null; // Varies, either XMM0 or RAX + masks[3] = RPC_MASK; + short reg = mask.firstReg(); + for( int i=0; i SYS5_RET_MASKS; + case "Win64" -> WIN64_RET_MASKS; + default -> throw new IllegalArgumentException("Unknown calling convention: "+CodeGen.CODE._callingConv); }; - 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]; + return masks[i]; } // Create a split op; any register to any register, including stack slots - @Override - public Node split() { - throw Utils.TODO(); - } + @Override public SplitNode split(String kind, byte round, LRG lrg) { return new SplitX86(kind,round); } // Return a MachNode unconditional branch @Override public CFGNode jump() { @@ -177,6 +221,7 @@ public class x86_64_v2 extends Machine { case BoolNode bool -> cmp(bool); case CallEndNode cend -> new CallEndX86(cend); case CallNode call -> call(call); + case CastNode cast -> new CastX86(cast); case CProjNode c -> new CProjNode(c); case ConstantNode con -> con(con); case DivFNode divf -> new DivFX86(divf); @@ -219,11 +264,12 @@ public class x86_64_v2 extends Machine { private Node add( AddNode add ) { Node lhs = add.in(1); Node rhs = add.in(2); + // The other addend must occupy the result register, even if constant. if( lhs instanceof LoadNode ld && ld.nOuts()==1 ) - return new AddMemX86(add,address(ld),ld.ptr(),idx,off,scale, imm(rhs),val); + return new AddMemX86(add,address(ld),ld.ptr(),idx,off,scale, 0,rhs); if( rhs instanceof LoadNode ld && ld.nOuts()==1 ) - return new AddMemX86(add,address(ld),ld.ptr(),idx,off,scale, imm(lhs),val); + return new AddMemX86(add,address(ld),ld.ptr(),idx,off,scale, 0,lhs); // Attempt a full LEA-style break down. // Returns one of AddX86, AddIX86, LeaX86, or LHS @@ -288,6 +334,10 @@ public class x86_64_v2 extends Machine { return new SetX86(cmp,bool.op()); } private Node _cmp( BoolNode bool ) { + // Float variant + if( bool.isFloat() ) + return new CmpFX86(bool); + Node lhs = bool.in(1); Node rhs = bool.in(2); if( lhs instanceof LoadNode ld && ld.nOuts()==1 ) @@ -296,20 +346,23 @@ public class x86_64_v2 extends Machine { 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); + // Vs immediate + if( rhs instanceof ConstantNode con && con._con instanceof TypeInteger ti ) + return new CmpIX86(bool, ti); + // x vs y + return new CmpX86(bool); } private Node con( ConstantNode con ) { + if( !con._con.isConstant() ) return new ConstantNode( con ); // Default unknown caller inputs 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 TypeMemPtr tmp -> throw Utils.TODO(); case TypeNil tn -> throw Utils.TODO(); // TOP, BOTTOM, XCtrl, Ctrl, etc. Never any executable code. - case Type t -> new ConstantNode(con); + case Type t -> t==Type.NIL ? new IntX86(con) : new ConstantNode(con); }; } @@ -428,6 +481,7 @@ public class x86_64_v2 extends Machine { } return mop; } + private int imm( Node xval ) { assert val==null && imm==0; if( xval instanceof ConstantNode con && con._con instanceof TypeInteger ti ) { diff --git a/src/main/java/com/seaofnodes/simple/print/ASMPrinter.java b/src/main/java/com/seaofnodes/simple/print/ASMPrinter.java new file mode 100644 index 00000000..a4f768c5 --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/print/ASMPrinter.java @@ -0,0 +1,53 @@ +package com.seaofnodes.simple.print; + +import com.seaofnodes.print.AssemblyAdapter; +import com.seaofnodes.print.ASMPrinter.Data; +import com.seaofnodes.print.ASMPrinter.Pool; +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.SB; +import com.seaofnodes.simple.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( postAlloc() && n instanceof CalleeSaveNode ) return true; + if( n instanceof MemMergeNode ) return true; + return n.getClass()==ConstantNode.class; + } + @Override public boolean postAlloc() { return _code._phase.ordinal()>CodeGen.Phase.RegAlloc.ordinal(); } +} diff --git a/src/main/java/com/seaofnodes/simple/print/IRPrinter.java b/src/main/java/com/seaofnodes/simple/print/IRPrinter.java new file mode 100644 index 00000000..e4d719f4 --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/print/IRPrinter.java @@ -0,0 +1,61 @@ +package com.seaofnodes.simple.print; + +import com.seaofnodes.print.IRAdapter; +import com.seaofnodes.simple.node.*; +import java.util.ArrayList; +import com.seaofnodes.simple.SB; +import com.seaofnodes.simple.codegen.CodeGen; + +/** This chapter's read-only IR facts and the familiar debugger entry points. */ +public final class IRPrinter extends IRAdapter { + private static final com.seaofnodes.print.IRPrinter PRINT = + new com.seaofnodes.print.IRPrinter<>(new IRPrinter()); + @Override public boolean global(Node n) { return n.isConst(); } + @Override public String type(Node n) { return n._type==null ? "" : n._type.str(); } + @Override public String inputMark(Node n, Node def) { return n instanceof MemMergeNode && def instanceof MemMergeNode ? "^" : " "; } + @Override public Kind kind(Node n) { + if( n instanceof StartNode ) return Kind.START; + if( n instanceof StopNode ) return Kind.STOP; + if( n instanceof FunNode ) return Kind.FUN; + if( n instanceof LoopNode ) return Kind.LOOP; + if( n instanceof RegionNode ) return Kind.REGION; + if( n instanceof ParmNode ) return Kind.PARM; + if( n instanceof PhiNode ) return Kind.PHI; + if( n instanceof CallEndNode ) return Kind.CALL_END; + if( n instanceof CallNode ) return Kind.CALL; + if( n instanceof ReturnNode ) return Kind.RETURN; + if( n instanceof ConstantNode ) return Kind.CONSTANT; + if( n instanceof CProjNode ) return Kind.CPROJ; + if( n instanceof ProjNode ) return n instanceof CFGNode ? Kind.CPROJ : Kind.PROJ; + if( n instanceof CFGNode ) return Kind.CTRL; + if( n instanceof MultiNode ) return Kind.MULTI; + return Kind.DATA; + } + @Override public int index(Node n) { + if( n instanceof ProjNode p ) return p._idx; + if( n instanceof CProjNode p ) return p._idx; + return n._nid; + } + @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._name==null ? "" : fun._name)+" "+fun.sig().str(); + } + + public static String prettyPrint(Node n, int depth) { return PRINT.prettyPrint(n,depth); } + public static SB printLine(Node n, SB sb) { return sb.p(PRINT.line(n)); } + + public static String prettyPrint(CodeGen code) { + var units=new ArrayList>(); + var funs=new ArrayList(); + for( Node n : code._start._outputs ) if( n instanceof FunNode ) funs.add(n); + units.add(new com.seaofnodes.print.IRPrinter.Unit<>(null,null,null,funs)); + return PRINT.program(code._start,code._stop,units); + } + public static String _prettyPrint(CodeGen code) { + if( code._cfg==null ) return prettyPrint(code); + var blocks=new ArrayList(); + for( Node n : code._cfg ) blocks.add(n); + return PRINT.scheduled(blocks); + } +} diff --git a/src/main/java/com/seaofnodes/simple/print/SimpleGraphAdapter.java b/src/main/java/com/seaofnodes/simple/print/SimpleGraphAdapter.java index 1992a0c0..0357ff61 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 19's view of the IR; browser, transport and layout are shared. */ +/** Chapter 20'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 4ec55f4d..9dce0b75 100644 --- a/src/main/java/com/seaofnodes/simple/print/SimpleGraphObserver.java +++ b/src/main/java/com/seaofnodes/simple/print/SimpleGraphObserver.java @@ -2,12 +2,12 @@ package com.seaofnodes.simple.print; import com.seaofnodes.graph.GraphCapture; import com.seaofnodes.graph.GraphViewer; -import com.seaofnodes.simple.CodeGen; +import com.seaofnodes.simple.codegen.CodeGen; import com.seaofnodes.simple.node.Node; import java.util.ArrayList; import java.io.IOException; -/** Chapter 19's compilation context; capture and event bookkeeping are shared. */ +/** Chapter 20's compilation context; capture and event bookkeeping are shared. */ public class SimpleGraphObserver extends GraphCapture { private CodeGen _code; public SimpleGraphObserver() { super(new SimpleGraphAdapter()); } diff --git a/src/main/java/com/seaofnodes/simple/type/TypeFunPtr.java b/src/main/java/com/seaofnodes/simple/type/TypeFunPtr.java index 94352790..609f2443 100644 --- a/src/main/java/com/seaofnodes/simple/type/TypeFunPtr.java +++ b/src/main/java/com/seaofnodes/simple/type/TypeFunPtr.java @@ -43,7 +43,8 @@ public class TypeFunPtr extends TypeNil { public static TypeFunPtr BOT = make((byte)3,TypeTuple.BOT,Type.BOTTOM,-1); public static TypeFunPtr TEST = make((byte)2,TypeTuple.TEST,TypeInteger.BOT,1); public static TypeFunPtr TEST0 = make((byte)3,TypeTuple.TEST,TypeInteger.BOT,3); - public static void gather(ArrayList ts) { ts.add(TEST); ts.add(TEST0); ts.add(BOT); } + public static TypeFunPtr MAIN = make((byte)3,TypeTuple.MAIN,TypeInteger.BOT,-1); + public static void gather(ArrayList ts) { ts.add(TEST); ts.add(TEST0); ts.add(BOT); ts.add(MAIN); } @Override Type xmeet(Type t) { @@ -68,6 +69,7 @@ public class TypeFunPtr extends TypeNil { public long fidxs() { return _fidxs; } public Type ret() { return _ret; } public int nargs() { return _sig._types.length; } + public int fidx() { assert Long.bitCount(_fidxs)==1; return Long.numberOfTrailingZeros(_fidxs); } @Override int hash() { return Utils.fold(_sig.hashCode() ^ _ret.hashCode() ^ _fidxs ^ super.hash()); } diff --git a/src/main/java/com/seaofnodes/simple/type/TypeStruct.java b/src/main/java/com/seaofnodes/simple/type/TypeStruct.java index 89c79a1f..dc92e086 100644 --- a/src/main/java/com/seaofnodes/simple/type/TypeStruct.java +++ b/src/main/java/com/seaofnodes/simple/type/TypeStruct.java @@ -186,6 +186,7 @@ public class TypeStruct extends Type { if( this==TOP ) return sb.p("StructTop"); sb.p(_name); if( _fields == null ) return sb; // Forward reference struct, just print the name + if( isAry() ) return sb; // Skip printing generic array fields sb.p(" {"); for( Field f : _fields ) f._type.print(sb).p(f._final ? " " : " !").p(f._fname).p("; "); diff --git a/src/main/java/com/seaofnodes/simple/type/TypeTuple.java b/src/main/java/com/seaofnodes/simple/type/TypeTuple.java index 6d3a2b69..95e35e99 100644 --- a/src/main/java/com/seaofnodes/simple/type/TypeTuple.java +++ b/src/main/java/com/seaofnodes/simple/type/TypeTuple.java @@ -55,6 +55,8 @@ public class TypeTuple extends Type { return make(ts); } + public Type ret() { assert _types.length==3; return _types[2]; } + @Override public String str() { return print(new SB()).toString(); } @Override public SB print(SB sb) { -- cgit v1.2.3