From fcce1e13814a02d8358eae9a40e33a7e6f2b5400 Mon Sep 17 00:00:00 2001 From: Cliff Click Date: Fri, 13 Jun 2025 15:42:08 -0700 Subject: Unifying Structs and Arrays --- Makefile | 180 - docs/chapter24/README.md | 345 ++ simple.bat | 2 - simple.sh | 2 - src/main/java/com/seaofnodes/simple/IterPeeps.java | 201 - src/main/java/com/seaofnodes/simple/Parser.java | 2002 -------- src/main/java/com/seaofnodes/simple/Var.java | 53 - .../com/seaofnodes/simple/codegen/BuildLRG.java | 90 - .../com/seaofnodes/simple/codegen/Coalesce.java | 105 - .../com/seaofnodes/simple/codegen/CodeGen.java | 521 -- .../com/seaofnodes/simple/codegen/ElfExpr.java | 87 - .../com/seaofnodes/simple/codegen/ElfFile.java | 381 -- .../com/seaofnodes/simple/codegen/Encoding.java | 531 -- .../simple/codegen/GlobalCodeMotion.java | 322 -- .../java/com/seaofnodes/simple/codegen/IFG.java | 549 -- .../java/com/seaofnodes/simple/codegen/LRG.java | 233 - .../com/seaofnodes/simple/codegen/LinkMem.java | 23 - .../seaofnodes/simple/codegen/ListScheduler.java | 261 - .../com/seaofnodes/simple/codegen/Machine.java | 97 - .../com/seaofnodes/simple/codegen/RIPRelSize.java | 14 - .../com/seaofnodes/simple/codegen/RegAlloc.java | 639 --- .../com/seaofnodes/simple/codegen/RegMask.java | 126 - .../com/seaofnodes/simple/codegen/RegMaskRW.java | 36 - .../java/com/seaofnodes/simple/node/AddFNode.java | 44 - .../java/com/seaofnodes/simple/node/AddNode.java | 169 - .../java/com/seaofnodes/simple/node/AndNode.java | 50 - .../java/com/seaofnodes/simple/node/ArithNode.java | 101 - .../java/com/seaofnodes/simple/node/BoolNode.java | 164 - .../java/com/seaofnodes/simple/node/CFGNode.java | 208 - .../java/com/seaofnodes/simple/node/CProjNode.java | 68 - .../com/seaofnodes/simple/node/CallEndMach.java | 36 - .../com/seaofnodes/simple/node/CallEndNode.java | 108 - .../java/com/seaofnodes/simple/node/CallNode.java | 176 - .../com/seaofnodes/simple/node/CalleeSaveNode.java | 23 - .../java/com/seaofnodes/simple/node/CastMach.java | 19 - .../java/com/seaofnodes/simple/node/CastNode.java | 74 - .../com/seaofnodes/simple/node/ConFldOffNode.java | 43 - .../com/seaofnodes/simple/node/ConstantNode.java | 52 - .../java/com/seaofnodes/simple/node/CtrlNode.java | 15 - .../java/com/seaofnodes/simple/node/DivFNode.java | 41 - .../java/com/seaofnodes/simple/node/DivNode.java | 28 - .../com/seaofnodes/simple/node/ExternNode.java | 23 - .../java/com/seaofnodes/simple/node/FRefNode.java | 35 - .../java/com/seaofnodes/simple/node/FunNode.java | 227 - .../java/com/seaofnodes/simple/node/IfNode.java | 97 - .../java/com/seaofnodes/simple/node/LoadNode.java | 233 - .../java/com/seaofnodes/simple/node/LoopNode.java | 105 - .../seaofnodes/simple/node/MachConcreteNode.java | 27 - .../java/com/seaofnodes/simple/node/MachNode.java | 60 - .../com/seaofnodes/simple/node/MemMergeNode.java | 180 - .../java/com/seaofnodes/simple/node/MemOpNode.java | 109 - .../com/seaofnodes/simple/node/MinusFNode.java | 36 - .../java/com/seaofnodes/simple/node/MinusNode.java | 39 - .../java/com/seaofnodes/simple/node/MulFNode.java | 51 - .../java/com/seaofnodes/simple/node/MulNode.java | 63 - .../java/com/seaofnodes/simple/node/MultiNode.java | 29 - .../java/com/seaofnodes/simple/node/NeverNode.java | 22 - .../java/com/seaofnodes/simple/node/NewNode.java | 131 - src/main/java/com/seaofnodes/simple/node/Node.java | 738 --- .../java/com/seaofnodes/simple/node/NotNode.java | 32 - .../java/com/seaofnodes/simple/node/OrNode.java | 45 - .../java/com/seaofnodes/simple/node/ParmNode.java | 45 - .../java/com/seaofnodes/simple/node/PhiNode.java | 271 - .../java/com/seaofnodes/simple/node/ProjNode.java | 54 - .../com/seaofnodes/simple/node/ReadOnlyMach.java | 19 - .../com/seaofnodes/simple/node/ReadOnlyNode.java | 30 - .../com/seaofnodes/simple/node/RegionNode.java | 168 - .../com/seaofnodes/simple/node/ReturnNode.java | 166 - .../com/seaofnodes/simple/node/RoundF32Node.java | 38 - .../java/com/seaofnodes/simple/node/SarNode.java | 38 - .../java/com/seaofnodes/simple/node/ScopeNode.java | 468 -- .../java/com/seaofnodes/simple/node/ShlNode.java | 50 - .../java/com/seaofnodes/simple/node/ShrNode.java | 34 - .../java/com/seaofnodes/simple/node/SplitNode.java | 25 - .../java/com/seaofnodes/simple/node/StartNode.java | 56 - .../java/com/seaofnodes/simple/node/StopNode.java | 75 - .../java/com/seaofnodes/simple/node/StoreNode.java | 140 - .../com/seaofnodes/simple/node/StructNode.java | 47 - .../java/com/seaofnodes/simple/node/SubFNode.java | 44 - .../java/com/seaofnodes/simple/node/SubNode.java | 43 - .../com/seaofnodes/simple/node/ToFloatNode.java | 34 - .../java/com/seaofnodes/simple/node/XCtrlNode.java | 15 - .../java/com/seaofnodes/simple/node/XorNode.java | 44 - .../seaofnodes/simple/node/cpus/arm/AddARM.java | 20 - .../seaofnodes/simple/node/cpus/arm/AddFARM.java | 22 - .../seaofnodes/simple/node/cpus/arm/AddIARM.java | 27 - .../seaofnodes/simple/node/cpus/arm/AndARM.java | 21 - .../seaofnodes/simple/node/cpus/arm/AndIARM.java | 24 - .../seaofnodes/simple/node/cpus/arm/AsrARM.java | 18 - .../seaofnodes/simple/node/cpus/arm/AsrIARM.java | 31 - .../seaofnodes/simple/node/cpus/arm/BranchARM.java | 77 - .../seaofnodes/simple/node/cpus/arm/CallARM.java | 50 - .../seaofnodes/simple/node/cpus/arm/CallRRARM.java | 32 - .../seaofnodes/simple/node/cpus/arm/CmpARM.java | 22 - .../seaofnodes/simple/node/cpus/arm/CmpFARM.java | 22 - .../seaofnodes/simple/node/cpus/arm/CmpIARM.java | 40 - .../seaofnodes/simple/node/cpus/arm/DivARM.java | 19 - .../seaofnodes/simple/node/cpus/arm/DivFARM.java | 19 - .../seaofnodes/simple/node/cpus/arm/FloatARM.java | 43 - .../seaofnodes/simple/node/cpus/arm/FunARM.java | 22 - .../seaofnodes/simple/node/cpus/arm/I2F8ARM.java | 25 - .../seaofnodes/simple/node/cpus/arm/IntARM.java | 67 - .../seaofnodes/simple/node/cpus/arm/LoadARM.java | 39 - .../seaofnodes/simple/node/cpus/arm/LslARM.java | 19 - .../seaofnodes/simple/node/cpus/arm/LslIARM.java | 33 - .../seaofnodes/simple/node/cpus/arm/LsrARM.java | 19 - .../seaofnodes/simple/node/cpus/arm/LsrIARM.java | 28 - .../seaofnodes/simple/node/cpus/arm/MemOpARM.java | 68 - .../seaofnodes/simple/node/cpus/arm/MulARM.java | 19 - .../seaofnodes/simple/node/cpus/arm/MulFARM.java | 18 - .../seaofnodes/simple/node/cpus/arm/NegARM.java | 25 - .../seaofnodes/simple/node/cpus/arm/NewARM.java | 37 - .../seaofnodes/simple/node/cpus/arm/NotARM.java | 27 - .../com/seaofnodes/simple/node/cpus/arm/OrARM.java | 20 - .../seaofnodes/simple/node/cpus/arm/OrIARM.java | 24 - .../seaofnodes/simple/node/cpus/arm/ParmARM.java | 17 - .../seaofnodes/simple/node/cpus/arm/ProjARM.java | 15 - .../seaofnodes/simple/node/cpus/arm/RetARM.java | 15 - .../seaofnodes/simple/node/cpus/arm/SetARM.java | 32 - .../seaofnodes/simple/node/cpus/arm/SplitARM.java | 65 - .../seaofnodes/simple/node/cpus/arm/StoreARM.java | 43 - .../seaofnodes/simple/node/cpus/arm/SubARM.java | 19 - .../seaofnodes/simple/node/cpus/arm/SubFARM.java | 18 - .../seaofnodes/simple/node/cpus/arm/SubIARM.java | 28 - .../seaofnodes/simple/node/cpus/arm/TFPARM.java | 56 - .../seaofnodes/simple/node/cpus/arm/TMPARM.java | 54 - .../seaofnodes/simple/node/cpus/arm/UJmpARM.java | 49 - .../seaofnodes/simple/node/cpus/arm/XorARM.java | 19 - .../seaofnodes/simple/node/cpus/arm/XorIARM.java | 26 - .../com/seaofnodes/simple/node/cpus/arm/arm.java | 816 --- .../seaofnodes/simple/node/cpus/riscv/AUIPC.java | 42 - .../simple/node/cpus/riscv/AddFRISC.java | 17 - .../simple/node/cpus/riscv/AddIRISC.java | 21 - .../seaofnodes/simple/node/cpus/riscv/AddRISC.java | 21 - .../simple/node/cpus/riscv/AndIRISC.java | 12 - .../seaofnodes/simple/node/cpus/riscv/AndRISC.java | 16 - .../simple/node/cpus/riscv/BranchRISC.java | 93 - .../simple/node/cpus/riscv/CallRISC.java | 63 - .../simple/node/cpus/riscv/CallRRISC.java | 31 - .../simple/node/cpus/riscv/DivFRISC.java | 19 - .../seaofnodes/simple/node/cpus/riscv/DivRISC.java | 17 - .../seaofnodes/simple/node/cpus/riscv/FltRISC.java | 46 - .../seaofnodes/simple/node/cpus/riscv/FunRISC.java | 22 - .../simple/node/cpus/riscv/I2F8RISC.java | 23 - .../seaofnodes/simple/node/cpus/riscv/ImmRISC.java | 53 - .../simple/node/cpus/riscv/Int8RISC.java | 46 - .../seaofnodes/simple/node/cpus/riscv/IntRISC.java | 30 - .../com/seaofnodes/simple/node/cpus/riscv/LUI.java | 32 - .../simple/node/cpus/riscv/LoadRISC.java | 29 - .../simple/node/cpus/riscv/MemOpRISC.java | 76 - .../simple/node/cpus/riscv/MulFRISC.java | 17 - .../seaofnodes/simple/node/cpus/riscv/MulRISC.java | 17 - .../seaofnodes/simple/node/cpus/riscv/NegRISC.java | 20 - .../seaofnodes/simple/node/cpus/riscv/NewRISC.java | 47 - .../seaofnodes/simple/node/cpus/riscv/NotRISC.java | 20 - .../seaofnodes/simple/node/cpus/riscv/OrIRISC.java | 12 - .../seaofnodes/simple/node/cpus/riscv/OrRISC.java | 16 - .../simple/node/cpus/riscv/ParmRISC.java | 17 - .../simple/node/cpus/riscv/ProjRISC.java | 15 - .../seaofnodes/simple/node/cpus/riscv/RetRISC.java | 21 - .../simple/node/cpus/riscv/SetFRISC.java | 33 - .../simple/node/cpus/riscv/SetIRISC.java | 17 - .../seaofnodes/simple/node/cpus/riscv/SetRISC.java | 20 - .../simple/node/cpus/riscv/SllIRISC.java | 12 - .../seaofnodes/simple/node/cpus/riscv/SllRISC.java | 17 - .../simple/node/cpus/riscv/SplitRISC.java | 66 - .../simple/node/cpus/riscv/SraIRISC.java | 14 - .../seaofnodes/simple/node/cpus/riscv/SraRISC.java | 17 - .../simple/node/cpus/riscv/SrlIRISC.java | 12 - .../seaofnodes/simple/node/cpus/riscv/SrlRISC.java | 17 - .../simple/node/cpus/riscv/StoreRISC.java | 47 - .../simple/node/cpus/riscv/SubFRISC.java | 17 - .../seaofnodes/simple/node/cpus/riscv/SubRISC.java | 16 - .../seaofnodes/simple/node/cpus/riscv/TFPRISC.java | 55 - .../seaofnodes/simple/node/cpus/riscv/TMPRISC.java | 41 - .../simple/node/cpus/riscv/UJmpRISC.java | 47 - .../simple/node/cpus/riscv/XorIRISC.java | 13 - .../seaofnodes/simple/node/cpus/riscv/XorRISC.java | 16 - .../seaofnodes/simple/node/cpus/riscv/riscv.java | 543 -- .../simple/node/cpus/x86_64_v2/AddFMemX86.java | 44 - .../simple/node/cpus/x86_64_v2/AddFX86.java | 33 - .../simple/node/cpus/x86_64_v2/AddIX86.java | 24 - .../simple/node/cpus/x86_64_v2/AddMemX86.java | 34 - .../simple/node/cpus/x86_64_v2/AddX86.java | 11 - .../simple/node/cpus/x86_64_v2/AndIX86.java | 11 - .../simple/node/cpus/x86_64_v2/AndX86.java | 11 - .../simple/node/cpus/x86_64_v2/CallRX86.java | 33 - .../simple/node/cpus/x86_64_v2/CallX86.java | 48 - .../simple/node/cpus/x86_64_v2/CmpFX86.java | 32 - .../simple/node/cpus/x86_64_v2/CmpIX86.java | 44 - .../simple/node/cpus/x86_64_v2/CmpMemX86.java | 78 - .../simple/node/cpus/x86_64_v2/CmpX86.java | 32 - .../simple/node/cpus/x86_64_v2/DivFX86.java | 34 - .../simple/node/cpus/x86_64_v2/DivX86.java | 33 - .../simple/node/cpus/x86_64_v2/FltX86.java | 47 - .../simple/node/cpus/x86_64_v2/FunX86.java | 34 - .../simple/node/cpus/x86_64_v2/I2f8X86.java | 31 - .../simple/node/cpus/x86_64_v2/ImmX86.java | 38 - .../simple/node/cpus/x86_64_v2/IntX86.java | 76 - .../simple/node/cpus/x86_64_v2/JmpX86.java | 81 - .../simple/node/cpus/x86_64_v2/LeaX86.java | 55 - .../simple/node/cpus/x86_64_v2/LoadX86.java | 57 - .../simple/node/cpus/x86_64_v2/MemAddX86.java | 40 - .../simple/node/cpus/x86_64_v2/MemOpX86.java | 88 - .../simple/node/cpus/x86_64_v2/MulFX86.java | 32 - .../simple/node/cpus/x86_64_v2/MulIX86.java | 34 - .../simple/node/cpus/x86_64_v2/MulX86.java | 29 - .../simple/node/cpus/x86_64_v2/NegX86.java | 22 - .../simple/node/cpus/x86_64_v2/NewX86.java | 25 - .../simple/node/cpus/x86_64_v2/NotX86.java | 39 - .../simple/node/cpus/x86_64_v2/OrIX86.java | 11 - .../simple/node/cpus/x86_64_v2/OrX86.java | 11 - .../simple/node/cpus/x86_64_v2/ParmX86.java | 17 - .../simple/node/cpus/x86_64_v2/ProjX86.java | 15 - .../simple/node/cpus/x86_64_v2/RegX86.java | 27 - .../simple/node/cpus/x86_64_v2/RetX86.java | 19 - .../simple/node/cpus/x86_64_v2/SarIX86.java | 39 - .../simple/node/cpus/x86_64_v2/SarX86.java | 24 - .../simple/node/cpus/x86_64_v2/SetX86.java | 54 - .../simple/node/cpus/x86_64_v2/ShlIX86.java | 39 - .../simple/node/cpus/x86_64_v2/ShlX86.java | 24 - .../simple/node/cpus/x86_64_v2/ShrIX86.java | 34 - .../simple/node/cpus/x86_64_v2/ShrX86.java | 24 - .../simple/node/cpus/x86_64_v2/SplitX86.java | 107 - .../simple/node/cpus/x86_64_v2/StoreX86.java | 85 - .../simple/node/cpus/x86_64_v2/SubFX86.java | 31 - .../simple/node/cpus/x86_64_v2/SubX86.java | 10 - .../simple/node/cpus/x86_64_v2/TFPX86.java | 67 - .../simple/node/cpus/x86_64_v2/TMPX86.java | 39 - .../simple/node/cpus/x86_64_v2/UJmpX86.java | 54 - .../simple/node/cpus/x86_64_v2/XorIX86.java | 11 - .../simple/node/cpus/x86_64_v2/XorX86.java | 11 - .../simple/node/cpus/x86_64_v2/x86_64_v2.java | 649 --- .../com/seaofnodes/simple/print/ASMPrinter.java | 306 -- .../seaofnodes/simple/print/GraphVisualizer.java | 287 -- .../com/seaofnodes/simple/print/IRPrinter.java | 331 -- .../java/com/seaofnodes/simple/print/JSViewer.java | 405 -- .../seaofnodes/simple/print/SimpleWebSocket.java | 149 - src/main/java/com/seaofnodes/simple/sys.java | 25 - .../java/com/seaofnodes/simple/type/Field.java | 136 - src/main/java/com/seaofnodes/simple/type/Type.java | 514 -- .../com/seaofnodes/simple/type/TypeConAry.java | 82 - .../com/seaofnodes/simple/type/TypeConAryB.java | 21 - .../com/seaofnodes/simple/type/TypeConAryI.java | 17 - .../java/com/seaofnodes/simple/type/TypeFloat.java | 110 - .../com/seaofnodes/simple/type/TypeFunPtr.java | 241 - .../com/seaofnodes/simple/type/TypeInteger.java | 153 - .../java/com/seaofnodes/simple/type/TypeMem.java | 108 - .../com/seaofnodes/simple/type/TypeMemPtr.java | 147 - .../java/com/seaofnodes/simple/type/TypeNil.java | 62 - .../java/com/seaofnodes/simple/type/TypePtr.java | 55 - .../java/com/seaofnodes/simple/type/TypeRPC.java | 99 - .../com/seaofnodes/simple/type/TypeStruct.java | 480 -- .../java/com/seaofnodes/simple/type/TypeTuple.java | 119 - src/main/java/com/seaofnodes/simple/util/Ary.java | 229 - .../java/com/seaofnodes/simple/util/AryInt.java | 192 - src/main/java/com/seaofnodes/simple/util/SB.java | 99 - .../java/com/seaofnodes/simple/util/Utils.java | 53 - src/main/smp/sys.smp | 199 - .../java/com/seaofnodes/simple/BrainFuckTest.java | 38 - .../java/com/seaofnodes/simple/Chapter01Test.java | 95 - .../java/com/seaofnodes/simple/Chapter02Test.java | 61 - .../java/com/seaofnodes/simple/Chapter03Test.java | 73 - .../java/com/seaofnodes/simple/Chapter04Test.java | 131 - .../java/com/seaofnodes/simple/Chapter05Test.java | 186 - .../java/com/seaofnodes/simple/Chapter06Test.java | 305 -- .../java/com/seaofnodes/simple/Chapter07Test.java | 222 - .../java/com/seaofnodes/simple/Chapter08Test.java | 208 - .../java/com/seaofnodes/simple/Chapter09Test.java | 299 -- .../java/com/seaofnodes/simple/Chapter10Test.java | 399 -- .../java/com/seaofnodes/simple/Chapter11Test.java | 551 -- .../java/com/seaofnodes/simple/Chapter12Test.java | 61 - .../java/com/seaofnodes/simple/Chapter13Test.java | 281 - .../java/com/seaofnodes/simple/Chapter14Test.java | 226 - .../java/com/seaofnodes/simple/Chapter15Test.java | 358 -- .../java/com/seaofnodes/simple/Chapter16Test.java | 259 - .../java/com/seaofnodes/simple/Chapter17Test.java | 556 -- .../java/com/seaofnodes/simple/Chapter18Test.java | 371 -- .../java/com/seaofnodes/simple/Chapter19Test.java | 332 -- .../java/com/seaofnodes/simple/Chapter20Test.java | 152 - .../java/com/seaofnodes/simple/Chapter21Test.java | 277 - .../java/com/seaofnodes/simple/Chapter22Test.java | 240 - .../java/com/seaofnodes/simple/Chapter23Test.java | 118 - src/test/java/com/seaofnodes/simple/Compile.java | 65 - src/test/java/com/seaofnodes/simple/Eval2.java | 469 -- src/test/java/com/seaofnodes/simple/EvalArm64.java | 760 --- src/test/java/com/seaofnodes/simple/EvalRisc5.java | 368 -- .../java/com/seaofnodes/simple/FuzzerWrap.java | 43 - src/test/java/com/seaofnodes/simple/Main.java | 18 - .../java/com/seaofnodes/simple/MergeSortTest.java | 45 - .../java/com/seaofnodes/simple/SchedulerTest.java | 74 - src/test/java/com/seaofnodes/simple/Simple.java | 289 -- src/test/java/com/seaofnodes/simple/TestArm64.java | 42 - src/test/java/com/seaofnodes/simple/TestC.java | 108 - src/test/java/com/seaofnodes/simple/TestRisc5.java | 50 - src/test/java/com/seaofnodes/simple/TypeTest.java | 198 - .../com/seaofnodes/simple/evaluator/Evaluator.java | 382 -- .../com/seaofnodes/simple/evaluator/Scheduler.java | 605 --- .../java/com/seaofnodes/simple/fuzzer/Fuzzer.java | 174 - .../com/seaofnodes/simple/fuzzer/FuzzerUtils.java | 111 - .../java/com/seaofnodes/simple/fuzzer/Reducer.java | 176 - .../seaofnodes/simple/fuzzer/ScriptGenerator.java | 869 ---- .../java/com/seaofnodes/simple/progs/antiDep1.smp | 11 - .../java/com/seaofnodes/simple/progs/arg_count.c | 10 - .../java/com/seaofnodes/simple/progs/arg_count.smp | 6 - .../java/com/seaofnodes/simple/progs/array1.smp | 9 - .../java/com/seaofnodes/simple/progs/brain_fuck.c | 18 - .../com/seaofnodes/simple/progs/brain_fuck.smp | 46 - src/test/java/com/seaofnodes/simple/progs/echo.smp | 3 - src/test/java/com/seaofnodes/simple/progs/fib.c | 16 - src/test/java/com/seaofnodes/simple/progs/fib.smp | 11 - .../com/seaofnodes/simple/progs/helloWorld.smp | 3 - src/test/java/com/seaofnodes/simple/progs/jig.smp | 19 - .../java/com/seaofnodes/simple/progs/large.smp | 5344 -------------------- src/test/java/com/seaofnodes/simple/progs/loop2.c | 7 - .../java/com/seaofnodes/simple/progs/loop2.smp | 10 - .../java/com/seaofnodes/simple/progs/newtonFloat.c | 16 - .../com/seaofnodes/simple/progs/newtonFloat.smp | 12 - .../seaofnodes/simple/progs/no_stack_arg_count.smp | 6 - src/test/java/com/seaofnodes/simple/progs/person.c | 34 - .../java/com/seaofnodes/simple/progs/person.smp | 9 - .../com/seaofnodes/simple/progs/riscv_load.smp | 4 - .../com/seaofnodes/simple/progs/sext_str_1.smp | 4 - .../seaofnodes/simple/progs/sext_str_fold_away.smp | 5 - .../simple/progs/sext_str_not_fold_away.smp | 4 - .../simple/progs/sext_str_not_fold_away_2.smp | 4 - src/test/java/com/seaofnodes/simple/progs/sieve.c | 19 - .../java/com/seaofnodes/simple/progs/sieve.smp | 31 - src/test/java/com/seaofnodes/simple/progs/sort.c | 28 - src/test/java/com/seaofnodes/simple/progs/sort.smp | 38 - .../com/seaofnodes/simple/progs/store_peep.smp | 3 - .../java/com/seaofnodes/simple/progs/stringHash.c | 30 - .../com/seaofnodes/simple/progs/stringHash.smp | 30 - 333 files changed, 345 insertions(+), 39926 deletions(-) delete mode 100644 Makefile create mode 100644 docs/chapter24/README.md delete mode 100644 simple.bat delete mode 100644 simple.sh delete mode 100644 src/main/java/com/seaofnodes/simple/IterPeeps.java delete mode 100644 src/main/java/com/seaofnodes/simple/Parser.java delete mode 100644 src/main/java/com/seaofnodes/simple/Var.java delete mode 100644 src/main/java/com/seaofnodes/simple/codegen/BuildLRG.java delete mode 100644 src/main/java/com/seaofnodes/simple/codegen/Coalesce.java delete mode 100644 src/main/java/com/seaofnodes/simple/codegen/CodeGen.java delete mode 100644 src/main/java/com/seaofnodes/simple/codegen/ElfExpr.java delete mode 100644 src/main/java/com/seaofnodes/simple/codegen/ElfFile.java delete mode 100644 src/main/java/com/seaofnodes/simple/codegen/Encoding.java delete mode 100644 src/main/java/com/seaofnodes/simple/codegen/GlobalCodeMotion.java delete mode 100644 src/main/java/com/seaofnodes/simple/codegen/IFG.java delete mode 100644 src/main/java/com/seaofnodes/simple/codegen/LRG.java delete mode 100644 src/main/java/com/seaofnodes/simple/codegen/LinkMem.java delete mode 100644 src/main/java/com/seaofnodes/simple/codegen/ListScheduler.java delete mode 100644 src/main/java/com/seaofnodes/simple/codegen/Machine.java delete mode 100644 src/main/java/com/seaofnodes/simple/codegen/RIPRelSize.java delete mode 100644 src/main/java/com/seaofnodes/simple/codegen/RegAlloc.java delete mode 100644 src/main/java/com/seaofnodes/simple/codegen/RegMask.java delete mode 100644 src/main/java/com/seaofnodes/simple/codegen/RegMaskRW.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/AddFNode.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/AddNode.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/AndNode.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/ArithNode.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/BoolNode.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/CFGNode.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/CProjNode.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/CallEndMach.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/CallEndNode.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/CallNode.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/CalleeSaveNode.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/CastMach.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/CastNode.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/ConFldOffNode.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/ConstantNode.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/CtrlNode.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/DivFNode.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/DivNode.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/ExternNode.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/FRefNode.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/FunNode.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/IfNode.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/LoadNode.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/LoopNode.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/MachConcreteNode.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/MachNode.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/MemMergeNode.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/MemOpNode.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/MinusFNode.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/MinusNode.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/MulFNode.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/MulNode.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/MultiNode.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/NeverNode.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/NewNode.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/Node.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/NotNode.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/OrNode.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/ParmNode.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/PhiNode.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/ProjNode.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/ReadOnlyMach.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/ReadOnlyNode.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/RegionNode.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/ReturnNode.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/RoundF32Node.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/SarNode.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/ScopeNode.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/ShlNode.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/ShrNode.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/SplitNode.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/StartNode.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/StopNode.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/StoreNode.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/StructNode.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/SubFNode.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/SubNode.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/ToFloatNode.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/XCtrlNode.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/XorNode.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/arm/AddARM.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/arm/AddFARM.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/arm/AddIARM.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/arm/AndARM.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/arm/AndIARM.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/arm/AsrARM.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/arm/AsrIARM.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/arm/BranchARM.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/arm/CallARM.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/arm/CallRRARM.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/arm/CmpARM.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/arm/CmpFARM.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/arm/CmpIARM.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/arm/DivARM.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/arm/DivFARM.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/arm/FloatARM.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/arm/FunARM.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/arm/I2F8ARM.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/arm/IntARM.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/arm/LoadARM.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/arm/LslARM.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/arm/LslIARM.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/arm/LsrARM.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/arm/LsrIARM.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/arm/MemOpARM.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/arm/MulARM.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/arm/MulFARM.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/arm/NegARM.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/arm/NewARM.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/arm/NotARM.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/arm/OrARM.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/arm/OrIARM.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/arm/ParmARM.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/arm/ProjARM.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/arm/RetARM.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/arm/SetARM.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/arm/SplitARM.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/arm/StoreARM.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/arm/SubARM.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/arm/SubFARM.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/arm/SubIARM.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/arm/TFPARM.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/arm/TMPARM.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/arm/UJmpARM.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/arm/XorARM.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/arm/XorIARM.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/arm/arm.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/riscv/AUIPC.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/riscv/AddFRISC.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/riscv/AddIRISC.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/riscv/AddRISC.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/riscv/AndIRISC.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/riscv/AndRISC.java delete 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/CallRISC.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/riscv/CallRRISC.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/riscv/DivFRISC.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/riscv/DivRISC.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/riscv/FltRISC.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/riscv/FunRISC.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/riscv/I2F8RISC.java delete 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/Int8RISC.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/riscv/IntRISC.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/riscv/LUI.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/riscv/LoadRISC.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/riscv/MemOpRISC.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/riscv/MulFRISC.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/riscv/MulRISC.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/riscv/NegRISC.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/riscv/NewRISC.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/riscv/NotRISC.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/riscv/OrIRISC.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/riscv/OrRISC.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/riscv/ParmRISC.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/riscv/ProjRISC.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/riscv/RetRISC.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/riscv/SetFRISC.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/riscv/SetIRISC.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/riscv/SetRISC.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/riscv/SllIRISC.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/riscv/SllRISC.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/riscv/SplitRISC.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/riscv/SraIRISC.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/riscv/SraRISC.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/riscv/SrlIRISC.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/riscv/SrlRISC.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/riscv/StoreRISC.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/riscv/SubFRISC.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/riscv/SubRISC.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/riscv/TFPRISC.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/riscv/TMPRISC.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/riscv/UJmpRISC.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/riscv/XorIRISC.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/riscv/XorRISC.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/riscv/riscv.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddFMemX86.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddFX86.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddIX86.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddMemX86.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddX86.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AndIX86.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AndX86.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CallRX86.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CallX86.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CmpFX86.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CmpIX86.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CmpMemX86.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CmpX86.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/DivFX86.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/DivX86.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/FltX86.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/FunX86.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/I2f8X86.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ImmX86.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/IntX86.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/JmpX86.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/LeaX86.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/LoadX86.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MemAddX86.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MemOpX86.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MulFX86.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MulIX86.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MulX86.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/NegX86.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/NewX86.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/NotX86.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/OrIX86.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/OrX86.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ParmX86.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ProjX86.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/RegX86.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/RetX86.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SarIX86.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SarX86.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SetX86.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShlIX86.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShlX86.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShrIX86.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShrX86.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SplitX86.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/StoreX86.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SubFX86.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SubX86.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/TFPX86.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/TMPX86.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/UJmpX86.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/XorIX86.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/XorX86.java delete mode 100644 src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/x86_64_v2.java delete mode 100644 src/main/java/com/seaofnodes/simple/print/ASMPrinter.java delete mode 100644 src/main/java/com/seaofnodes/simple/print/GraphVisualizer.java delete mode 100644 src/main/java/com/seaofnodes/simple/print/IRPrinter.java delete mode 100644 src/main/java/com/seaofnodes/simple/print/JSViewer.java delete mode 100644 src/main/java/com/seaofnodes/simple/print/SimpleWebSocket.java delete mode 100644 src/main/java/com/seaofnodes/simple/sys.java delete mode 100644 src/main/java/com/seaofnodes/simple/type/Field.java delete mode 100644 src/main/java/com/seaofnodes/simple/type/Type.java delete mode 100644 src/main/java/com/seaofnodes/simple/type/TypeConAry.java delete mode 100644 src/main/java/com/seaofnodes/simple/type/TypeConAryB.java delete mode 100644 src/main/java/com/seaofnodes/simple/type/TypeConAryI.java delete mode 100644 src/main/java/com/seaofnodes/simple/type/TypeFloat.java delete mode 100644 src/main/java/com/seaofnodes/simple/type/TypeFunPtr.java delete mode 100644 src/main/java/com/seaofnodes/simple/type/TypeInteger.java delete mode 100644 src/main/java/com/seaofnodes/simple/type/TypeMem.java delete mode 100644 src/main/java/com/seaofnodes/simple/type/TypeMemPtr.java delete mode 100644 src/main/java/com/seaofnodes/simple/type/TypeNil.java delete mode 100644 src/main/java/com/seaofnodes/simple/type/TypePtr.java delete mode 100644 src/main/java/com/seaofnodes/simple/type/TypeRPC.java delete mode 100644 src/main/java/com/seaofnodes/simple/type/TypeStruct.java delete mode 100644 src/main/java/com/seaofnodes/simple/type/TypeTuple.java delete mode 100644 src/main/java/com/seaofnodes/simple/util/Ary.java delete mode 100644 src/main/java/com/seaofnodes/simple/util/AryInt.java delete mode 100644 src/main/java/com/seaofnodes/simple/util/SB.java delete mode 100644 src/main/java/com/seaofnodes/simple/util/Utils.java delete mode 100644 src/main/smp/sys.smp delete mode 100644 src/test/java/com/seaofnodes/simple/BrainFuckTest.java delete mode 100644 src/test/java/com/seaofnodes/simple/Chapter01Test.java delete mode 100644 src/test/java/com/seaofnodes/simple/Chapter02Test.java delete mode 100644 src/test/java/com/seaofnodes/simple/Chapter03Test.java delete mode 100644 src/test/java/com/seaofnodes/simple/Chapter04Test.java delete mode 100644 src/test/java/com/seaofnodes/simple/Chapter05Test.java delete mode 100644 src/test/java/com/seaofnodes/simple/Chapter06Test.java delete mode 100644 src/test/java/com/seaofnodes/simple/Chapter07Test.java delete mode 100644 src/test/java/com/seaofnodes/simple/Chapter08Test.java delete mode 100644 src/test/java/com/seaofnodes/simple/Chapter09Test.java delete mode 100644 src/test/java/com/seaofnodes/simple/Chapter10Test.java delete mode 100644 src/test/java/com/seaofnodes/simple/Chapter11Test.java delete mode 100644 src/test/java/com/seaofnodes/simple/Chapter12Test.java delete mode 100644 src/test/java/com/seaofnodes/simple/Chapter13Test.java delete mode 100644 src/test/java/com/seaofnodes/simple/Chapter14Test.java delete mode 100644 src/test/java/com/seaofnodes/simple/Chapter15Test.java delete mode 100644 src/test/java/com/seaofnodes/simple/Chapter16Test.java delete mode 100644 src/test/java/com/seaofnodes/simple/Chapter17Test.java delete mode 100644 src/test/java/com/seaofnodes/simple/Chapter18Test.java delete mode 100644 src/test/java/com/seaofnodes/simple/Chapter19Test.java delete mode 100644 src/test/java/com/seaofnodes/simple/Chapter20Test.java delete mode 100644 src/test/java/com/seaofnodes/simple/Chapter21Test.java delete mode 100644 src/test/java/com/seaofnodes/simple/Chapter22Test.java delete mode 100644 src/test/java/com/seaofnodes/simple/Chapter23Test.java delete mode 100644 src/test/java/com/seaofnodes/simple/Compile.java delete mode 100644 src/test/java/com/seaofnodes/simple/Eval2.java delete mode 100644 src/test/java/com/seaofnodes/simple/EvalArm64.java delete mode 100644 src/test/java/com/seaofnodes/simple/EvalRisc5.java delete mode 100644 src/test/java/com/seaofnodes/simple/FuzzerWrap.java delete mode 100644 src/test/java/com/seaofnodes/simple/Main.java delete mode 100644 src/test/java/com/seaofnodes/simple/MergeSortTest.java delete mode 100644 src/test/java/com/seaofnodes/simple/SchedulerTest.java delete mode 100644 src/test/java/com/seaofnodes/simple/Simple.java delete mode 100644 src/test/java/com/seaofnodes/simple/TestArm64.java delete mode 100644 src/test/java/com/seaofnodes/simple/TestC.java delete mode 100644 src/test/java/com/seaofnodes/simple/TestRisc5.java delete mode 100644 src/test/java/com/seaofnodes/simple/TypeTest.java delete mode 100644 src/test/java/com/seaofnodes/simple/evaluator/Evaluator.java delete mode 100644 src/test/java/com/seaofnodes/simple/evaluator/Scheduler.java delete mode 100644 src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java delete mode 100644 src/test/java/com/seaofnodes/simple/fuzzer/FuzzerUtils.java delete mode 100644 src/test/java/com/seaofnodes/simple/fuzzer/Reducer.java delete mode 100644 src/test/java/com/seaofnodes/simple/fuzzer/ScriptGenerator.java delete mode 100644 src/test/java/com/seaofnodes/simple/progs/antiDep1.smp delete mode 100644 src/test/java/com/seaofnodes/simple/progs/arg_count.c delete mode 100644 src/test/java/com/seaofnodes/simple/progs/arg_count.smp delete mode 100644 src/test/java/com/seaofnodes/simple/progs/array1.smp delete mode 100644 src/test/java/com/seaofnodes/simple/progs/brain_fuck.c delete mode 100644 src/test/java/com/seaofnodes/simple/progs/brain_fuck.smp delete mode 100644 src/test/java/com/seaofnodes/simple/progs/echo.smp delete mode 100644 src/test/java/com/seaofnodes/simple/progs/fib.c delete mode 100644 src/test/java/com/seaofnodes/simple/progs/fib.smp delete mode 100644 src/test/java/com/seaofnodes/simple/progs/helloWorld.smp delete mode 100644 src/test/java/com/seaofnodes/simple/progs/jig.smp delete mode 100644 src/test/java/com/seaofnodes/simple/progs/large.smp delete mode 100644 src/test/java/com/seaofnodes/simple/progs/loop2.c delete mode 100644 src/test/java/com/seaofnodes/simple/progs/loop2.smp delete mode 100644 src/test/java/com/seaofnodes/simple/progs/newtonFloat.c delete mode 100644 src/test/java/com/seaofnodes/simple/progs/newtonFloat.smp delete mode 100644 src/test/java/com/seaofnodes/simple/progs/no_stack_arg_count.smp delete mode 100644 src/test/java/com/seaofnodes/simple/progs/person.c delete mode 100644 src/test/java/com/seaofnodes/simple/progs/person.smp delete mode 100644 src/test/java/com/seaofnodes/simple/progs/riscv_load.smp delete mode 100644 src/test/java/com/seaofnodes/simple/progs/sext_str_1.smp delete mode 100644 src/test/java/com/seaofnodes/simple/progs/sext_str_fold_away.smp delete mode 100644 src/test/java/com/seaofnodes/simple/progs/sext_str_not_fold_away.smp delete mode 100644 src/test/java/com/seaofnodes/simple/progs/sext_str_not_fold_away_2.smp delete mode 100644 src/test/java/com/seaofnodes/simple/progs/sieve.c delete mode 100644 src/test/java/com/seaofnodes/simple/progs/sieve.smp delete mode 100644 src/test/java/com/seaofnodes/simple/progs/sort.c delete mode 100644 src/test/java/com/seaofnodes/simple/progs/sort.smp delete mode 100644 src/test/java/com/seaofnodes/simple/progs/store_peep.smp delete mode 100644 src/test/java/com/seaofnodes/simple/progs/stringHash.c delete mode 100644 src/test/java/com/seaofnodes/simple/progs/stringHash.smp diff --git a/Makefile b/Makefile deleted file mode 100644 index 6547a73a..00000000 --- a/Makefile +++ /dev/null @@ -1,180 +0,0 @@ -# -# Makefile expects to run at the project root, and not nested inside anywhere -# -# cd $DESK/Simple/chapterXX; make -# -# -# first time only - this is the only thing that hits the Interwebs -# -# make lib -# -# any time you want to nuke everything from space: -# -# make clean tags -# -# run the tests -# -# make tests - -####################################################### -# -# Boilerplate because make syntax isn't the best -# - -# Use bash for recipes -SHELL := /bin/bash - -# Remove target on error (no broken leftover files) -.DELETE_ON_ERROR: - -# Keep partial builds but not partial recipes; keeping them speeds up the next `make` -.NOTINTERMEDIATE: - -# for printing variable values -# usage: make print-VARIABLE -# > VARIABLE = value_of_variable -print-% : ; @echo $* = $($*) - -# literal space -space := $() $() - -# Decide OS-specific questions -# jar-file seperator -ifeq ($(OS),Windows_NT) - SEP = ; -else - SEP = : -endif -# Find a reasonable ctags. -CTAGS = $(shell which ctags) -# Hack for MacOS: /usr/bin/ctags is unfriendly, so look for ctags from brew -ifeq ($(UNAME),Darwin) - CTAGS = $(shell brew list ctags 2> /dev/null | grep bin/ctags) -endif - -# Fun Args to javac. Mostly limit to java8 source definitions, and fairly -# aggressive lint warnings. -JAVAC_ARGS = -g - -# Source code -SIMPLE := com/seaofnodes/simple -SRC := src/main/java -TST := src/test/java -CLZDIR:= build/classes -main_javas := $(wildcard $(SRC)/$(SIMPLE)/*java $(SRC)/$(SIMPLE)/*/*java $(SRC)/$(SIMPLE)/node/cpus/*/*java) -test_javas := $(wildcard $(TST)/$(SIMPLE)/*java $(TST)/$(SIMPLE)/*/*java) -main_classes := $(patsubst $(SRC)/%java,$(CLZDIR)/main/%class,$(main_javas)) -test_classes := $(patsubst $(TST)/%java,$(CLZDIR)/test/%class,$(test_javas)) -test_cp := $(patsubst $(TST)/$(SIMPLE)/%.java,com.seaofnodes.simple.%,$(wildcard $(TST)/$(SIMPLE)/*Test.java)) -classes = $(main_classes) $(test_classes) -# All the libraries -libs = $(wildcard lib/*jar) -jars = $(subst $(space),$(SEP),$(libs)) - - -default_targets := $(CLZDIR)/.tag $(test_classes) -# Optionally add ctags to the default target if a reasonable one was found. -ifneq ($(CTAGS),) -default_targets += tags -endif - -default: $(default_targets) - -####################################################### -# Compile just the out-of-date files - -OODM := -$(CLZDIR)/main/.mtag: $(main_classes) $(main_javas) - @[ -d $(CLZDIR)/main ] || mkdir -p $(CLZDIR)/main - @# This crap is really just "javac", but with a beautiful error message. - @# The very very long list of java files is suppressed and counted. - @# The required output "Note: blah blah deprecated blah" is suppressed. - $(file > .argsM.txt , $(OODM)) - @if [ ! -z "$(OODM)" ] ; then \ - echo "compiling main because " $< " and " `wc -w < .argsM.txt` " more files" ; \ - if ! javac $(JAVAC_ARGS) -cp "$(CLZDIR)/main$(SEP)$(jars)" -sourcepath $(SRC) -d $(CLZDIR)/main $(OODM) >& .out.txt ; then \ - cat .out.txt ; \ - exit 1; \ - fi ; \ - rm -rf .out.txt ; \ - fi - @touch $(CLZDIR)/main/.mtag - @rm -f .argsM.txt - -# Collect just the out-of-date files -$(main_classes): $(CLZDIR)/main/%class: $(SRC)/%java - $(eval OODM += $$<) - - -####################################################### - -OODT := -$(CLZDIR)/test/.ttag: $(test_classes) $(test_javas) $(CLZDIR)/main/.mtag - @[ -d $(CLZDIR)/test ] || mkdir -p $(CLZDIR)/test - $(file > .argsT.txt $(OODT)) - @if [ ! -z "$(OODT)" ] ; then \ - echo "compiling test because " $< " and " `wc -w < .argsT.txt` " more files" ; \ - if ! javac $(JAVAC_ARGS) -cp "$(CLZDIR)/test$(SEP)$(CLZDIR)/main$(SEP)$(jars)" -sourcepath $(TST) -d $(CLZDIR)/test $(OODT) >& .out.txt ; then \ - cat .out.txt ; \ - exit 1; \ - fi ; \ - rm -rf .out.txt ; \ - fi - @touch $(CLZDIR)/test/.ttag - @rm -f .argsT.txt - -# Collect just the out-of-date files -$(test_classes): $(CLZDIR)/test/%class: $(TST)/%java - $(eval OODT += $$<) - -####################################################### -# Base launch line for JVM tests -JVM=nice java -ea -cp "$(CLZDIR)/main${SEP}${jars}${SEP}$(CLZDIR)/test" - - -tests: $(CLZDIR)/main/.mtag $(CLZDIR)/test/.ttag - @echo "testing " $(test_cp) - @[ -d build/objs ] || mkdir -p build/objs - @$(JVM) org.junit.runner.JUnitCore $(test_cp) - @$(JVM) org.junit.runner.JUnitCore com.seaofnodes.simple.FuzzerWrap - -fuzzer: $(CLZDIR)/main/.mtag $(CLZDIR)/test/.ttag - @echo "fuzzing " $(test_cp) - @$(JVM) org.junit.runner.JUnitCore com.seaofnodes.simple.FuzzerWrap - -# Build a Simple jar -release: build/release/simple.jar - -# Build a Simple jar -build/release/simple.jar: $(CLZDIR)/main/.mtag $(CLZDIR)/test/.ttag - @echo "jarring " $@ " because " $? - @[ -d $(dir $@) ] || mkdir -p $(dir $@) - @jar cf build/release/simple.jar -C $(CLZDIR)/main . -C $(CLZDIR)/test . -C $(SRC)/$(SIMPLE) . -C $(TST)/$(SIMPLE) . - -# Launch viewer -view: $(main_classes) - @echo "viewing " - @$(JVM) com.seaofnodes.simple.JSViewer - -.PHONY: clean -clean: - rm -rf build - rm -f TAGS - (find . -name "*~" -exec rm {} \; 2>/dev/null; exit 0) - -# Download libs from maven -lib: lib/junit-4.12.jar lib/hamcrest-core-1.3.jar - -# Jars -lib/junit-4.12.jar: - @[ -d lib ] || mkdir -p lib - @(cd lib; wget https://repo1.maven.org/maven2/junit/junit/4.12/junit-4.12.jar) - -lib/hamcrest-core-1.3.jar: - @[ -d lib ] || mkdir -p lib - @(cd lib; wget https://repo1.maven.org/maven2/org/hamcrest/hamcrest-core/1.3/hamcrest-core-1.3.jar) - -# Build emacs tags (part of a tasty emacs ide experience) -tags: $(main_javas) $(test_javas) - @rm -f TAGS - @$(CTAGS) -e --recurse=yes --extras=+q --fields=+fksaiS $(SRC) $(TST) diff --git a/docs/chapter24/README.md b/docs/chapter24/README.md new file mode 100644 index 00000000..03056897 --- /dev/null +++ b/docs/chapter24/README.md @@ -0,0 +1,345 @@ + + +## Unifying Structs and Arrays + +The idea is that structs have fields indexed by names, and arrays have fields +indexed by numbers, and these ideas are compatible with each other. This idea +has been used by the C language for a long time - an array can follow a normal +struct C and can index it with the usual array syntax. + +The alternative, using a field to hold a pointer to an array, exchanges some +flexibility for an extra indirection. The obvious flexibility allows swapping +the array out (generally for a larger one) and this is the basis of very common +`Vector` or `ArrayList` generic containers. + + +```java +struct str { + int hash; // A cache of the strings hash code + u32 #; // The array length + u8 []; // Array of bytes +}; + +str X = "abc"; +print(X.hash); // Read the hash field +X[0]=='a'; // Read array element 0 +X#; // Length of the array + +``` + +Here, the field named `#` holds the array length and the field named `[]` is +the actual array, which follows the other fields directly in memory. +Here is another example for variable length array: + +```java +struct vecInt { + u32 size; // Actual number of elements + u32 #; // The array length or capacity + int []; // Array of integers. +}; +``` + +The obvious limit here is that the array cannot be directly resized; it is +literally in the same memory block as the leading fields. Such a `vecInt` can +be *re-allocated* to a new larger `vecInt`, but the original `vecInt` will be +reclaimed - you cannot use the same `vecInt` reference forever. + +Any number of named fields are allowed: + +```java +struct Class { + str className; + str professor; + int time; + int credits; + Class prerequisite; + u16 #; // Limit of 65535 students per class + Student []; +}; +``` + +Or self-reference: + +```java +struct NTree { + Some val; + u8 #; // Length is 0-255 + NTree []; // More NTrees +}; +``` + +The syntax rules are: +- the last field is named `[]` and can be of any type. +- the second-to-last field is named `#` and must be some unsigned integer type + representing the maximum array length. +- may require a leading space in front of ` []` + + + +### Constructors and Arrays + +The allocation includes the array length, and can include a constructor: +`new NTree{val=17;}[2];` + +The constructor is optional, the array length is not. + +Optional function for initializing arrays, allows for write-once final arrays: +`new u8[buffer#,{ u32 idx -> buffer[idx].toUpperCase() }]` + + + +### Changing array max length + +An issue for large numbers of range-checked small arrays is that reserving a +full 4 bytes for array length can substantially increase the array overhead. +Simple allows for arrays to have smaller max lengths, although these are +treated as a different type than arrays with different max lengths. + +```java +struct x0 { u8 #; u8[]; }; // Array limited to 0-255 length +struct x1 { u16 #; u8[]; }; // Array limited to 0-65535 length +struct x2 { u32 #; u8[]; }; // +struct x3 { u64 #; u8[]; }; // + +x0 x = "abc"; // 4 bytes total: [3, 'a', 'b', 'c'] +x1 y = "def"; // 6 bytes total: [3, 0 , 'd', 'e', 'f', 0,]; 1 pad byte to acheieve 2-byte alignment +x = y; // ERROR, "x1 is not x0", no auto-widening +x += y; // OK, 7 bytes total: [6, 'a', 'b', 'c', 'd', 'e', 'f'] +y += x; // OK, 8 bytes total: [6, 0 , 'd', 'e', 'f', 'a', 'b', 'c'] + +x += "VeryLongString.... ....255_chars"; // Runtime RangeCheck + +// Explicit conversion to widen string +y = "" + x; +``` + + +### Two flavors of extendable arrays/strings: an extra indirection or not + +With an extra indirection you get e.g. Java `ArrayList` or C++ `Vector`. + +Without, then any call which extends the extendable array return a potentially +new object, only growing as needed. + +```java +val vec1 = new vecInt[0].add(2); // len=1, capacity=1 +val vec2 = vec1.add(3); // NEW vecInt returned, only is deleted; len=2; capacity=2; +vec1[0]; // ERROR, vec1 has been freed by call to `vec1.add(3)` +vec2[0]; // OK +vec2[1]; // OK +vec2[2]; // Runtime error, AIOOBE +``` + + +## A String Implementation + +Default strings have names like "str", "cstr", "ustr" or such. + +Always unicode8 with explicit u8 manipulations. Unicode "characters" will come +as function calls over the base implementation, probably in another string class. + +## Default string + +Efficiently growable via concantenation. Supports a variety of IO/printf functions. +The underlying array can be shared mutable if you ask for this. +The defaults will always be private mutable and read-only shared. + +```java +struct str { + + // Add a string to the string, possibly growing + val add = { str str -> + val self = this; + for( int i=0; i + if( len >= # ) return copy(# ? #*2 : 1).add(e); + [size++] = e; + return this; + }; + + // Copy 'this' to a new larger size + val copy = { int sz2 -> + val v2 = new str[sz2]; + for( int i=0; i sys.libc.write(1/*stdout*/,[]/*array base as argument to C*/,len); return this; } + + u32 len; // In-use size + u32 #; // Max length is 4Gig, although variants can request smaller lengths + u8 []; // Character data +}; +``` + + +### A Final String + +Intended to be a **immutable** string, possibly used as a base for a **secure** +string. This needs to be created all at once, and probably requires a +defensive copy. + +```java +struct sstr { + // Possible static call syntax; no references to "this" + val make { str str -> new sstr[str#,str.at] }; + + // Array contents are immutable. + // Still figuring out good syntax for this. + u8 #; + u8 [~]; // Middlin '~'? Makes primitive array contents *immutable* and requires constructor syntax +}; + +// Construction requires a function that produces the array contents. +// Here we are defining a defensive copy over `buf`. +sstr safe = new sstr[buf.len,{ int idx -> buf[idx]; }]; +sstr safe = sstr.make("abc"); // Defensive copy is made +``` + + +### A Extendable String (aka StringBuilder or StringBuffer) + +This API is intended to be a drop-in for a `StringBuilder` style, and takes an +extra indirection with each usage. + +```java +struct xstr { + str !_str; // The extra indirection is here + val add = { str str -> _str = str.add2(str); }; + val write = { int fd -> _str.write(fd); }; +}; +``` + +### A Unicode String + +This API is intended to support unicode character manipulation over a plain `str` + +```java +struct ustr { + // Assuming idx is at the start of a character, return the character + // as a 32b integer. Returns junk integer if not at a character start. + // For all ASCII strings this will amount to a simple byte load. + // For bytes with high order bits set, this may read 1-4 characters. + val at = { int idx -> ... }; +}; +// Unicode character iterator +struct ucharator { +// Return successive unicode character codes +}; +``` + +### A "C" string + +A zero-terminated string, but the length has to be available directly + +```java +struct cstr { + u8 #; // length including the trailing zero + u8[]; // always ends in a zero byte; suitable for direct passing to C string functions +}; +``` + +### A "german string": a very short string that can exist in a register + +Length in memory is 1 byte (3 bits?) of length, then 0-7 chars; max 8 chars. +Fits in a 64 bit register. + +```java +struct gstr { // aka germanString, or blend string and register "streg" + u3 #; // Size is 0-7 bytes??? + u8 []; // Up to 7 bytes +} +// Using inlined object (see below). All these strings are short and are +// represented as packed integers in a 64b register. +*gstr prize1 = "gold"; +*gstr prize2 = "silver"; +*gstr prize3 = "bronze"; + +``` + +### Main becomes `val main = { str[] args -> ...}` + + +## Structs can be inlined + +Structs are normally by-reference, but can be converted to by-value by using +the `*` operator, pronounced "contents of" (this name might be changed to +e.g. `inline`). + +This is a name-space change only, you cannot take the address of the internal +struct without entirely consuming its lifetime. No "escaping". + +```java +// 2 64-bit words, no other overheads +struct Complex { f64 x,y; val len = { ->Math.sqrt(x*x+y*y); }; }; + +// Current Simple rules: always by-reference +struct ByRef { + Complex c; // 4-byte pointer to a Complex +}; +print(new ByRef.c.y); // Lookup requires 1 extra memory load from ref to c + +// New behavior: by value (inlining a struct) +// The '*' syntax indicator can be something else, e.g. a keyword "inline" +// I am pronouncing '*' as "contents of" +struct ByValue { + *Complex c; // c is inlined, full 16 bytes into ByValue +}; +print(new ByValue.c.y); // x,y inlined into ByValue, no extra memory load + +// Mixing Refs and Struct Values. Basically, a ref can be converted +// to a value by taking the "contents of" the ref, and values cannot +// be *converted* to a ref, although they can be copied over a ref. +ref. c = val.c; // Error, mixing refs and values +ref.*c = val.c; // Allowed, whole structure copy. Does not allocate, requires ref.c not-null +val. c = ref.c; // Error, mixing refs and values +val. c = *ref.c; // Allowed, whole structure copy +ref. c = ref.c; // Allowed, pointer copy +val. c = val.c; // Allowed, whole structure copy. +val *c = val.c; // Error, cannot take "contents of" a value + +// Alternative syntax, to avoid the "contents of" being on LHS and field applying to being on RHS: +ref.c = val.*c; // Assign into ref.c the "contents of" val.c + +// Arrays of inlined structures +var ary = new *Complex[99]; // Array of 99 Complex objects, inlined + +// Calling methods has the same syntax +ref.c.len(); +val.c.len(); + +// Function arguments use the same typing, so can pass-by-value +val math.sin = { *Complex c -> ... }; // Passes a complex by value +math.sin(ref.*c); // Requires de-reference to pass by value +math.sin(val. c); // Pass by value + +``` + + + +## RoadMap for other chapters + +`&&` and `||` +Drop `int` to `i32`. +Drop `TypeMemPtr` to 4 bytes; using `mmap` to make heap in low 4G. +Add optimistic pass SCCP to `opto` +Default parser for-loop construction, or partial peel. + `for( init; test; next ) body` +becomes: +```java +{ init; // Normal scope entry to bound lifetime of index variable +if( !test ) { // Zero-trip count test + do { + body; + next; + } while( test ); // Exit test at loop bottom by default +} }; +``` diff --git a/simple.bat b/simple.bat deleted file mode 100644 index 05732113..00000000 --- a/simple.bat +++ /dev/null @@ -1,2 +0,0 @@ -echo off -java -cp "build/classes/test;build/classes/main;lib/hamcrest-core-1.3.jar;lib/junit-4.12.jar" com.seaofnodes.simple.Simple %* diff --git a/simple.sh b/simple.sh deleted file mode 100644 index d8335f44..00000000 --- a/simple.sh +++ /dev/null @@ -1,2 +0,0 @@ -#! /bin/sh -java -ea -cp "build/classes/test:build/classes/main:lib/hamcrest-core-1.3.jar:lib/junit-4.12.jar" com.seaofnodes.simple.Simple $@ diff --git a/src/main/java/com/seaofnodes/simple/IterPeeps.java b/src/main/java/com/seaofnodes/simple/IterPeeps.java deleted file mode 100644 index 9ae4231e..00000000 --- a/src/main/java/com/seaofnodes/simple/IterPeeps.java +++ /dev/null @@ -1,201 +0,0 @@ -package com.seaofnodes.simple; - -import com.seaofnodes.simple.codegen.CodeGen; -import com.seaofnodes.simple.util.Ary; -import com.seaofnodes.simple.node.*; -import com.seaofnodes.simple.print.JSViewer; -import java.util.Arrays; -import java.util.BitSet; -import java.util.Random; - -/** - * The IterPeeps runs after parsing. It iterates the peepholes to a fixed point - * so that no more peepholes apply. This should be linear because peepholes rarely - * (never?) increase code size. The graph should monotonically reduce in some - * dimension, which is usually size. It might also reduce in e.g. number of - * MulNodes or Load/Store nodes, swapping out more "expensive" Nodes for cheaper - * ones. - *
- * The theoretical overall worklist is mindless just grabbing the next thing and - * doing it. If the graph changes, put the neighbors on the worklist. - *
- * Lather, Rinse, Repeat until the worklist runs dry. - *

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

    - *
  • Nodes have uses; replacing some set of Nodes with another requires more graph - * reworking. Not rocket science, but it can be fiddly. Its helpful to have a - * small set of graph munging utilities, and the strong invariant that the graph - * is stable and correct between peepholes. In our case `Node.subsume` does - * most of the munging, building on our prior stable Node utilities.
  • - * - *
  • Changing a Node also changes the graph "neighborhood". The neighbors need to - * be checked to see if THEY can also peephole, and so on. After any peephole - * or graph update we put a Nodes uses and defs on the worklist.
  • - * - *
  • Our strong invariant is that for all Nodes, either they are on the worklist - * OR no peephole applies. This invariant is easy to check, although expensive. - * Basically the normal "iterate peepholes to a fixed point" is linear, and this - * check is linear at each peephole step... so quadratic overall. Its a useful - * assert, but one we can disable once the overall algorithm is stable - and - * then turn it back on again when some new set of peepholes is misbehaving. - * The code for this is turned on in `IterPeeps.iterate` as `assert - * progressOnList(stop);`
  • - *
- */ -public class IterPeeps { - - private final WorkList _work; - - public IterPeeps( long seed ) { _work = new WorkList<>(seed); } - - @SuppressWarnings("unchecked") - public N add( N n ) { return (N)_work.push(n); } - - public void addAll( Ary ary ) { _work.addAll(ary); } - - /** - * Iterate peepholes to a fixed point - */ - public void iterate( CodeGen code ) { - assert progressOnList(code); - int cnt=0; - - Node n; - while( (n=_work.pop()) != null ) { - if( n.isDead() ) continue; - cnt++; // Useful for debugging, searching which peephole broke things - Node x = n.peepholeOpt(); - if( x != null ) { - if( x.isDead() ) continue; - // peepholeOpt can return brand-new nodes, needing an initial type set - if( x._type==null ) x.setType(x.compute()); - // Changes require neighbors onto the worklist - if( x != n || !(x instanceof ConstantNode) ) { - // All outputs of n (changing node) not x (prior existing node). - for( Node z : n._outputs ) _work.push(z); - // Everybody gets a free "go again" in case they didn't get - // made in their final form. - _work.push(x); - // If the result is not self, revisit all inputs (because - // there's a new user), and replace in the graph. - if( x != n ) { - for( Node z : n. _inputs ) _work.push(z); - for( Node z : x._outputs ) _work.push(z); - n.subsume(x); - } - } - // If there are distant neighbors, move to worklist - n.moveDepsToWorklist(); - JSViewer.show(); // Show again - assert progressOnList(code); // Very expensive assert - } - if( n.isUnused() && !(n instanceof StopNode) ) - n.kill(); // Just plain dead - } - - } - - // Visit ALL nodes and confirm the invariant: - // Either you are on the _work worklist OR running `iter()` makes no progress. - - // This invariant ensures that no progress is missed, i.e., when the - // worklist is empty we have indeed done all that can be done. To help - // with debugging, the {@code assert} is broken out in a place where it is easy to - // stop if a change is found. - - // Also, the normal usage of `iter()` may attempt peepholes with distance - // neighbors and these should fail, but will then try to add dependencies - // {@link #Node.addDep} which is a side effect in an assert. The {@link - // #midAssert} is used to stop this side effect. - private boolean progressOnList(CodeGen code) { - code._midAssert = true; - Node changed = code._stop.walk( n -> { - Node m = n; - if( n.compute().isa(n._type) && (!n.iskeep() || n._nid<=6) ) { // Types must be forwards, even if on worklist - if( _work.on(n) ) return null; - m = n.peepholeOpt(); - if( m==null ) return null; - } - System.err.println("BREAK HERE FOR BUG"); - return m; - }); - code._midAssert = false; - return changed==null; - } - - /** - * Classic WorkList, with a fast add/remove, dup removal, random pull. - * The Node's nid is used to check membership in the worklist. - */ - @SuppressWarnings("unchecked") - public static class WorkList { - - private Node[] _es; - private int _len; - private final BitSet _on; // Bit set if Node._nid is on WorkList - private final Random _R; // For randomizing pull from the WorkList - private final long _seed; - - /* Useful stat - how many nodes are processed in the post parse iterative opt */ - private long _totalWork = 0; - - public WorkList() { this(123); } - WorkList(long seed) { - _es = new Node[1]; - _len=0; - _on = new BitSet(); - _seed = seed; - _R = new Random(); - _R.setSeed(_seed); - } - - /** - * Pushes a Node on the WorkList, ensuring no duplicates - * If Node is null it will not be added. - */ - public E push( E x ) { - if( x==null ) return null; - int idx = x._nid; - if( !_on.get(idx) ) { - _on.set(idx); - if( _len==_es.length ) - _es = Arrays.copyOf(_es,_len<<1); - _es[_len++] = x; - _totalWork++; - } - return x; - } - - public void addAll( Ary ary ) { - for( E n : ary ) - push(n); - } - - /** - * True if Node is on the WorkList - */ - boolean on( E x ) { return _on.get(x._nid); } - boolean isEmpty() { return _len==0; } - - /** - * Removes a random Node from the WorkList; null if WorkList is empty - */ - public E pop() { - if( _len == 0 ) return null; - int idx = _R.nextInt(_len); - E x = (E)_es[idx]; - _es[idx] = _es[--_len]; // Compress array - _on.clear(x._nid); - return x; - } - - public void clear() { - _len = 0; - _on.clear(); - _R.setSeed(_seed); - _totalWork = 0; - } - } -} diff --git a/src/main/java/com/seaofnodes/simple/Parser.java b/src/main/java/com/seaofnodes/simple/Parser.java deleted file mode 100644 index 1db873e0..00000000 --- a/src/main/java/com/seaofnodes/simple/Parser.java +++ /dev/null @@ -1,2002 +0,0 @@ -package com.seaofnodes.simple; - -import com.seaofnodes.simple.codegen.CodeGen; -import com.seaofnodes.simple.node.*; -import com.seaofnodes.simple.print.GraphVisualizer; -import com.seaofnodes.simple.type.*; -import com.seaofnodes.simple.util.Ary; -import com.seaofnodes.simple.util.Utils; -import com.seaofnodes.simple.node.ScopeNode.Kind; -import java.util.*; - -/** - * The Parser converts a Simple source program to the Sea of Nodes intermediate - * representation directly in one pass. There is no intermediate Abstract - * Syntax Tree structure. - *

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

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

- * We keep a list of all ScopeNodes so that we can show them in graphs. - * @see #parseIf() - * @see #_xScopes - */ - public ScopeNode _scope; - - /** - * List of keywords disallowed as identifiers - */ - public static final HashSet KEYWORDS = new HashSet<>(){{ - add("bool"); - add("break"); - add("byte"); - add("continue"); - add("else"); - add("f32"); - add("f64"); - add("false"); - add("flt"); - add("i16"); - add("i32"); - add("i64"); - add("i8"); - add("if"); - add("int"); - add("new"); - add("null"); - add("return"); - add("struct"); - add("true"); - add("u1"); - add("u16"); - add("u32"); - add("u8"); - add("while"); - }}; - - /** - * We clone ScopeNodes when control flows branch; it is useful to have - * a list of all active ScopeNodes for purposes of visualization of the SoN graph - */ - public final Stack _xScopes = new Stack<>(); - - ScopeNode _continueScope; - ScopeNode _breakScope; // Merge all the while-breaks here - FunNode _fun; // Current function being parsed - - // Mapping from a type name to a Type. The string name matches - // `type.str()` call. - public static HashMap TYPES = defaultTypes(); - - // Mapping from a type name to the constructor for a Type. - public final HashMap INITS; - - - public Parser(CodeGen code, TypeInteger arg) { - _code = code; - _scope = new ScopeNode(); - _continueScope = _breakScope = null; - ZERO = con(TypeInteger.ZERO).keep(); - NIL = con(Type.NIL).keep(); - XCTRL= new XCtrlNode().peephole().keep(); - TYPES = defaultTypes(); - INITS = new HashMap<>(); - } - - @Override - public String toString() { return _lexer.toString(); } - - public static HashMap defaultTypes() { - return new HashMap<>() {{ - put("bool",TypeInteger.U1 ); - put("byte",TypeInteger.U8 ); - put("f32" ,TypeFloat .F32); - put("f64" ,TypeFloat .F64); - put("flt" ,TypeFloat .F64); - put("i16" ,TypeInteger.I16); - put("i32" ,TypeInteger.I32); - put("i64" ,TypeInteger.BOT); - put("i8" ,TypeInteger.I8 ); - put("int" ,TypeInteger.BOT); - put("u1" ,TypeInteger.U1 ); - put("u16" ,TypeInteger.U16); - put("u32" ,TypeInteger.U32); - put("u8" ,TypeInteger.U8 ); - put("val" ,Type.TOP); // Marker type, indicates type inference - put("var" ,Type.BOTTOM); // Marker type, indicates type inference - }}; - } - - // Debugging utility to find a Node by index - public Node f(int nid) { return _code.f(nid); } - - private Node ctrl() { return _scope.ctrl(); } - private N ctrl(N n) { return _scope.ctrl(n); } - - public void parse() { - - _scope.define(ScopeNode.CTRL, Type.CONTROL , false, null, _lexer); - _scope.define(ScopeNode.MEM0, TypeMem.BOT , false, null, _lexer); - _scope.define(ScopeNode.ARG0, TypeInteger.BOT, false, null, _lexer); - - ctrl(XCTRL); - _scope.mem(new MemMergeNode(false)); - - // Parse the sys import - _lexer = new Lexer(com.seaofnodes.simple.sys.SYS); - while( !_lexer.isEOF() ) { - parseStatement(); - _lexer.skipWhiteSpace(); - } - - // Reset lexer for program text - _lexer = new Lexer(_code._src); - _xScopes.push(_scope); - - // Parse whole program, as-if function header "{ int arg -> body }" - parseFunctionBody( _code._main,loc(),"arg"); - - // Kill an empty default main. Keep only if it was explicitly defined - // (programmer asked for a "main") or it has stuff (i.e. beginner - // default main). - FunNode main = _code.link(_code._main); - StopNode stop = _code._stop; - if( main.ret().expr()._type==Type.TOP && main.uctrl()==null ) { - // Kill an empty default main; so it does not attempt to put a - // "main" in any final ELF file - main.setDef(1,XCTRL); // Delete default start input - stop.delDef(stop._inputs.find(main.ret())); - } else { - // We have a non-empty default main. - // Check for an explicit main - for( Node n : stop._inputs ) - if( n instanceof FunNode fun && fun._name.equals("main") ) - // Found an explicit "main" AND we have a default "main" - throw error("Cannot define both an explicit main and a default main"); - main._name = "main"; - } - - if( !_lexer.isEOF() ) throw _errorSyntax("unexpected"); - - // Clean up and reset - _xScopes.pop(); - _scope.kill(); - for( StructNode init : INITS.values() ) - init.unkeep().kill(); - INITS.clear(); - stop.peephole(); - } - - /** - * Parses a function body, assuming the header is parsed. - */ - private ReturnNode parseFunctionBody( TypeFunPtr sig, Lexer loc, String... ids) { - // If this is a method, record & restore the existing struct fields, - // which are also the struct initializers. The method might update - // mutable fields, so when the function parse is done reset back to - // pre-function-parse values. - Node[] preParse = new Node[_scope.nIns()]; - for( int i=0; i<_scope.nIns(); i++ ) - preParse[i] = _scope.in(i)==null ? null : _scope.in(i).keep(); - - // Stack parser state on the local Java stack, and unstack it later - FunNode oldfun = _fun; - ScopeNode breakScope = _breakScope; _breakScope = null; - ScopeNode continueScope = _continueScope; _continueScope = null; - - FunNode fun = _fun = (FunNode)peep(new FunNode(loc(),sig,_nestedType,null,_code._start)); - // Once the function header is available, install in linker table - - // allowing recursive functions. Linker matches on declared args and - // exact fidx, and ignores the return (because the fidx will only match - // the exact single function). - _code.link(fun); - - Node rpc = new ParmNode("$rpc",0,TypeRPC.BOT,fun,con(TypeRPC.BOT)).peephole(); - - // Build a multi-exit return point for all function returns - RegionNode r = new RegionNode( null,null,null).init(); - assert r.inProgress(); - PhiNode rmem = new PhiNode(ScopeNode.MEM0,TypeMem.BOT,r,null).init(); - PhiNode rrez = new PhiNode(ScopeNode.ARG0,Type.BOTTOM,r,null).init(); - ReturnNode ret = new ReturnNode(r, rmem, rrez, rpc, fun).init(); - fun.setRet(ret); - assert ret.inProgress(); - _code._stop.addDef(ret); - - - // Pre-call the function from Start, with worse-case arguments. This - // represents all the future, yet-to-be-parsed functions calls and - // external calls. - _scope.push(new Kind.Func()); - ctrl(fun); // Scope control from function - // Private mem alias tracking per function - MemMergeNode mem = new MemMergeNode(true); - mem.addDef(null); // Alias#0 - mem.addDef(new ParmNode(ScopeNode.MEM0,1,TypeMem.BOT,fun,con(TypeMem.BOT)).peephole()); // All aliases - _scope.mem(mem); - // All args, "as-if" called externally - for( int i=0; i - * '{' statements '}' - * - * Does not parse the opening or closing '{}' - * @return a {@link Node} or {@code null} - */ - private Node parseBlock(Kind kind) { - // Enter a new scope - _scope.push(kind); - Node last = ZERO; - while (!peek('}') && !_lexer.isEOF()) - last = parseStatement(); - // Exit scope - last.keep(); - _scope.pop(); - return last.unkeep(); - } - - /** - * Parses a statement - * - *

-     *     returnStatement | declStatement | blockStatement | ifStatement | expressionStatement
-     * 
- * @return a {@link Node} or {@code null} - */ - private Node parseStatement() { - if( matchx("return") ) return parseReturn(); - else if (matchx("if") ) return parseIf(); - else if (matchx("while") ) return parseWhile(); - else if (matchx("for") ) return parseFor(); - else if (matchx("break") ) return parseBreak(); - else if (matchx("continue")) return parseContinue(); - else if (matchx("struct") ) return parseStruct(); - else if (matchx("#showGraph")) return require(showGraph(),";"); - else if (matchx(";") ) return null; // Empty statement - // Break ambiguity around leading function types and starting a block - else if (peek('{') && !isTypeFun() ) { - match("{"); - return require(parseBlock(new Kind.Block()),"}"); - } - // Declaration or normal assignment/expression - else return parseDeclarationStatement(); - } - - /** - * Parses a while statement - * - *
-     *     while ( expression ) statement
-     * 
- * @return a {@link Node}, never {@code null} - */ - private Node parseWhile() { - require("("); - return parseLooping(false); - } - - - /** - * Parses a for statement - * - *
-     *     for( var x=init; test; incr ) body
-     * 
- * @return a {@link Node}, never {@code null} - */ - private Node parseFor() { - // { var x=init,y=init,...; - // while( test ) { - // body; - // next; - // } - // } - require("("); - _scope.push(new Kind.Block()); // Scope for the index variables - if( !match(";") ) // Can be empty init "for(;test;next) body" - parseDeclarationStatement(); // Non-empty init - Node rez = parseLooping(true); - _scope.pop(); // Exit index variable scope - return rez; - } - - // Shared by `for` and `while` - private Node parseLooping( boolean doFor ) { - - var savedContinueScope = _continueScope; - var savedBreakScope = _breakScope; - - // Loop region has two control inputs, the first is the entry - // point, and second is back edge that is set after loop is parsed - // (see end_loop() call below). Note that the absence of back edge is - // used as an indicator to switch off peepholes of the region and - // associated phis; see {@code inProgress()}. - - ctrl(new LoopNode(loc(),ctrl()).peephole()); // Note we set back edge to null here - - // At loop head, we clone the current Scope (this includes all - // names in every nesting level within the Scope). - // We create phis eagerly for all the names we find, see dup(). - - // Save the current scope as the loop head - ScopeNode head = _scope.keep(); - // Clone the head Scope to create a new Scope for the body. - // Create phis eagerly as part of cloning - _xScopes.push(_scope = _scope.dup(true)); // The true argument triggers creating phis - - // Parse predicate - var pred = peek(';') ? con(1) : parseAsgn(); - require( doFor ? ";" : ")" ); - - // IfNode takes current control and predicate - Node ifNode = new IfNode(ctrl(), pred.keep()).peephole(); - // Setup projection nodes - Node ifT = new CProjNode(ifNode. keep(), 0, "True" ).peephole().keep(); - Node ifF = new CProjNode(ifNode.unkeep(), 1, "False").peephole(); - - // for( ;;next ) body - int nextPos = -1, nextEnd = -1; - if( doFor ) { - // Skip the next expression and parse it later - nextPos = pos(); - skipAsgn(); - nextEnd = pos(); - require(")"); - } - - // Clone the body Scope to create the break/exit Scope which accounts for any - // side effects in the predicate. The break/exit Scope will be the final - // scope after the loop, and its control input is the False branch of - // the loop predicate. Note that body Scope is still our current scope. - ctrl(ifF); - _xScopes.push(_breakScope = _scope.dup()); - _breakScope.addGuards(ifF,pred,true); // Up-cast predicate - - // No continues yet - _continueScope = null; - - // Parse the true side, which corresponds to loop body - // Our current scope is the body Scope - ctrl(ifT.unkeep()); // set ctrl token to ifTrue projection - _scope.addGuards(ifT,pred.unkeep(),false); // Up-cast predicate - parseStatement().isKill(); // Parse loop body - _scope.removeGuards(ifT); - - // Merge the loop bottom into other continue statements - if (_continueScope != null) { - _continueScope = jumpTo(_continueScope); - _scope.kill(); - _scope = _continueScope; - } - - // Now append the next code onto the body code - if( doFor ) { - int old = pos(nextPos); - if( !peek(')') ) - parseAsgn(); - if( pos() != nextEnd ) - throw errorSyntax( "Unexpected code after expression" ); - pos(old); - } - - // The true branch loops back, so whatever is current _scope.ctrl gets - // added to head loop as input. endLoop() updates the head scope, and - // goes through all the phis that were created earlier. For each phi, - // it sets the second input to the corresponding input from the back - // edge. If the phi is redundant, it is replaced by its sole input. - var exit = _breakScope; - head.endLoop(_scope, exit); - head.unkeep().kill(); - - _xScopes.pop(); // Cleanup - _xScopes.pop(); // Cleanup - - _continueScope = savedContinueScope; - _breakScope = savedBreakScope; - - // At exit the false control is the current control, and - // the scope is the exit scope after the exit test. - // During sys parsing, there is no xscope here. - if( !_xScopes.isEmpty() ) { - _xScopes.pop(); - _xScopes.push( exit ); - } - _scope = exit; - return ZERO; - } - - private ScopeNode jumpTo(ScopeNode toScope) { - ScopeNode cur = _scope.dup(); - ctrl(XCTRL); // Kill current scope - // Prune nested lexical scopes that have depth > than the loop head - // We use _breakScope as a proxy for the loop head scope to obtain the depth - while( cur.depth() > _breakScope.depth() ) - cur.pop(); - // If this is a continue then first time the target is null - // So we just use the pruned current scope as the base for the - // "continue" - if( toScope == null ) - return cur; - // toScope is either the break scope, or a scope that was created here - assert toScope.depth() <= _breakScope.depth(); - toScope.ctrl(toScope.mergeScopes(cur,loc()).peephole()); - return toScope; - } - - private void checkLoopActive() { if (_breakScope == null) throw error("No active loop for a break or continue"); } - - private Node parseContinue() { checkLoopActive(); _continueScope = require(jumpTo( _continueScope ),";"); return ZERO; } - private Node parseBreak () { - checkLoopActive(); - // At the time of the break, and loop-exit conditions are only valid if - // they are ALSO valid at the break. It is the intersection of - // conditions here, not the union. - _breakScope.removeGuards(_breakScope.ctrl()); - _breakScope = require(jumpTo(_breakScope ),";"); - _breakScope.addGuards(_breakScope.ctrl(), null, false); - return ZERO; - } - - // Look for an unbalanced `)`, skipping balanced - private void skipAsgn() { - int paren=0; - while( true ) - // Next X char handles skipping complex comments - switch( _lexer.nextXChar() ) { - case Character.MAX_VALUE: - throw Utils.TODO(); - case ')': - if( --paren<0 ) { - posT( pos() - 1 ); - return; // Leave the `)` behind - } - break; - case '(': paren ++; break; - default: break; - } - } - - - /** - * Parses a statement - * - *
-     *     if ( expression ) statement [else statement]
-     * 
- * @return a {@link Node}, never {@code null} - */ - private Node parseIf() { - // Parse predicate - require("("); - var pred = require(parseAsgn(), ")"); - return parseTrinary(pred,true,"else"); - } - - // Parse a conditional expression, merging results. - private Node parseTrinary( Node pred, boolean stmt, String fside ) { - pred.keep(); - - // IfNode takes current control and predicate - Node ifNode = new IfNode(ctrl(), pred).peephole(); - // Setup projection nodes - Node ifT = new CProjNode(ifNode. keep(), 0, "True" ).peephole().keep(); - Node ifF = new CProjNode(ifNode.unkeep(), 1, "False").peephole().keep(); - // In if true branch, the ifT proj node becomes the ctrl - // But first clone the scope and set it as current - ScopeNode fScope = _scope.dup(); // Duplicate current scope - _xScopes.push(fScope); // For graph visualization we need all scopes - - // Parse the true side - ctrl(ifT.unkeep()); // set ctrl token to ifTrue projection - _scope.addGuards(ifT,pred,false); // Up-cast predicate - Node lhs = (stmt ? parseStatement() : parseAsgn()).keep(); // Parse true-side - _scope.removeGuards(ifT); - - // See if a one-sided def was made: "if(pred) int x = 1;" and throw. - // See if any forward-refs were made, and copy them to the other side: - // "pred ? n*fact(n-1) : 1" - fScope.balanceIf(_scope); - - ScopeNode tScope = _scope; - - // Parse the false side - _scope = fScope; // Restore scope, then parse else block if any - ctrl(ifF.unkeep()); // Ctrl token is now set to ifFalse projection - // Up-cast predicate, even if not else clause, because predicate can - // remain true if the true clause exits: `if( !ptr ) return 0; return ptr.fld;` - _scope.addGuards(ifF,pred,true); - boolean doRHS = match(fside); - Node rhs = (doRHS - ? (stmt ? parseStatement() : parseAsgn()) - : con(lhs._type.makeZero())).keep(); - _scope.removeGuards(ifF); - if( doRHS ) - fScope = _scope; - pred.unkeep(); - - // Check the trinary widening int/flt - if( !stmt ) { - rhs = widenInt( rhs.unkeep(), lhs._type ).keep(); - lhs = widenInt( lhs.unkeep(), rhs._type ).keep(); - } - - _scope = tScope; - _xScopes.pop(); // Discard pushed from graph display - // See if a one-sided def was made: "if(pred) int x = 1;" and throw. - // See if any forward-refs were made, and copy them to the other side: - // "pred ? n*fact(n-1) : 1" - tScope.balanceIf(fScope); - - // Merge results - RegionNode r = ctrl(tScope.mergeScopes(fScope,loc())); - Node ret = peep(new PhiNode("",lhs._type.meet(rhs._type).glb(false),r,lhs.unkeep(),rhs.unkeep())); - // Immediately fail e.g. `arg ? 7 : ptr` - ParseException err; - if( !stmt && (err=ret.err()) !=null ) throw err; - r.peephole(); - return ret; - } - - /** - * Parses a return statement; "return" already parsed. - * The $ctrl edge is killed. - * - *
-     *     'return' expr ;
-     * 
- * @return an expression {@link Node}, never {@code null} - */ - private Node parseReturn() { - var expr = require(parseAsgn(), ";"); - // Need default memory, since it can be lazy, need to force - // a non-lazy Phi - _fun.addReturn(ctrl(), _scope.mem().merge(), expr); - ctrl(XCTRL); // Kill control - return expr; - } - - /** - * Dumps out the node graph - * @return {@code null} - */ - Node showGraph() { - System.out.println(new GraphVisualizer().generateDotOutput(_code._stop,_scope,_xScopes)); - return null; - } - - /** Parse: [name '='] expr - */ - private Node parseAsgn() { - int old = pos(); - String name = _lexer.matchId(); - int old_pre_equals = pos(); - // Just a plain expression, no assignment. - // Distinguish `var==expr` from `var=expr` - if( name==null || KEYWORDS.contains(name) || !matchOpx('=','=') ) - { pos(old); return parseExpression(); } - - // Find variable to update - Var def = _scope.lookup(name); - if( def==null ) - throw error("Undefined name '" + name + "'"); - - // If a method var, allow nested blocks and methods, but not fcns or other structs. - // Else safety check for accessing fields out of function scope. - if( instance(def) ) { - pos(old_pre_equals); // setup for `self.name = expr` - Node self = _scope.in(_scope.lookup("self")); // Insert self reference - return parsePostfixName(self,name); - } - - // TOP fields are for late-initialized fields; these have never - // been written to, and this must be the final write. Other writes - // outside the constructor need to check the final bit. - if( _scope.in(def._idx)._type!=Type.TOP && def._final && - // Inside an allocation, final assign is OK, outside nope. - // The alloc() call added the allocation scope - !(_scope.inAllocation() && def._idx >= _scope._kinds.last(-1)._lexSize) ) - throw error("Cannot reassign final '"+name+"'"); - - // Parse assignment expression - Node expr = parseAsgn(); - - // Lift expression, based on type - Node lift = liftExpr(expr.keep(), def.type(), def._final, true); - // Update - _scope.update(name,lift); - // Return un-lifted expr - return expr.unkeep(); - } - - // Make finals deep; widen ints to floats; narrow wide int types. - // Early error if types do not match variable. - private Node liftExpr( Node expr, Type t, boolean xfinal, boolean isLoad ) { - if( expr._type instanceof TypeMemPtr tmp && TYPES.get(tmp._obj._name).isFRef() ) - throw error("Must define forward ref "+tmp._obj._name); - // Final is deep on ptrs - if( xfinal && t instanceof TypeMemPtr tmp ) { - t = tmp.makeRO(); - expr = peep(new ReadOnlyNode(expr)); - } - // Auto-widen array to i64 (cast ptr to raw int bits) - if( t == TypeInteger.BOT && expr._type instanceof TypeMemPtr tmp && tmp._obj.isAry() ) - expr = peep(new AddNode(peep(new CastNode(t,ctrl(),expr)),off(tmp._obj,"[]"))); - // Auto-widen int to float - expr = widenInt( expr, t ); - // Auto-narrow wide ints to narrow ints. For loads, emit code to force - // the loaded value to match the declared sign/zero bits. For stores, - // just force the type, acting "as if" the store silently truncates. - Type et = expr._type; - if( isLoad ) { expr = zsMask(expr,t); et = expr._type; } - else if( et instanceof TypeInteger && t instanceof TypeInteger ) et=t; - - // Type is sane - if( et!=Type.BOTTOM && !et.shallowISA(t) ) - expr = peep(new CastNode(t,null,expr)); - return expr; - } - - private Node widenInt( Node expr, Type t ) { - return (expr._type instanceof TypeInteger || expr._type==Type.NIL) && t instanceof TypeFloat - ? peep(new ToFloatNode(expr)) : expr; - } - - /** - * Parse declaration or expression statement - * declStmt = type var['=' exprAsgn][, var['=' exprAsgn]]* ';' | exprAsgn ';' - *

- * exprAsgn = var '=' exprAsgn | expr - */ - private Node parseDeclarationStatement() { - int old = pos(); - Type t = type(); - if( peek('.') ) // Ambiguity static vars: "type.var", parse as expression - { pos(old); t=null; } - if( t == null ) - return require(parseAsgn(),";"); - - // now parse var['=' asgnexpr] in a loop - Node n = parseDeclaration(t); - while( match(",") ) - n = parseDeclaration(t); - return require(n,";"); - } - - /** Parse final: [!]var['=' asgn] - */ - private Node parseDeclaration(Type t) { - assert t!=null; - // Has var/val instead of a user-declared type - boolean inferType = t==Type.TOP || t==Type.BOTTOM; - boolean hasBang = match("!"); - Lexer loc = loc(); - String name = requireId(); - // Optional initializing expression follows - boolean xfinal = false; - boolean fld_final = false; // Field is final, but not deeply final - Node expr; - if( match("=") ) { - expr = isExternDecl() - ? externDecl(name,t) - : parseAsgn(); - // TOP means val and val is always final - xfinal = (t==Type.TOP) || - expr instanceof ExternNode || - // BOTTOM is var and var is always not-final - (t!=Type.BOTTOM && - // no Bang AND - !hasBang && - // Locals are not-final by default - !_scope.inFunction() && - // not-null (expecting null to be set to not-null) - expr._type != Type.NIL && - // Pointers are final by default; int/flt are not-final by default. - (t instanceof TypeNil)); - - // var/val, then type comes from expression - if( inferType ) { - if( expr._type==Type.NIL ) - throw error("a not-null/non-zero expression"); - t = expr._type; - if( !xfinal ) t = t.glb(false); // Widen if not final - } - // expr is a constant function - if( t instanceof TypeFunPtr && expr._type instanceof TypeFunPtr tfp && tfp.isConstant() ) { - if( expr instanceof ExternNode ) t = expr._type; // Upgrade declared type to exact function - else _code.link(tfp).setName(name); // Assign debug name to Simple function - } - - } else { - // Need an expression to infer the type. - // Also, if not-null then need an initializing expression. - // Allowed in a class def, because type/init will happen in the constructor. - if( (inferType || (t instanceof TypeNil tn && !tn.nullable() )) && !_scope.inConstructor() ) - throw errorSyntax("=expression"); - // Initial value for uninitialized struct fields. - expr = switch( t ) { - // Nullable pointers get a NIL; not-null get a TOP which - // signals that they *must* be initialized in the constructor. - case TypeNil tn -> tn.nullable() ? NIL : con(Type.TOP); - case TypeInteger ti -> ZERO; - case TypeFloat tf -> con(TypeFloat.FZERO); - // Bottom signals type inference: they must be initialized in - // the constructor and that's when we'll discover the type. - case Type tt -> { assert tt==Type.BOTTOM; yield con(tt); } - }; - // Nullable fields are set in the constructor, but remain shallow final. - // e.g. final pointer to a r/w array - if( t instanceof TypeNil tn && !tn.nullable() && !hasBang ) - fld_final = true; - } - - // Lift expression, based on type - Node lift = liftExpr(expr, t, xfinal, true); - // Rebuild the deep RO 't', not returned from liftExpr - if( xfinal && t instanceof TypeMemPtr tmp ) - t = tmp.makeRO(); - - // Lift type to the declaration. This will report as an error later if - // we cannot lift the type. - if( !lift._type.isa(t) ) - lift = peep(new CastNode(t,null,lift)); - // Define a new name - if( !_scope.define(name,t,xfinal || fld_final,lift, loc) ) - throw error("Redefining name '" + name + "'", loc); - return lift; - } - - - - /** - * Parse a struct declaration, and return the following statement. - * Structs cannot be redefined, but can be nested. - * - * @return zero - */ - private String _nestedType; - private Node parseStruct() { - // "struct" already parsed, so expect the struct name next. - String old = _nestedType; - String typeName = requireId(); - if( old!=null ) typeName = old+"."+typeName; - _nestedType = typeName; - // New declared or forward-ref type - TypeStruct ts = TYPES.get(typeName) instanceof TypeMemPtr tmp ? tmp._obj : TypeStruct.open(typeName); - TYPES.put(typeName,TypeMemPtr.make(ts)); - - // A Block scope parse, and inspect the scope afterward for fields. - _scope.push(new Kind.Define(TypeMemPtr.make(ts))); - require("{"); - int lexlen = _scope._kinds.last()._lexSize, nvar=lexlen; - while (!peek('}') && !_lexer.isEOF()) { - parseStatement().isKill(); - while( nvar < _scope._vars._len ) { - Var v = _scope.var(nvar++); - if( !v.isFRef() ) { - ts = ts.add( Field.make( v._name, v.type(), _code.getALIAS(), v._final, v._final && _scope.in( v._idx )._type != Type.TOP ) ); - TYPES.put( typeName, TypeMemPtr.make( ts ) ); - } - } - } - - // Count struct declarations - int varlen = _scope._vars._len; - ts = ts.close(); - TYPES.put(typeName,TypeMemPtr.make(ts)); - - // Generate an initializing struct - StructNode s = new StructNode(ts); - for( int i=lexlen; i")) || id==null ) - return posT(old1); // Reset lexer to reparse - pos(old2); // Reset lexer to reparse - // Yes a forward ref, so declare it - TYPES.put(tname,t1); - // Return the (array, final) type - return t2; - } - - // Make an array type of t - private TypeMemPtr typeAry( Type t, boolean efinal ) { - if( t instanceof TypeMemPtr tmp && tmp.notNull() ) - throw error("Arrays of reference types must always be nullable"); - String tname = "[]"+t.str(); - TypeMemPtr ta = (TypeMemPtr)TYPES.get(tname); - if( ta==null ) - // Remember final version - TYPES.put(tname, ta = TypeMemPtr.make( TypeStruct.makeAry(tname,TypeInteger.U32,_code.getALIAS(),t,_code.getALIAS(),true) )); - Field elem = ta._obj.field("[]"); - if( elem._final == efinal ) - return ta; - return TypeMemPtr.make( ta._obj.replace(elem.makeFrom(efinal))); - } - - // A function type is `{ type... -> type }` or `{ type }`. - private Type typeFunPtr() { - int old = pos(); // Record lexer position - match("{"); // Skip already-peeked '{' - Type t0 = type(); // Either return or first arg - if( t0==null ) return posT(old); // Not a function - if( match("}") ) // No-arg function { -> type } - return TypeFunPtr.make(match("?"),false,TypeFunPtr.TEMPTY,t0); - Ary ts = new Ary<>(Type.class); - ts.push(t0); // First argument - while( true ) { - if( match("->") ) { // End of arguments, parse return - Type ret = type(); - if( ret==null || !match("}") ) - return posT(old); // Not a function - return TypeFunPtr.make(match("?"),false,ts.asAry(),ret); - } - Type t1 = type(); - if( t1==null ) return posT(old); // Not a function - ts.push(t1); - } - } - - // True if a TypeFunPtr, without advancing parser - private boolean isTypeFun() { - int old = pos(); - if( typeFunPtr()==null ) return false; - pos(old); - return true; - } - - /** - * Parse an expression of the form: - * - *

-     *     expr : bitwise [? expr [: expr]]
-     * 
- * @return an expression {@link Node}, never {@code null} - */ - private Node parseExpression() { - Node expr = parseBitwise(); - return match("?") ? parseTrinary(expr,false,":") : expr; - } - - /** - * Parse an bitwise expression - * - *
-     *     bitwise : compareExpr (('&' | '|' | '^') compareExpr)*
-     * 
- * @return a bitwise expression {@link Node}, never {@code null} - */ - private Node parseBitwise() { - Node lhs = parseComparison(); - while( true ) { - if( false ) ; - else if( match("&") ) lhs = new AndNode(loc(),lhs,null); - else if( match("|") ) lhs = new OrNode(loc(),lhs,null); - else if( match("^") ) lhs = new XorNode(loc(),lhs,null); - else break; - lhs.setDef(2,parseComparison()); - lhs = peep(lhs); - } - return lhs; - } - - /** - * Parse an expression of the form: - * - *
-     *     expr : additiveExpr op additiveExpr
-     * 
- * @return an comparator expression {@link Node}, never {@code null} - */ - private Node parseComparison() { - var lhs = parseShift(); - while( true ) { - int idx=0; boolean negate=false; - // Test for any local nodes made, and "keep" lhs during peepholes - if( false ) ; - else if( match("==") ) { idx=2; lhs = new BoolNode.EQ(lhs, null); } - else if( match("!=") ) { idx=2; lhs = new BoolNode.EQ(lhs, null); negate=true; } - else if( match("<=") ) { idx=2; lhs = new BoolNode.LE(lhs, null); } - else if( match("<" ) ) { idx=2; lhs = new BoolNode.LT(lhs, null); } - else if( match(">=") ) { idx=1; lhs = new BoolNode.LE(null, lhs); } - else if( match(">" ) ) { idx=1; lhs = new BoolNode.LT(null, lhs); } - else break; - // Peepholes can fire, but lhs is already "hooked", kept alive - lhs.setDef(idx,parseShift()); - lhs = peep(lhs.widen()); - if( negate ) // Extra negate for != - lhs = peep(new NotNode(lhs)); - } - return lhs; - } - - /** - * Parse an additive expression - * - *
-     *     shiftExpr : additiveExpr (('<<' | '>>' | '>>>') additiveExpr)*
-     * 
- * @return a shift expression {@link Node}, never {@code null} - */ - private Node parseShift() { - Node lhs = parseAddition(); - while( true ) { - if( false ) ; - else if( match("<<") ) lhs = new ShlNode(loc(),lhs,null); - else if( match(">>>")) lhs = new ShrNode(loc(),lhs,null); - else if( match(">>") ) lhs = new SarNode(loc(),lhs,null); - else break; - lhs.setDef(2,parseAddition()); - ParseException err; - if( (err=lhs.err()) != null ) - throw err; - lhs = peep(lhs.widen()); - } - return lhs; - } - - /** - * Parse an additive expression - * - *
-     *     additiveExpr : multiplicativeExpr (('+' | '-') multiplicativeExpr)*
-     * 
- * @return an add expression {@link Node}, never {@code null} - */ - private Node parseAddition() { - Node lhs = parseMultiplication(); - while( true ) { - if( false ) ; - else if( match("+") ) lhs = new AddNode(lhs,null); - else if( match("-") ) lhs = new SubNode(lhs,null); - else break; - lhs.setDef(2,parseMultiplication()); - lhs = peep(lhs.widen()); - } - return lhs; - } - - /** - * Parse an multiplicativeExpr expression - * - *
-     *     multiplicativeExpr : unaryExpr (('*' | '/') unaryExpr)*
-     * 
- * @return a multiply expression {@link Node}, never {@code null} - */ - private Node parseMultiplication() { - var lhs = parseUnary(); - while( true ) { - if( false ) ; - else if( match("*") ) lhs = new MulNode(lhs,null); - else if( match("/") ) lhs = new DivNode(lhs,null); - else break; - lhs.setDef(2,parseUnary()); - lhs = peep(lhs.widen()); - } - return lhs; - } - - /** - * Parse a unary minus expression. - * - *
-     *     unaryExpr : ('-') unaryExpr | '!') unaryExpr | postfixExpr | primaryExpr | '--' Id | '++' Id
-     * 
- * @return a unary expression {@link Node}, never {@code null} - */ - private Node parseUnary() { - // Pre-dec/pre-inc - int old = pos(); - if( match("--") || match("++") ) { - int delta = _lexer.peek(-1)=='+' ? 1 : -1; // Pre vs post - String name = _lexer.matchId(); - if( name!=null ) { - Var n = _scope.lookup(name); - if( n != null && !(n.type() instanceof TypeMemPtr) ) { - if( n._final ) - throw error("Cannot reassign final '"+n._name+"'"); - Node expr = n.type() instanceof TypeFloat - ? peep(new AddFNode(_scope.in(n),con(TypeFloat.constant(delta)))) - : zsMask(peep(new AddNode(_scope.in(n),con(delta))),n.type()); - _scope.update(n,expr); - return expr; - } - } - // Reset, try again - pos(old); - } - if (match("-")) return peep(new MinusNode(parseUnary()).widen()); - if (match("!")) return peep(new NotNode(parseUnary())); - // Else be a primary expression - return parsePrimary(); - } - - /** - * Parse a primary expression: - * - *
-     *     primaryExpr : integerLiteral | "string" | 'char' | true | false | null |
-     *                   new Type | '(' expression ')' | Id['++','--'] |
-     *                   [static.]* Id
-     * 
- * @return a primary {@link Node}, never {@code null} - */ - private Node parsePrimary() { - if( _lexer.isNumber(_lexer.peek()) ) return parseLiteral(); - if( _lexer.peek('"') ) return newString(parseString()); - if( matchx("true" ) ) return con(1); - if( matchx("false") ) return ZERO; - if( matchx("null" ) ) return NIL; - if( match ("'" ) ) return parseChar(); - if( match ("(" ) ) return parsePostfix(require(parseAsgn(), ")")); - if( matchx("new" ) ) return parsePostfix(alloc()); - if( match ("{" ) ) return parsePostfix(require(func(),"}")); - - // Parse static variable lookup: `type.fld` in the type namespace - // CNC: not a full type parse, just a typename parse - int pos = pos(); - Type t = type(); - if( t!=null ) { - if( peek('.') ) - return parsePostfix(con(t)); - //pos(pos); // TODO: not a `type.fld` - //return null; - throw Utils.TODO(); - } - - // Expect an identifier now - - // - if null, make fref, normal var handling - // - if final constant, normal var handling - // - if method var, scan skipping blocks & methods, must hit correct struct no fcns - // - if normal var, scan skipping blocks, allowing last fcn - // - NO fcns nested in methods, NO methods nested in fcns - - String id = _lexer.matchId(); - if( id == null || KEYWORDS.contains(id) ) - throw errorSyntax("an identifier or expression"); - Var var = _scope.lookup(id); - - if( var==null ) { - // If missing, assume a forward reference - _scope.define(id, FRefNode.FREF_TYPE, true, XCTRL, loc()); - var = _scope.lookup(id); - } - - // If a method var, allow nested blocks and methods, but not fcns or other structs. - // Else safety check for accessing fields out of function scope. - if( instance(var) ) { - Node self = _scope.in(_scope.lookup("self")); // Insert self reference - return parsePostfixName(self,id); - } - - // Load local value - Node rvalue = _scope.in(var); - if( rvalue._type == Type.BOTTOM ) - if( rvalue instanceof FRefNode ) - return parsePostfix(rvalue); - else throw error("Cannot read uninitialized field '"+id+"'"); - - // Check for assign-update, x += e0; - char ch = _lexer.matchOperAssign(); - if( ch==0 ) // Normal primary, check for postfix updates - return parsePostfix(rvalue); - // Assign-update direct into Scope - Node op = opAssign(ch,rvalue, var.type() ); - _scope.update(var,op); - return postfix(ch) ? rvalue.unkeep() : op; - } - - // true if instance variable lookup (var is in any number of nested blocks, - // then an instance function then inside a struct declaration). - private boolean instance(Var var ) { - // If a method var, allow nested blocks and methods, but not fcns or other structs - Kind kind = _scope.kind(var); - if( kind instanceof Kind.Define ) { - for( int i = _scope.depth()-1; i>=0 && var._idx < _scope._kinds.at(i)._lexSize; i-- ) { - Kind xkind = _scope._kinds.at(i); - if( xkind instanceof Kind.Block ) continue; - if( xkind instanceof Kind.Func ) { - if( _scope._kinds.at(i-1)==kind ) - return true; // Instance variable: lookup in a method in a decl - } - throw Utils.TODO(); - } - } else { - // If a normal var, allow any nested blocks, and a last fcn - - // but not functions further out (which involves capture). - Node def = _scope.in(var._idx); - for( int i = _scope.depth()-1; i>=0 && var._idx < _scope._kinds.at(i)._lexSize; i-- ) - if( _scope._kinds.at(i) instanceof Kind.Func && - !(def instanceof FRefNode) && !(var._final && def._type.isConstant()) ) - throw error("Variable '"+var._name+"' is out of function scope and must be a final constant"); - } - return false; - } - - // Check for assign-update, "x += e0". - // Returns "x + e0"; the caller assigns value. - // if ch is postfix, then lhs is kept and caller will unkeep and return it. - private Node opAssign( char ch, Node lhs, Type t ) { - // RHS of the update. - lhs.keep(); // Alive across parseAsgn - Node rhs = - (byte)ch == 1 ? con( 1) : // var++ - (byte)ch == -1 ? con(-1) : // var-- - parseAsgn(); // var op= rhs - if( !postfix(ch) ) lhs.unkeep(); // Allow to die in next peep - // 4 cases: - // int + int ==>> narrow int - // int + flt ==>> error, caller must fail assigning flt into int - // flt + int ==>> use float op, wrap toFloat() - // flt + flt ==>> use float op - Node op = switch(ch) { - case 1, (char)-1, - '+' -> new AddNode(lhs,rhs); - case '-' -> new SubNode(lhs,rhs); - case '*' -> new MulNode(lhs,rhs); - case '/' -> new DivNode(lhs,rhs); - default -> throw Utils.TODO(); - }; - // Convert to float ops, or narrow int types; error if not declared type. - // Also, if postfix LHS is still keep() - return liftExpr(peep(op.widen()),t,false,true); - } - - - /** - Parse an allocation - */ - private Node alloc() { - Type t = type(); - if( t==null ) throw error("Expected a type"); - // Parse ary[ length_expr ] - if( match("[") ) { - if( !t.makeZero().isa(t) ) - throw error("Cannot allocate a non-nullable, since arrays always zero/null fill"); - Node len = parseAsgn(); - if( !(len._type instanceof TypeInteger) ) - throw error("Cannot allocate an array with length "+len._type); - require("]"); - TypeMemPtr tmp = typeAry(t,false); - return newArray(tmp._obj,len); - } - - if( !(t instanceof TypeMemPtr tmp) ) - throw error("Cannot allocate a "+t.str()); - - // Parse new struct { default_initialization } - StructNode s = INITS.get(tmp._obj._name); - if( s==null ) throw error("Unknown struct type '" + tmp._obj._name + "'"); - - Field[] fs = s._ts._fields; - // if the object is fully initialized, we can skip a block here. - // Check for constructor block: - boolean hasConstructor = match("{"); - Ary init=s._inputs; int idx=0; - if( hasConstructor ) { - idx = _scope.nIns(); - // Push a scope, and pre-assign all struct fields. - _scope.push(new Kind.Block()); - Lexer loc = loc(); - for( int i=0; i init ) { - Field[] fs = obj._fields; - if( fs==null ) - throw error("Unknown struct type '" + obj._name + "'"); - int len = fs.length; - Node[] ns = new Node[2+len]; - ns[0] = ctrl(); // Control in slot 0 - // Total allocated length in bytes - ns[1] = size; - // Memory aliases for every field - for( int i = 0; i < len; i++ ) - if( !fs[i]._one ) - ns[2+i] = memAlias(fs[i]._alias); - Node nnn = new NewNode(TypeMemPtr.make(obj), ns).peephole().keep(); - for( int i = 0; i < len; i++ ) - if( !fs[i]._one ) - memAlias(fs[i]._alias, new ProjNode(nnn,i+2,memName(fs[i]._alias)).peephole()); - Node ptr = new ProjNode(nnn.unkeep(),1,obj._name).peephole().keep(); - - // Initial nonzero values for every field - for( int i = 0; i < len; i++ ) { - Node val = init.get( i + idx ); - if( !fs[i]._one && val._type != val._type.makeZero() ) { - Node mem = memAlias(fs[i]._alias); - Node st = new StoreNode(loc(),fs[i]._fname,fs[i]._alias,fs[i]._t,mem,ptr,off(obj,fs[i]._fname),val,true).peephole(); - memAlias(fs[i]._alias,st); - } - } - - return ptr.unkeep(); - } - - private static final Ary ALTMP = new Ary<>(Node.class); - private Node newArray(TypeStruct ary, Node len) { - ConFldOffNode base = off(ary, "[]"); - int scale= ary.aryScale(); - Node size = peep(new AddNode(base,peep(new ShlNode(null,len.keep(),con(scale))))); - ALTMP.clear(); ALTMP.add(len.unkeep()); ALTMP.add(con(ary._fields[1]._t.makeZero())); - return newStruct(ary,size,0,ALTMP); - } - private Node newString(String s) { - TypeMemPtr tmp = typeAry(TypeInteger.U8,true); - int lenAlias = tmp._obj.field("#" )._alias; - int elemAlias = tmp._obj.field("[]")._alias; - TypeConAryB con = TypeConAryB.make(s); - Type elem = con.elem(); - // Make a TMP, not-null (byte)2, singleton (true) (requires text - // strings are hash-interned), with a TypeStruct having a constant - // array body. - TypeMemPtr str = TypeMemPtr.make((byte)2,TypeStruct.makeAry("[]u8", TypeInteger.constant(s.length()), lenAlias, con, elemAlias, true),true); - assert str.isConstant(); - return con(str); - } - - // We set up memory aliases by inserting special vars in the scope these - // variables are prefixed by $ so they cannot be referenced in Simple code. - // Using vars has the benefit that all the existing machinery of scoping - // and phis work as expected - private Node memAlias(int alias ) { return _scope.mem(alias ); } - private void memAlias(int alias, Node st) { _scope.mem(alias, st); } - public static String memName(int alias) { return ("$"+alias).intern(); } - - /** - * Parse postfix expression; this can be a field expression, an array - * lookup or a postfix operator like '#' - * - *
-     *     expr ('.' FIELD)* [ = expr ]       // Field reference
-     *     expr '#'                           // Postfix unary read operator
-     *     expr ['++' | '--' ]                // Postfix unary write operator
-     *     expr ('[' expr ']')* = [ = expr ]  // Array reference
-     *     expr '(' [args,]* ')'              // Function call
-     * 
- */ - private Node parsePostfix(Node expr) { - String name; - if( match(".") ) name = requireId(); - else if( match("#") ) name = "#"; - else if( match("[") ) name = "[]"; - else if( match("(") ) return parsePostfix(require(functionCall(expr,null),")")); - else return expr; // No postfix - - return parsePostfixName(expr,name); - } - - /** - * Parse postfix expression; this can be a field expression, an array - * lookup or a postfix operator like '#' - * - *
-     *     expr ['++' | '--' ]                 // Postfix unary write operator
-     *     expr [ [op]= expr ]                 // Field reference
-     *     expr ('[' expr ']')* [ [op]= expr ] // Array reference
-     * 
- */ - private Node parsePostfixName(Node expr, String name) { - - if( expr._type==Type.NIL ) - throw error("Accessing unknown field '" + name + "' from 'null'"); - - // Sanity check expr for being a reference - if( !(expr._type instanceof TypeMemPtr ptr) ) - throw error( "Expected "+(name=="#" || name=="[]" ? "array" : "reference")+" but found " + expr._type.str() ); - - // Find the field from the Type. Lookup in the base object field names. - TypeMemPtr ptr2 = (TypeMemPtr)TYPES.get(ptr._obj._name); - ptr = (TypeMemPtr)ptr.join(ptr2);// Upgrade to latest TYPES - TypeStruct base = ptr._obj; - int fidx = base.find(name); - if( fidx == -1 ) - throw error("Accessing unknown field '" + name + "' from '" + ptr.str() + "'"); - - // Get field type and layout offset from base type and field index fidx - Field f = base._fields[fidx]; // Field from field index - Type tf = f._t; - if( tf instanceof TypeMemPtr ftmp && ftmp.isFRef() ) - tf = ftmp.makeFrom(((TypeMemPtr)(TYPES.get(ftmp._obj._name)))._obj); - - // Field offset; fixed for structs, computed for arrays - Node off = (name.equals("[]") // If field is an array body - // Array index math - ? peep(new AddNode(off(base,"[]"),peep(new ShlNode(null,require(parseAsgn(),"]"),con(base.aryScale()))))) - // Struct field offsets are hardwired - : off(base,name)).keep(); - - // Disambiguate "obj.fld==x" boolean test from "obj.fld=x" field assignment - if( matchOpx('=','=') ) { - Node val = parseAsgn().keep(); - Node lift = liftExpr( val, tf, f._final, false ); - - Node st = new StoreNode(loc(), name, f._alias, tf, memAlias(f._alias), expr, off.unkeep(), lift, false); - // Arrays include control, as a proxy for a safety range check. - // Structs don't need this; they only need a NPE check which is - // done via the type system. - if( base.isAry() ) st.setDef(0,ctrl()); - memAlias(f._alias, st.peephole()); - return val.unkeep(); // "obj.a = expr" returns the expression while updating memory - } - - // Keep expr for possible store update - expr.keep(); - // Load field - Node load = f._one && INITS.containsKey( base._name ) - ? INITS.get(base._name).in(base.find(name)) - : new LoadNode(loc(),name, f._alias, tf, memAlias(f._alias), expr, off); - // Arrays include control, as a proxy for a safety range check - // Structs don't need this; they only need a NPE check which is - // done via the type system. - if( base.isAry() && !name.equals("#") ) load.setDef(0,ctrl()); - load = peep(load); - - // Check for assign-update, "ptr.fld += expr" or "ary[idx]++" - char ch = _lexer.matchOperAssign(); - if( ch!=0 ) { - Node op = opAssign(ch,load, tf ); - Node st = new StoreNode(loc(), name, f._alias, tf, memAlias(f._alias), expr.unkeep(), off.unkeep(), op, false); - // Arrays include control, as a proxy for a safety range check. - // Structs don't need this; they only need a NPE check which is - // done via the type system. - if( base.isAry() ) st.setDef(0,ctrl()); - memAlias(f._alias, peep(st)); - load = postfix(ch) ? load.unkeep() : op; - // And use the original loaded value as the result - return load; - //throw Utils.TODO(); // NO POSTFIX AFTER STORE, JUST RETURN - } else { - off.unkill(); - // Method call: Loaded a function from a named field, AND the base - // field type is final. - if( load._type instanceof TypeFunPtr && name!="[]" && - !(load instanceof ExternNode) && - ((TypeMemPtr)TYPES.get(base._name))._obj.field(name)._final && - // And calling a function - match("(") ) { - return parsePostfix(require(functionCall(load,expr),")")); - } - - // Drop expr, keeping load alive - load.keep(); - expr.unkill(); - load.unkeep(); - } - - return parsePostfix(load); - } - - - // zero/sign extend. "i" is limited to either classic unsigned (min==0) or - // classic signed (min=minus-power-of-2); max=power-of-2-minus-1. - private Node zsMask(Node val, Type t ) { - if( !(val._type instanceof TypeInteger tval && t instanceof TypeInteger t0 && !tval.isa(t0)) ) { - if( !(val._type instanceof TypeFloat tval && t instanceof TypeFloat t0 && !tval.isa(t0)) ) - return val; - // Float rounding - return peep(new RoundF32Node(val)); - } - if( t0._min==0 ) // Unsigned - return peep(new AndNode(null,val,con(t0._max))); - // Signed extension - int shift = Long.numberOfLeadingZeros(t0._max)-1; - Node shf = con(shift); - if( shf._type==TypeInteger.ZERO ) - return val; - return peep(new SarNode(null,peep(new ShlNode(null,val,shf.keep())),shf.unkeep())); - } - - /** - * Parse a function body; the caller will parse the surrounding "{}" - * - *
-     *     { [type arg,]* -> expr }
-     *     { expr } // The no-argument function
-     * 
- */ - private Node func() { - Ary ts = new Ary<>(Type.class); - Ary ids = new Ary<>(String.class); - - // Defined in constructor? Add `self` argument. "static" calls still - // add a `self` they just ignore it. - if( _scope._kinds.last() instanceof Kind.Define define ) { - // Upgrade defined struct to latest fields - TypeMemPtr tmp = (TypeMemPtr)TYPES.get(define._tmp._obj._name); - ts .push(tmp); - ids.push("self"); - } - - // Parse other arguments - _lexer.skipWhiteSpace(); - Lexer loc = loc(); // First argument location - while( true ) { - Type t = type(); // Arg type - if( t==null ) break; - String id = requireId(); - ts .push(t ); // Push type/arg pairs - ids.push(id); - match(","); - } - require("->"); - // Make a concrete function type, with a fidx - TypeFunPtr tfp = _code.makeFun(TypeFunPtr.make(false,false,ts.asAry(),Type.BOTTOM)); - ReturnNode ret = parseFunctionBody(tfp,loc,ids.asAry()); - return con(ret._fun.sig()); - } - - /** - * Parse function call arguments; caller will parse the surrounding "()" - *
-     *   ( arg* )
-     * 
- */ - private Node functionCall(Node fcn, Node self) { - if( fcn._type == Type.NIL ) - throw error("Calling a null function pointer"); - if( !(fcn instanceof FRefNode) && !fcn._type.isa(TypeFunPtr.BOT) ) - throw error("Expected a function but got "+fcn._type.glb(false).str()); - fcn.keep(); // Keep while parsing args - - Ary args = new Ary<>( Node.class ); - args.push(null); // Space for ctrl,mem - args.push(null); - if( self != null ) // Method call has a self - args.push(self); - while( !peek(')') ) { - Node arg = parseAsgn(); - if( arg==null ) break; - args.push(arg.keep()); - if( !match(",") ) break; - } - // Control & memory after parsing args - args.set(0,ctrl().keep()); - args.set(1,_scope.mem().merge().keep()); - args.push(fcn); // Function pointer - // Unkeep them all - for( Node arg : args ) - arg.unkeep(); - // Dead into the call? Skip all the node gen - if( ctrl()._type == Type.XCONTROL ) { - for( Node arg : args ) - if( arg.isUnused() ) - arg.kill(); - return con(Type.TOP); - } - - // Into the call - CallNode call = (CallNode)new CallNode(loc(), args.asAry()).peephole(); - - // Post-call setup - CallEndNode cend = (CallEndNode)new CallEndNode(call).peephole(); - call.peephole(); // Rerun peeps after CallEnd, allows early inlining - // Control from CallEnd - ctrl(new CProjNode(cend,0,ScopeNode.CTRL).peephole()); - // Memory from CallEnd - MemMergeNode mem = new MemMergeNode(true); - mem.addDef(null); // Alias#0 - mem.addDef(new ProjNode(cend,1,ScopeNode.MEM0).peephole()); - _scope.mem(mem); - // Call result - return new ProjNode(cend,2,"#2").peephole(); - } - - // Just after parsing "type foo = " can parse `"C"` - private boolean isExternDecl( ) { - int old = pos(); - String s = parseString(); - if( "C".equals(s) ) return true; - pos(old); - return false; - } - // External linked constant - private Node externDecl( String ex, Type t ) { - if( t instanceof TypeFunPtr tfp ) // Generic TFP from type parse - t = _code.makeFun(tfp); // Get a FIDX, becomes a constant - return new ExternNode(t,ex).peephole(); - } - - /** - * Parse integer literal - * - *
-     *     integerLiteral: [1-9][0-9]* | [0]
-     *     floatLiteral: [digits].[digits]?[e [digits]]?
-     * 
- */ - private ConstantNode parseLiteral() { return con(_lexer.parseNumber()); } - public static Node con( long con ) { return con==0 ? ZERO : con(TypeInteger.constant(con)); } - public static ConstantNode con( Type t ) { return (ConstantNode)new ConstantNode(t).peephole(); } - public static ConFldOffNode off( TypeStruct base, String fname ) { return (ConFldOffNode)(new ConFldOffNode(base._name,fname).peephole()); } - public Node peep( Node n ) { - // Peephole, then improve with lexically scoped guards - return _scope.upcastGuard(n.peephole()); - } - - // Parse a string or null - private String parseString() { - if( !peek('"') ) return null; - _lexer.inc(); - int start = pos(); - while( !_lexer.isEOF() && _lexer.nextChar()!= '"' ) ; - if( _lexer.isEOF() ) - throw error("Unclosed string"); - return new String(_lexer._input,start,pos()-start-1); - } - - // Already parsed "'" - private Node parseChar() { - return require(con(TypeInteger.constant(_lexer.nextChar())),"'"); - } - - ////////////////////////////////// - // Utilities for lexical analysis - - // Return true and skip if "syntax" is next in the stream. - private boolean match (String syntax) { return _lexer.match (syntax); } - // Match must be "exact", not be followed by more id letters - private boolean matchx(String syntax) { return _lexer.matchx(syntax); } - private boolean matchOpx(char c0, char c1) { return _lexer.matchOpx(c0,c1); } - // Return true and do NOT skip if 'ch' is next - private boolean peek(char ch) { return _lexer.peek(ch); } - private boolean peekIsId() { return _lexer.peekIsId(); } - - // ch is +/- 1, means oper++ or oper-- means postfix - private static boolean postfix(char ch) { - return (byte)ch== 1 || (byte)ch== -1; - } - - public int pos() { return _lexer._position; } - private int pos(int pos) { - int old = _lexer._position; - _lexer._position = pos; - return old; - } - private Type posT(int pos) { _lexer._position = pos; return null; } - // Source code location - Lexer loc() { return new Lexer(_lexer); } - - - // Require and return an identifier - private String requireId() { - String id = _lexer.matchId(); - if (id != null && !KEYWORDS.contains(id) ) return id.intern(); - throw error("Expected an identifier, found '"+id+"'"); - } - - private String matchId() { - int old = pos(); - String id = _lexer.matchId(); - if( id==null ) return null; - if( !KEYWORDS.contains(id) ) return id; - pos(old); - return null; - } - - // Require an exact match - private Parser require(String syntax) { require(null, syntax); return this; } - private N require(N n, String syntax) { - if (match(syntax)) return n; - throw errorSyntax(syntax); - } - - //////////////////////////////////// - // Lexer components - - // Lexer provides low level access to the raw file bytes, peeks and matches - // short strings, parses numbers, skips comments and whitespace, tracks - // line numbers, allows the parse position to be saved and restored, and - // serves as a location indicator for errors. - - public static class Lexer { - - // Input buffer; an array of text bytes read from a file or a string - private final byte[] _input; - // Tracks current position in input buffer - private int _position = 0; - - // - private int _line_number = 1; - // Start of current line - private int _line_start = 0; - - /** - * Record the source text for lexing - */ - public Lexer(String source) { - this(source.getBytes()); - } - - /** - * Direct from disk file source - */ - public Lexer(byte[] buf) { - _input = buf; - } - - /** - * Copy a lexer from a lexer - */ - private Lexer(Lexer l) { - _input = l._input; - _position = l._position; - _line_number = l._line_number; - _line_start = l._line_start; - } - - // Very handy in the debugger, shows the unparsed program - @Override - public String toString() { - return new String(_input, _position, _input.length - _position); - } - - // True if at EOF - private boolean isEOF() { - return _position >= _input.length; - } - - // Peek next character, or report EOF - private char peek() { - return isEOF() ? Character.MAX_VALUE // Special value that causes parsing to terminate - : (char) _input[_position]; - } - // Just crash if misused - public byte peek(int off) { return _input[_position+off]; } - - private void inc() { - if( _position++ < _input.length && _input[_position-1] == '\n' ) { - _line_number++; - _line_start = _position; - } - } - // Does not honor LF, so caller must roll back position on a LF - private char nextChar() { - char ch = peek(); - inc(); - return ch; - } - - // True if a white space - private boolean isWhiteSpace() { - return peek() <= ' '; // Includes all the use space, tab, newline, CR - } - - /** - * Return the next non-white-space character - */ - private void skipWhiteSpace() { - while( true ) { - if( isWhiteSpace() ) inc(); - // Skip // to end of line - else if( _position+2 < _input.length && - _input[_position ] == '/' && - _input[_position+1] == '/') { - inc(); inc(); - while( !isEOF() && _input[_position] != '\n' ) - inc(); - } else if( _position+2 < _input.length && - _input[_position ] == '/' && - _input[_position+1] == '*') { - // Skip /*comment*/ - while( !isEOF() && !(_input[_position-1] == '*' && _input[_position] == '/')) - inc(); - inc(); - } else break; - } - } - - // Next non-white-space character, or EOF - public char nextXChar() { skipWhiteSpace(); return nextChar(); } - - // Return true, if we find "syntax" after skipping white space; also - // then advance the cursor past syntax. - // Return false otherwise, and do not advance the cursor. - boolean match(String syntax) { - assert syntax.indexOf('\n')==-1; // No newlines in match - skipWhiteSpace(); - int len = syntax.length(); - if (_position + len > _input.length) return false; - for (int i = 0; i < len; i++) - if ((char) _input[_position + i] != syntax.charAt(i)) - return false; - _position += len; - return true; - } - - // Match must be exact and not followed by more ID characters. - // Prevents identifier "ifxy" from matching an "if" statement. - boolean matchx(String syntax) { - if( !match(syntax) ) return false; - if( !isIdLetter(peek()) ) return true; - _position -= syntax.length(); - return false; - } - // Match these two characters in a row - boolean matchOpx(char c0, char c1) { - skipWhiteSpace(); - if( _position+1 >= _input.length || _input[_position]!=c0 || _input[_position+1]==c1 ) - return false; - inc(); - return true; - } - - private boolean peek(char ch) { - skipWhiteSpace(); - return peek()==ch; - } - - boolean peekIsId() { - skipWhiteSpace(); - return isIdStart(peek()); - } - - // Return an identifier or null - String matchId() { - return peekIsId() ? parseId() : null; - } - - // Used for errors - String getAnyNextToken() { - if (isEOF()) return ""; - if (isIdStart(peek())) return parseId(); - if (isNumber(peek())) return parseNumberString(); - if (isPunctuation(peek())) return parsePunctuation(); - return String.valueOf(peek()); - } - - - boolean isNumber(char ch) {return Character.isDigit(ch);} - - // Return a constant Type, either TypeInteger or TypeFloat - private Type parseNumber() { - int old = _position; - int len = isLongOrDouble(); - if( len > 0 ) { - if( len > 1 && _input[old]=='0' ) - throw error("Syntax error: integer values cannot start with '0'",this); - long i = Long.parseLong(new String(_input,old,len)); - return TypeInteger.constant(i); - } - return TypeFloat.constant(Double.parseDouble(new String(_input,old,-len))); - } - private String parseNumberString() { - int old = _position; - int len = Math.abs(isLongOrDouble()); - return new String(_input,old,len); - } - - // Return +len that ends a long - // Return -len that ends a double - private int isLongOrDouble() { - int old = _position; - char c; - while( Character.isDigit(c=nextChar()) ) ; - if( !(c=='e' || c=='.') ) - return --_position - old; - if( peek() == '-' ) nextChar(); - while( Character.isDigit(c=nextChar()) || c=='e' || c=='.' ) ; - return -(--_position - old); - } - - // First letter of an identifier - private boolean isIdStart(char ch) { - return Character.isAlphabetic(ch) || ch == '_'; - } - - // All characters of an identifier, e.g. "_x123" - private boolean isIdLetter(char ch) { - return Character.isLetterOrDigit(ch) || ch == '_'; - } - - private String parseId() { - int start = _position; - while (isIdLetter(nextChar())) ; - return new String(_input, start, --_position - start); - } - - // - private boolean isPunctuation(char ch) { - return "=;[]<>()+-/*&|^".indexOf(ch) != -1; - } - - private String parsePunctuation() { - int start = _position; - return new String(_input, start, 1); - } - - // Next oper= character, or 0. - // As a convenience, mark "++" as a char 1 and "--" as char -1 (65535) - public char matchOperAssign() { - skipWhiteSpace(); - if( _position+2 >= _input.length ) return 0; - char ch0 = (char)_input[_position]; - if( "+-/*&|^".indexOf(ch0) == -1 ) return 0; - char ch1 = (char)_input[_position+1]; - if( ch1 == '=' ) { _position += 2; return ch0; } - if( isIdLetter((char)_input[_position+2]) ) return 0; - if( ch0 == '+' && ch1 == '+' ) { _position += 2; return (char) 1; } - if( ch0 == '-' && ch1 == '-' ) { _position += 2; return (char)-1; } - return 0; - } - } - - ParseException errorSyntax(String syntax) { return _errorSyntax("expected "+syntax); } - private ParseException _errorSyntax(String msg) { - return error("Syntax error, "+msg+": " + _lexer.getAnyNextToken()); - } - ParseException error(String msg) { return error(msg,_lexer); } - public static ParseException error(String msg, Lexer loc) { return new ParseException(msg,loc); } - - public static class ParseException extends RuntimeException { - public final Lexer _loc; - // file:line:charoff err - //String msg = "src:"+_line_number+":"+(_position-_line_start)+" "+errorMessage; - ParseException( String msg, Lexer loc ) { super(msg); _loc = loc; } - } - -} diff --git a/src/main/java/com/seaofnodes/simple/Var.java b/src/main/java/com/seaofnodes/simple/Var.java deleted file mode 100644 index cf549cd6..00000000 --- a/src/main/java/com/seaofnodes/simple/Var.java +++ /dev/null @@ -1,53 +0,0 @@ -package com.seaofnodes.simple; - -import com.seaofnodes.simple.type.*; - - -/** - * The tracked fields are now complex enough to deserve a array-of-structs layout - */ -public class Var { - - public final String _name; // Declared name - // These fields are not final for forward reference late updates or - // promotions. - public int _idx; // index in containing scope - private Type _type; // Declared type - public boolean _final; // Final field - public boolean _fref; // Forward ref - public Parser.Lexer _loc; // Source location - - public Var(int idx, String name, Type type, boolean xfinal, Parser.Lexer loc ) { - this(idx,name,type,xfinal,loc,false); - } - public Var(int idx, String name, Type type, boolean xfinal, Parser.Lexer loc, boolean fref) { - _idx = idx; - _name = name; - _type = type; - _final = xfinal; - _fref = fref; - _loc = loc; - } - public Type type() { - if( !_type.isFRef() ) return _type; - // Update self to no longer use the forward ref type - Type def = Parser.TYPES.get(((TypeMemPtr)_type)._obj._name); - return (_type=_type.meet(def)); - } - - // Forward reference variables (not types) must be BOTTOM and - // distinct from inferred variables - public boolean isFRef() { return _fref; } - - public void defFRef( Type type, boolean xfinal, Parser.Lexer loc ) { - assert isFRef() && xfinal; - _type = type; - _final = true; - _fref = false; - _loc = loc; - } - - @Override public String toString() { - return _type.toString()+(_final ? " ": " !")+_name; - } -} diff --git a/src/main/java/com/seaofnodes/simple/codegen/BuildLRG.java b/src/main/java/com/seaofnodes/simple/codegen/BuildLRG.java deleted file mode 100644 index e9b5eb2e..00000000 --- a/src/main/java/com/seaofnodes/simple/codegen/BuildLRG.java +++ /dev/null @@ -1,90 +0,0 @@ -package com.seaofnodes.simple.codegen; - -import com.seaofnodes.simple.node.*; -import com.seaofnodes.simple.type.TypeMem; - -abstract public class BuildLRG { - // Compute live ranges in a single forwards pass. Every def is a new live - // range except live ranges are joined at Phi. Also, gather the - // intersection of register masks in each live range. Example: "free - // always zero" register forces register zero, and calling conventions - // typically force registers. - - // Sets FAILED to the set of hard-conflicts (means no need for an IFG, - // since it will not color, just split the conflicted ranges now). Returns - // true if no hard-conflicts, although we still might not color. - public static boolean run(int round, RegAlloc alloc) { - for( Node bb : alloc._code._cfg ) - for( Node n : bb.outs() ) { - if( n instanceof PhiNode phi && !(phi._type instanceof TypeMem) ) { - // All Phi inputs end up with the same LRG. - // Pass 1: find any pre-existing LRG, to avoid make-then-Union a LRG - LRG lrg = alloc.lrg(phi); - if( lrg == null ) - for( int i=1; i v2.nadj() ) - { v1 = v2; v2 = alloc.lrg(n); } - int v2len = v2._adj==null ? 0 : v2._adj._len; - // See that they do not conflict (coalescing would make a self-conflict) - if( v1._adj!=null && v2._adj!=null && v1._mask.overlap(v2._mask) ) { - // Walk the smaller neighbors, and add to the larger. - // Check for direct conflicts along the way. - for( LRG v3 : v1._adj ) { - if( v3==v2 ) { - // Direct conflict, skip this LRG pair - v2._adj.setLen(v2len); - continue outer; - } - // Add missing LRGs from V1 to V2 - if( v2._adj.find(v3)== -1 ) - v2._adj.push(v3); - } - } - // Most constrained mask - RegMask mask = v1._mask==v2._mask ? v1._mask : v1._mask.copy().and(v2._mask); - // Check for capacity - if( v2.nadj() >= mask.size() ) { - // Fails capacity, will not be trivial colorable - if( v2._adj!=null ) v2._adj.setLen(v2len); - continue; - } - - // Union lrgs. - progress = true; - - // Since `n` is dying, remove from the LRGS BEFORE doing - // the union; this will let the union code keep the - // matching bits from the other side, preserving some - // info for biased coloring - if( ov1. _machDef==n ) ov1. _machDef = null; - if( ov1._splitDef==n ) ov1._splitDef = null; - if( ov2. _machUse==n ) ov2. _machUse = null; - if( ov2._splitUse==n ) ov2._splitUse = null; - // Keep larger adjacency list. - Ary larger = v2._adj; - v2._adj = null; - - // Union lrgs. Swaps again, based on small lrg - LRG vWin = v1.union(v2); - vWin._adj = larger; - LRG vLose = v1.leader() ? v2 : v1; - - // LRGs below v2len were always in v2, but maybe not in v1. - // LRGs above v2len are in v1 but were not in v2. - - // Walk v2's neighbors, replacing vLose with vWin. - if( larger != null ) - for( LRG vn : larger ) { - int idx = vn._adj.find(vLose); - if( idx != -1 ) - vn._adj.del(idx); - if( vn._adj.find(vWin) == -1 ) - vn._adj.add(vWin); - } - } else { - - // Since `n` is dying, remove from the LRGS - if( v1. _machDef==n ) v1. _machDef=null; - if( v1. _machUse==n ) v1. _machUse=null; - if( v1._splitDef==n ) v1._splitDef=null; - if( v1._splitUse==n ) v1._splitUse=null; - } - - // Remove junk split - n.removeSplit(); - j--; - } - } - - if( progress ) - alloc.unify(); - - return true; - } -} diff --git a/src/main/java/com/seaofnodes/simple/codegen/CodeGen.java b/src/main/java/com/seaofnodes/simple/codegen/CodeGen.java deleted file mode 100644 index caa7ff44..00000000 --- a/src/main/java/com/seaofnodes/simple/codegen/CodeGen.java +++ /dev/null @@ -1,521 +0,0 @@ -package com.seaofnodes.simple.codegen; - -import com.seaofnodes.simple.IterPeeps; -import com.seaofnodes.simple.Parser; -import com.seaofnodes.simple.node.*; -import com.seaofnodes.simple.print.*; -import com.seaofnodes.simple.type.*; -import com.seaofnodes.simple.util.Ary; -import com.seaofnodes.simple.util.SB; -import java.io.IOException; -import java.util.*; - -@SuppressWarnings("unchecked") -public class CodeGen { - public static final String PORTS = "com.seaofnodes.simple.node.cpus"; - // 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 - LoopTree, // Build a loop tree; break infinite loops - Select, // Convert to target hardware nodes - Schedule, // Global schedule (code motion) nodes - LocalSched, // Local schedule - RegAlloc, // Register allocation - Encoding, // Encoding - Export, // Export - } - 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, true ); } - public CodeGen( String src, TypeInteger arg, long workListSeed, boolean reset ) { - CODE = this; - if( reset ) Type.reset(); - _main = makeFun(TypeFunPtr.MAIN); - _phase = null; - _callingConv = null; - _start = new StartNode(arg); - _stop = new StopNode(src); - _src = src; - _arg = arg; - _iter = new IterPeeps(workListSeed); - P = new Parser(this,arg); - } - - - // All passes up to Phase, except ELF - public CodeGen driver( Phase phase ) { return driver(phase,null,null); } - public CodeGen driver( Phase phase, String cpu, String callingConv ) { - if( _phase==null ) parse(); - int p1 = phase.ordinal(); - if( _phase.ordinal() < p1 && _phase.ordinal() < Phase.Opto .ordinal() ) opto(); - if( _phase.ordinal() < p1 && _phase.ordinal() < Phase.TypeCheck .ordinal() ) typeCheck(); - if( _phase.ordinal() < p1 && _phase.ordinal() < Phase.LoopTree .ordinal() ) loopTree(); - if( _phase.ordinal() < p1 && _phase.ordinal() < Phase.Select .ordinal() && cpu != null ) instSelect(cpu,callingConv); - if( _phase.ordinal() < p1 && _phase.ordinal() < Phase.Schedule .ordinal() ) GCM(); - if( _phase.ordinal() < p1 && _phase.ordinal() < Phase.LocalSched.ordinal() ) localSched(); - if( _phase.ordinal() < p1 && _phase.ordinal() < Phase.RegAlloc .ordinal() ) regAlloc(); - if( _phase.ordinal() < p1 && _phase.ordinal() < Phase.Encoding .ordinal() ) encode(); - return this; - } - - // Run all the phases through final ELF emission - public CodeGen driver( String cpu, String callingConv, String obj ) throws IOException { - return driver(Phase.Encoding,cpu,callingConv).exportELF(obj); - } - - - // --------------------------- - /** - * 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++; } - - - // idepths are cached and valid until *inserting* CFG edges (deleting is - // OK). This happens with inlining, which bumps the version to bulk - // invalidate the idepth caches. - private int _iDepthVersion = 0; - public void invalidateIDepthCaches() { _iDepthVersion++; } - public boolean validIDepth(int idepth) { - if( idepth==0 ) return false; - if( _iDepthVersion==0 ) return true; - return (idepth%100)==_iDepthVersion; - } - public int iDepthAt(int idepth) { - return 100*idepth+_iDepthVersion; - } - public int iDepthFrom(int idepth) { - assert idepth==0 || validIDepth(idepth); - return idepth+100; - } - - // 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<>(); - - // - private int _fidx =0; - public TypeFunPtr makeFun( TypeFunPtr fun ) { - int fidx = _fidx++; - assert fidx<64; // TODO: need a larger FIDX space - return fun.makeFrom(fidx); - } - // Signature for MAIN - public final TypeFunPtr _main; - // Reverse from a constant function pointer to the IR function being called - public FunNode link( TypeFunPtr tfp ) { - assert tfp.isConstant(); - return _linker.get(tfp.makeFrom(Type.BOTTOM)); - } - - // Insert linker mapping from constant function signature to the function - // being called. - public void link(FunNode fun) { - _linker.put(fun.sig().makeFrom(Type.BOTTOM),fun); - } - - // "Linker" mapping from constant TypeFunPtrs to heads of function. These - // TFPs all have exact single fidxs and their return is wiped to BOTTOM (so - // the return is not part of the match). - private final HashMap _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); - JSViewer.show(); - 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); - - // OPTIMISTIC PASS GOES HERE - - // Not really a true optimistic pass, but look for unlinked functions. - // This can be removed, which may trigger another round of pessimistic. - // This is the point where we flip from a virtual Call Graph (any call - // can call any function) to having a correct (but conservative) CG. - int progress = 0; - while( progress != _start.nOuts() ) { - progress = _start.nOuts(); - FunNode main = link(_main); - for( int i=0; i<_start.nOuts(); i++ ) { - Node use = _start.out(i); - if( use instanceof FunNode fun && - fun.nIns()==2 && fun.in(1)==_start && fun != main && - (fun._name==null || fun._name.startsWith("sys.")) ) { - add(fun).setDef(1,Parser.XCTRL); - addAll(fun._outputs); - i--; - } - } - _iter.iterate(this); - } - - // Freeze field sizes; do struct layouts; convert field offsets into - // constants. - for( int i=0; i<_start.nOuts(); i++ ) { - Node use = _start.out(i); - if( use instanceof ConFldOffNode off ) { - TypeMemPtr tmp = (TypeMemPtr) Parser.TYPES.get(off._name); - off.subsume( off.asOffset(tmp._obj) ); - i--; - } - } - _iter.iterate(this); - - // To help with testing, sort StopNode returns by NID - Arrays.sort(_stop._inputs._es,0,_stop.nIns(),(x,y) -> x._nid - y._nid ); - - _tOpto = (int)(System.currentTimeMillis() - t0); - - // TODO: - // Optimistic - // TODO: - // loop unroll, peel, RCE, etc - return this; - } - public N add( N n ) { return _iter.add(n); } - public void addAll( Ary ary ) { _iter.addAll(ary); } - - - // --------------------------- - // Last check for bad programs - public 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( Node::err ); - _tTypeCheck = (int)(System.currentTimeMillis() - t0); - if( err != null ) - throw err; - return this; - } - - // --------------------------- - // Build the loop tree; break never-exit loops - public int _tLoopTree; - public CodeGen loopTree() { - assert _phase.ordinal() <= Phase.TypeCheck.ordinal(); - _phase = Phase.LoopTree; - long t0 = System.currentTimeMillis(); - // Build the loop tree, fix never-exit loops - _start.buildLoopTree(_start,_stop); - _tLoopTree = (int)(System.currentTimeMillis() - t0); - return this; - } - - // --------------------------- - // Code generation CPU target - public Machine _mach; - // Chosen calling convention (usually either Win64 or SystemV) - public String _callingConv; - // Callee save registers - public RegMask _callerSave; - - // All returns have the following inputs: - // 0 - ctrl - // 1 - memory - // 2 - varies with returning GPR,FPR - // 3 - RPC - // 4+- Caller save registers - public RegMask[] _retMasks; - // Return Program Counter - public RegMask _rpcMask; - - // Convert to target hardware nodes - public int _tInsSel; - public CodeGen instSelect( String cpu, String callingConv ) { return instSelect(cpu,callingConv,PORTS); } - public CodeGen instSelect( String cpu, String callingConv, String base ) { - assert _phase.ordinal() == Phase.LoopTree.ordinal(); - _phase = Phase.Select; - - _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(new Class[]{CodeGen.class}).newInstance(this); } - catch( Exception e ) { throw new RuntimeException(e); } - - // Build global copies of common register masks. - long callerSave = _mach.callerSave(); - long neverSave = _mach. neverSave(); - int maxReg = Math.min(64,_mach.regs().length); - assert maxReg>=64 || (-1L << maxReg & callerSave)==0; // No stack slots in callerSave - _callerSave = new RegMask(callerSave); - - // Build a Return RegMask array. All returns have the following inputs: - // 0 - ctrl - // 1 - memory - // 2 - varies with returning GPR,FPR - // 3 - RPC - // 4+- Caller save registers - _retMasks = new RegMask[(maxReg-_callerSave.size()-Long.bitCount( neverSave ))+4]; - for( int reg=0, i=4; reg(); - _instSelect( _stop, map ); - _stop = ( StopNode)map.get(_stop ); - StartNode start = (StartNode)map.get(_start); - _start = start==null ? new StartNode(_start) : 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(this); // Post selection action - - 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 Reverse Post Order. - public int _tGCM; - public Ary _cfg = new Ary<>(CFGNode.class); - - // Global schedule (code motion) nodes - public CodeGen GCM() { return GCM(false); } - public CodeGen GCM( boolean show) { - assert _phase.ordinal() <= Phase.Select.ordinal(); - _phase = Phase.Schedule; - long t0 = System.currentTimeMillis(); - - GlobalCodeMotion.buildCFG(this); - _tGCM = (int)(System.currentTimeMillis() - t0); - if( show ) - System.out.println(new GraphVisualizer().generateDotOutput(_stop,null,null)); - return this; - } - - // --------------------------- - // Local (basic block) scheduler phase, a classic list scheduler - public 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 - public 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; - } - - // Human readable register name - public String reg(Node n) { return reg(n,null); } - public String reg(Node n, FunNode fun) { - if( _phase.ordinal() >= Phase.RegAlloc.ordinal() ) { - String s = _regAlloc.reg(n,fun); - if( s!=null ) return s; - } - return "N"+ n._nid; - } - - - // --------------------------- - // Encoding - public int _tEncode; - public Encoding _encoding; // Encoding object - public void preEncode() { } // overridden by alternative ports - public CodeGen encode() { - assert _phase == Phase.RegAlloc; - _phase = Phase.Encoding; - long t0 = System.currentTimeMillis(); - _encoding = new Encoding(this); - preEncode(); - _encoding.encode(); - _tEncode = (int)(System.currentTimeMillis() - t0); - return this; - } - - // --------------------------- - // Exporting to external formats - public CodeGen exportELF(String fname) throws IOException { - assert _phase == Phase.Encoding; - _phase = Phase.Export; - if( fname == null ) new LinkMem(this).link(); // In memory patching - else new ElfFile(this).export(fname); // External ELF file - return this; - } - - // --------------------------- - public boolean _asmLittle=true; - 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 ) - : _stop.p(9999); - } - - // Debugging helper - public Node f(int idx) { return _stop.find(idx); } - - - String printCFG() { - if( _cfg==null ) return "no CFG"; - SB sb = new SB(); - for( CFGNode cfg : _cfg ) { - sb.fix(8,""+cfg._idepth); - IRPrinter.printLine( cfg, sb ); - } - return sb.toString(); - } - - public static void print_as_hex(Encoding enc) { - for (byte b : enc._bits.toByteArray()) { - System.out.print(String.format("%02X", b)); - } - System.out.println(); - } - - public void print_as_hex() { - for (byte b : _encoding._bits.toByteArray()) { - System.out.print(String.format("%02X", b)); - } - System.out.println(); - } - - //// Debug purposes for now -// public CodeGen printENCODING() { -// ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); -// -// for(Node bb : CodeGen.CODE._cfg) { -// for(Node n: bb.outs()) { -// if(n instanceof MachNode) { -// ((MachNode) n).encoding(E); -// } -// } -// } -// -// print_as_hex(outputStream); -// // Get the raw bytes from the output stream -// return this; -// } -} diff --git a/src/main/java/com/seaofnodes/simple/codegen/ElfExpr.java b/src/main/java/com/seaofnodes/simple/codegen/ElfExpr.java deleted file mode 100644 index 9d9b8a1c..00000000 --- a/src/main/java/com/seaofnodes/simple/codegen/ElfExpr.java +++ /dev/null @@ -1,87 +0,0 @@ -package com.seaofnodes.simple.codegen; - -import java.io.FileOutputStream; -import java.io.IOException; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; - -public class ElfExpr { - public static void main(String[] args) { - try (FileOutputStream fos = new FileOutputStream("hello.o")) { - ByteBuffer buffer = ByteBuffer.allocate(512); - buffer.order(ByteOrder.LITTLE_ENDIAN); - - // === ELF HEADER (64 bytes) === - buffer.put(new byte[]{ - 0x7F, 'E', 'L', 'F', // ELF Magic - 2, 1, 1, 0, // 64-bit, little-endian, version - 0, 0, 0, 0, 0, 0, 0, // ABI padding - 2, 0, // Type: Executable (ET_EXEC) - 0x3E, 0x00, // Machine: x86-64 - 1, 0, 0, 0, // ELF version - 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Entry point - 0, 0, 0, 0, 0, 0, 0, 0, // Program header offset - 64, 0, 0, 0, 0, 0, 0, 0, // Section header offset - 0, 0, 0, 0, // Flags - 64, 0, // ELF header size - 0, 0, // Program header entry size - 0, 0, // Number of program header entries - 40, 0, // Section header entry size - 2, 0, // Number of section headers - 1, 0 // Section header string table index - }); - - // === .TEXT SECTION (Executable Code) === - byte[] textSection = { - // sys_write (write(1, message, 13)) - (byte) 0x48, (byte) 0xC7, (byte) 0xC0, 0x01, 0x00, 0x00, 0x00, // mov rax, 1 - (byte) 0x48, (byte) 0xC7, (byte) 0xC7, 0x01, 0x00, 0x00, 0x00, // mov rdi, 1 - (byte) 0x48, (byte) 0xBE, (byte)0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov rsi, message - (byte) 0x48, (byte) 0xC7, (byte) 0xC2, 0x0D, 0x00, 0x00, 0x00, // mov rdx, 13 - (byte) 0x0F, (byte) 0x05, // syscall - - // sys_exit (exit(0)) - (byte) 0x48, (byte) 0xC7, (byte) 0xC0, 0x3C, 0x00, 0x00, 0x00, // mov rax, 60 - (byte) 0x48, (byte) 0x31, (byte) 0xFF, // xor rdi, rdi - (byte) 0x0F, (byte) 0x05 // syscall - }; - - // Append section data - buffer.position(256); - buffer.put(textSection); - - // === STRING MESSAGE (.data Section) === - byte[] message = "Hello, World!\n".getBytes(); - buffer.position(400); - buffer.put(message); - - // === SECTION HEADERS === - buffer.position(64); - - // Null Section (required as first entry) - buffer.put(new byte[40]); - - // .text Section - buffer.putInt(1); // Offset in section string table (".text") - buffer.putInt(1); // Type: SHT_PROGBITS - buffer.putLong(6); // Flags: Executable | Allocatable - buffer.putLong(0); // Virtual address - buffer.putLong(256); // Offset to section data - buffer.putLong(textSection.length); // Section size - buffer.putInt(0); // Link - buffer.putInt(0); // Info - buffer.putLong(4); // Address alignment - buffer.putLong(0); // No extra entries - - // === SECTION HEADER STRING TABLE === - buffer.position(500); - buffer.put(".text".getBytes()); - buffer.put((byte) 0x00); // Null terminator - - fos.write(buffer.array()); - System.out.println("ELF object file 'hello.o' created."); - } catch (IOException e) { - e.printStackTrace(); - } - } -} diff --git a/src/main/java/com/seaofnodes/simple/codegen/ElfFile.java b/src/main/java/com/seaofnodes/simple/codegen/ElfFile.java deleted file mode 100644 index 8f9e2981..00000000 --- a/src/main/java/com/seaofnodes/simple/codegen/ElfFile.java +++ /dev/null @@ -1,381 +0,0 @@ -package com.seaofnodes.simple.codegen; - -import com.seaofnodes.simple.*; -import com.seaofnodes.simple.codegen.Encoding.BAOS; -import com.seaofnodes.simple.node.*; -import com.seaofnodes.simple.type.*; -import com.seaofnodes.simple.util.Ary; -import java.io.*; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.util.HashMap; -import java.util.Map; - -public class ElfFile { - - final CodeGen _code; - Ary
_sections = new Ary<>(Section.class); - - ElfFile( CodeGen code ) { _code = code; } - - public static int writeCString(DataSection strtab, String name) { - // place name in the string table - int pos = strtab._contents.size(); - byte[] name_bytes = name.getBytes(); - strtab._contents.write(name_bytes, 0, name_bytes.length); - // the strings are null terminated - strtab._contents.write(0); - return pos; - } - - public static void write4( ByteArrayOutputStream bits, int op ) { - bits.write(op ); - bits.write(op>> 8); - bits.write(op>>16); - bits.write(op>>24); - } - public static void write8( ByteArrayOutputStream bits, long i64 ) { - write4(bits, (int) i64 ); - write4(bits, (int)(i64>>32)); - } - - public static final int SYMBOL_SIZE = 4+1+1+2+8+8; - - public static final int SYM_BIND_LOCAL = 0; - public static final int SYM_BIND_GLOBAL = 1; - public static final int SYM_BIND_WEAK = 2; - - public static final int SYM_TYPE_NOTYPE = 0; - public static final int SYM_TYPE_OBJECT = 1; - public static final int SYM_TYPE_FUNC = 2; - public static final int SYM_TYPE_SECTION = 3; - - public static class Symbol { - String _name; - int _name_pos; - - // index in the symbol table - int _index; - - // which section is it in - int _parent; - - // depends on the symbol type - // TODO - int _value, _size; - - // top 4bits are the "bind", bottom 4 are "type" - int _info; - - // bind 1011 - // 00001111 - // 10110000 - - Symbol(String name, int parent, int bind, int type) { - _name = name; - _parent = parent; - _info = (bind << 4) | (type & 0xF); - } - - void writeHeader(ByteBuffer out) { - out.putInt(_name_pos); // name - out.put((byte) _info); // info - // default visibility - out.put((byte) 0); // other - out.putShort((short) _parent); // shndx - out.putLong(_value); // value - out.putLong(_size); // size - } - } - - public static final int SHF_WRITE = 1; - public static final int SHF_ALLOC = 2; - public static final int SHF_EXECINSTR = 4; - public static final int SHF_STRINGS = 32; - public static final int SHF_INFO_LINK = 64; - - public static final int SECTION_HDR_SIZE = 64; - abstract public static class Section { - String _name; - int _index; - - int _type, _flags; - int _link, _info; - - // where's the name in the string table - int _name_pos; - int _file_offset; - - Section(String name, int type) { - _name = name; - _type = type; - } - - void writeHeader(ByteBuffer out) { - out.putInt(_name_pos); // name - out.putInt(_type); // type - out.putLong(_flags); // flags - out.putLong(0); // addr - out.putLong(_file_offset); // offset - out.putLong(size()); // size - out.putInt(_link); // link - out.putInt(_info); // info - out.putLong(16); // addralign - if (_type == 2) { - out.putLong(SYMBOL_SIZE);// entsize - } else if (_type == 4) { - out.putLong(24); // entsize - } else { - out.putLong(0); // entsize - } - } - - abstract void write(ByteBuffer out); - abstract int size(); - } - - public static class SymbolSection extends Section { - Ary _symbols = new Ary<>(Symbol.class); - Ary _loc = new Ary<>(Symbol.class); - - SymbolSection(String name, int type) { - super(name, type); - } - - @Override - void writeHeader(ByteBuffer out) { - // points to string table section - _link = 1; - - // number of non-local symbols - _info = _loc.len() + 1; - - super.writeHeader(out); - } - - void push(Symbol s) { - if ((s._info >> 4) != SYM_BIND_LOCAL) { - _symbols.push(s); - } else { - _loc.push(s); - } - } - - @Override - void write(ByteBuffer out) { - // Index 0 both designates the first entry in the table and serves as the undefined symbol index - for( int i = 0; i < SYMBOL_SIZE/4; i++ ) { - out.putInt(0); - } - // index is already set - for( Symbol s : _loc ) { - s.writeHeader(out); - } - for( Symbol s : _symbols ) { - s.writeHeader(out); - } - } - - @Override - int size() { - return (1 + _symbols.len() + _loc.len()) * SYMBOL_SIZE; - } - } - - public static class DataSection extends Section { - ByteArrayOutputStream _contents; - - DataSection(String name, int type) { - super(name, type); - _contents = new ByteArrayOutputStream(); - } - - DataSection(String name, int type, ByteArrayOutputStream contents) { - super(name, type); - _contents = contents; - } - - @Override - void write(ByteBuffer out) { - out.put(_contents.toByteArray()); - } - - @Override - int size() { - return _contents.size(); - } - } - - private void pushSection(Section s) { - _sections.push(s); - // ELF is effectively "1-based", due to the NULL section - s._index = _sections._len; - } - - /* creates function and stores where it starts*/ - private final HashMap _funcs = new HashMap<>(); - private void encodeFunctions(SymbolSection symbols, DataSection text) { - for( int i=0; i<_code._cfg._len; i++ ) { - if( !(_code._cfg.at(i) instanceof FunNode fun) ) continue; - // skip until the function ends - while( !(_code._cfg.at(i) instanceof ReturnNode ret) ) - i++; - int end = _code._encoding._opStart[ret._nid] + _code._encoding._opLen[ret._nid]; - - Symbol func = new Symbol(fun._name, text._index, SYM_BIND_GLOBAL, SYM_TYPE_FUNC); - func._value = _code._encoding._opStart[fun._nid]; - func._size = end - func._value; - symbols.push(func); - _funcs.put(fun.sig(), func); - } - } - - public void export(String fname) throws IOException { - DataSection strtab = new DataSection(".strtab", 3 /* SHT_SYMTAB */); - // first byte is reserved for an empty string - strtab._contents.write(0); - pushSection(strtab); - - // place all the symbols - SymbolSection symbols = new SymbolSection(".symtab", 2 /* SHT_STRTAB */); - pushSection(symbols); - - // zero flag by default - // we've already constructed this entire section in the encoding phase - DataSection text = new DataSection(".text", 1 /* SHT_PROGBITS */, _code._encoding._bits); - text._flags = SHF_ALLOC | SHF_WRITE | SHF_EXECINSTR; - pushSection(text); - - // Build and write constant pool - BAOS cpool = new BAOS(); - Encoding enc = _code._encoding; - enc.writeConstantPool(cpool,false); - DataSection rdata = new DataSection(".rodata", 1 /* SHT_PROGBITS */, cpool); - rdata._flags = SHF_ALLOC; - pushSection(rdata); - - // populate function symbols - encodeFunctions(symbols, text); - - int idx = 1; - for( Section s : _sections ) { - Symbol sym = new Symbol(s._name, idx++, SYM_BIND_LOCAL, SYM_TYPE_SECTION); - // we can reuse the same name pos from the actual section - sym._name_pos = s._name_pos; - sym._size = s.size(); - symbols.push(sym); - } - - // calculate local index - int num = 1; - for( Symbol s : symbols._loc ) - s._index = num++; - // extra space for .rela.text - int start_global = num+1; // Add one to skip the final .rela.text local symbol - for( Symbol a: symbols._symbols ) - a._index = start_global++; - - // create .text relocations - DataSection text_rela = new DataSection(".rela.text", 4 /* SHT_RELA */); - for( Node n : enc._externals.keySet()) { - int nid = n._nid; - String extern = enc._externals.get(n); - - Symbol sym = new Symbol(extern, 0, SYM_BIND_GLOBAL, SYM_TYPE_NOTYPE); - sym._index = start_global++; - symbols.push(sym); - - int offset = enc._opStart[nid] + enc._opLen[nid] - 4; - - // u64 offset - write8(text_rela._contents, offset); - // u64 info - write8(text_rela._contents, ((long)sym._index << 32L) | 4L /* PLT32 */); - // i64 addend - write8(text_rela._contents, -4); - } - - // Write relocations for the constant pool - for( Encoding.Relo relo : enc._bigCons.values() ) { - Symbol glob = new Symbol("CPOOL$"+start_global, rdata._index, SYM_BIND_GLOBAL, SYM_TYPE_FUNC); - glob._value = relo._target; - glob._size = relo._t.size(); - glob._index = start_global++; - symbols.push(glob); - write8(text_rela._contents, relo._opStart+relo._off); - write8(text_rela._contents, ((long)glob._index << 32L) | relo._elf ); - write8(text_rela._contents, -4); - } - - text_rela._flags = SHF_INFO_LINK; - text_rela._link = 2; - text_rela._info = text._index; - pushSection(text_rela); - - // Final .rela.text symbol - Symbol sym = new Symbol(text_rela._name, num++, SYM_BIND_LOCAL, SYM_TYPE_SECTION); - sym._name_pos = text_rela._name_pos; - sym._size = text_rela.size(); - symbols.push(sym); - - // populate string table - for( Section s : _sections ) { s._name_pos = writeCString(strtab, s._name); } - for( Symbol s : symbols._symbols ) { s._name_pos = writeCString(strtab, s._name); } - for( Symbol s : symbols._loc ) { s._name_pos = writeCString(strtab, s._name); } - - int size = 64; // ELF header - // size of all section data - for( Section s : _sections ) { - s._file_offset = size; - size += s.size(); - } - // headers go at the very end - int shdr_offset = size; - // there's a "null" section at the start of the section headers - size += SECTION_HDR_SIZE*(1 + _sections.len()); - - // System.out.printf("Hello %d, %d, %s\n", size, shdr_offset); - ByteBuffer out = ByteBuffer.allocate(size); - out.order(ByteOrder.LITTLE_ENDIAN); - - // ELF header - out.put(new byte[]{ 0x7F, 'E', 'L', 'F', 2, 1, 1, 0 }); - out.put(new byte[8]); - out.putShort((short)1 /* ET_REL */); // type - out.putShort((short)62 /* AMD64 */); // machine - out.putInt(1); // version - out.putLong(0); // entry - out.putLong(0); // phoff - out.putLong(shdr_offset); // shoff - out.putInt(0); // flags - out.putShort((short)64); // ehsize - out.putShort((short)0); // phentsize - out.putShort((short)0); // phnum - out.putShort((short)64); // shentsize - out.putShort((short) (1 + _sections.len())); // shnum - out.putShort((short)1); // shstrndx - - // raw section data - for( Section s : _sections ) { - s.write(out); - } - - // "null" section - for( int i = 0; i < 16; i++ ) { - out.putInt(0); - } - - for( Section s : _sections ) { - s.writeHeader(out); - } - assert out.position() == size; - - File file = new File(fname); - if( file.getParentFile()!=null ) - file.getParentFile().mkdirs(); - BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(file)); - bos.write(out.array()); - bos.close(); - } - -} diff --git a/src/main/java/com/seaofnodes/simple/codegen/Encoding.java b/src/main/java/com/seaofnodes/simple/codegen/Encoding.java deleted file mode 100644 index 1065cd9f..00000000 --- a/src/main/java/com/seaofnodes/simple/codegen/Encoding.java +++ /dev/null @@ -1,531 +0,0 @@ -package com.seaofnodes.simple.codegen; - -import com.seaofnodes.simple.node.*; -import com.seaofnodes.simple.print.IRPrinter; -import com.seaofnodes.simple.type.*; -import com.seaofnodes.simple.util.Ary; -import com.seaofnodes.simple.util.SB; -import com.seaofnodes.simple.util.Utils; -import java.io.ByteArrayOutputStream; -import java.util.*; - - -/** - * Instruction encodings - *

- * This class holds the encoding bits, plus the relocation information needed - * to move the code to a non-zero offset, or write to an external file (ELF). - *

- * There are also a bunch of generic utilities for managing bits and bytes - * common in all encodings. - */ -public class Encoding { - - // Top-level program graph structure - public final CodeGen _code; - - // Instruction bytes. The registers are encoded already. Relocatable data - // is in a fixed format depending on the kind of relocation. - - // - RIP-relative offsets into the same chunk are just encoded with their - // check relative offsets; no relocation info is required. - - // - Fixed address targets in the same encoding will be correct for a zero - // base; moving the entire encoding requires adding the new base address. - - // - RIP-relative to external chunks have a zero offset; the matching - // relocation info will be used to patch the correct value. - - public static class BAOS extends ByteArrayOutputStream { - public byte[] buf() { return buf; } - void set( byte[] buf0, int count0 ) { buf=buf0; count=count0; } - }; - final public BAOS _bits = new BAOS(); - - public int [] _opStart; // Start of opcodes, by _nid - public byte[] _opLen; // Length of opcodes, by _nid - - // Function headers now padded when printing - public boolean _padFunHeads; - - // Big Constant relocation info. - public static class Relo { - public final Node _op; - public final Type _t; // Constant type - public final byte _off; // Offset from start of opcode - public final byte _elf; // ELF relocation type, e.g. 2/PC32 - public int _target; // Where constant is finally placed - public int _opStart; // Opcode start - Relo( Node op, Type t, byte off, byte elf ) { - _op=op; _t=t; _off=off; _elf=elf; - } - } - public final HashMap _bigCons = new HashMap<>(); - - Encoding( CodeGen code ) { _code = code; } - - // Shortcut to the defining register - public short reg(Node n) { - return _code._regAlloc.regnum(n); - } - - public int read1(int idx) { - byte[] buf = _bits.buf(); - return (buf[idx]&0xFF); - } - public int read2(int idx) { - byte[] buf = _bits.buf(); - return (buf[idx]&0xFF) | (buf[idx+1]&0xFF) <<8; - } - public int read4(int idx) { - byte[] buf = _bits.buf(); - return (buf[idx]&0xFF) | (buf[idx+1]&0xFF) <<8 | (buf[idx+2]&0xFF)<<16 | (buf[idx+3]&0xFF)<<24; - } - public long read8(int idx) { - return (read4(idx) & 0xFFFFFFFFL) | read4(idx+4); - } - - // This buffer is invalid/moving until after all encodings are written - public byte[] bits() { return _bits.buf(); } - - // 4 byte little-endian write - public void patch4( int idx, int val ) { - byte[] buf = _bits.buf(); - buf[idx ] = (byte)(val ); - buf[idx+1] = (byte)(val>> 8); - buf[idx+2] = (byte)(val>>16); - buf[idx+3] = (byte)(val>>24); - } - - static void padN(int n, BAOS bits) { - while( (bits.size()+n-1 & -n) > bits.size() ) - bits.write(0); - } - - // Convenience for writing log-N - static void addN( int log, Type t, BAOS bits ) { - long x = t instanceof TypeInteger ti - ? ti.value() - : log==3 - ? Double.doubleToRawLongBits( ((TypeFloat)t).value()) - : Float.floatToRawIntBits((float)((TypeFloat)t).value()); - addN(log,x,bits); - } - static void addN( int log, long x, BAOS bits ) { - for( int i=0; i < 1<>= 8; - } - } - - public Encoding add1( int op ) { addN(0,op,_bits); return this; } - public Encoding add2( int op ) { addN(1,op,_bits); return this; } - public Encoding add4( int op ) { addN(2,op,_bits); return this; } - public Encoding add8(long op ) { addN(3,op,_bits); return this; } - - - // Nodes need "relocation" patching; things done after code is placed. - // Record src and dst Nodes. - private final HashMap _internals = new HashMap<>(); - // Source is a Call, destination in the Fun. - public Encoding relo( CallNode call ) { - _internals.put(call,_code.link(call.tfp())); - return this; - } - public Encoding relo( ConstantNode con ) { - TypeFunPtr tfp = (TypeFunPtr)con._con; - _internals.put(con,_code.link(tfp)); - return this; - } - public void jump( CFGNode jmp, CFGNode dst ) { - _internals.put(jmp,dst.uctrlSkipEmpty()); - } - - - final HashMap _externals = new HashMap<>(); - public Encoding external( Node n, String extern ) { - _externals.put(n,extern); - return this; - } - - // Store t as a 32/64 bit constant in the code space; generate RIP-relative - // addressing to load it - public void largeConstant( Node relo, Type t, int off, int elf ) { - assert t.isConstant(); - assert (byte)off == off; - assert (byte)elf == elf; - _bigCons.put(relo,new Relo(relo,t,(byte)off,(byte)elf)); - } - - // -------------------------------------------------- - void encode() { - // Basic block layout: negate branches to keep blocks in-order; insert - // unconditional jumps. Attempt to keep backwards branches taken, - // forwards not-taken (this is the default prediction on most - // hardware). Layout is still Reverse Post Order but with more - // restrictions. - basicBlockLayout(); - - // Write encoding bits in order into a big byte array. - // Record opcode start and length. - writeEncodings(); - - // Short-form RIP-relative support: replace long encodings with short - // encodings and compact the code, changing all the offsets. - compactShortForm(); - - // Patch RIP-relative and local encodings now. - patchLocalRelocations(); - } - - // -------------------------------------------------- - // Basic block layout: negate branches to keep blocks in-order; insert - // unconditional jumps. Attempt to keep backwards branches taken, forwards - // not-taken (this is the default prediction on most hardware). Layout is - // still Reverse Post Order but with more restrictions. - private void basicBlockLayout() { - IdentityHashMap> rpos = new IdentityHashMap<>(); - Ary rpo = new Ary<>(CFGNode.class); - rpos.put(_code._start.loop(),rpo); - BitSet visit = _code.visit(); - rpo.add(_code._stop); - for( Node n : _code._start._outputs ) - if( n instanceof FunNode fun ) { - int x = rpo._len; - _rpo_cfg(fun, visit, rpos ); - assert rpo.at(x) instanceof ReturnNode; - } - rpo.add(_code._start); - - // Reverse in-place - for( int i=0; i< rpo.size()>>1; i++ ) - rpo.swap(i,rpo.size()-1-i); - visit.clear(); - _code._cfg = rpo; // Save the new ordering - } - - - private void _rpo_cfg(CFGNode bb, BitSet visit, IdentityHashMap> rpos ) { - if( bb==null || visit.get(bb._nid) ) return; - visit.set(bb._nid); - CFGNode next = bb.uctrl(); - - // Loops run an inner "rpo_cfg", then append the entire loop body in - // place. This keeps loop bodies completely contained, although if - // Some Day Later we have real profile data we ought to arrange the - // branch orderings based on frequency. - if( bb instanceof LoopNode loop ) { - Ary body = new Ary<>(CFGNode.class); // Private RPO for the loop - rpos.put(loop.loop(),body); // Find it via loop tree - _rpo_cfg(next,visit,rpos); // RPO the loop - body.add(loop); // Include loop last (first in RPO) - Ary outer = rpos.get(loop.cfg(1).loop()); - outer.addAll(body); // Append to original CFG - return; - } - - // IfNodes visit 2 sides, and may choose to reorder them in the RPO - if( bb instanceof IfNode iff ) { - // Pick out the T/F projections - CProjNode t = iff.cproj(0); - CProjNode f = iff.cproj(1); - // Invert the branch or not - if( shouldInvert(t,f,iff.loopDepth()) ) { - iff.negate(); - t.invert(); - f.invert(); - CProjNode tmp=f; f=t; t=tmp; // Swap t/f - } - - // Whichever side is visited first becomes last in the RPO. With - // no loops, visit the False side last (so True side first) so that - // when the False RPO visit returns, the IF is immediately next. - // When the RPO is reversed, the fall-through path will always be - // following the IF. - _rpo_cfg(t,visit,rpos); // True side first - next = f; // False side last - } - - // If the *next* BB has already been visited, and we are not already a - // jump, we may need an unconditional forwards jump here - Ary rpo = rpos.get(bb.loop()); - if( next!=null && visit.get(next._nid) && !(bb instanceof IfNode) ) { - boolean needJump = next instanceof LoopNode - // Empty blocks from an IF will invert the IF and backwards - // branch to the loop head. If not empty, or not an IF - // will need a jump. - ? (bb.nOuts()>1 || !(bb.cfg0() instanceof IfNode) ) - // Forwards jump. If all the blocks, in RPO order, to our - // target are empty, we will fall in and not need a jump. - : !backwardsEmptyScan(rpo,next); - if( needJump ) { - CFGNode jmp = _code._mach.jump(); - jmp._ltree = bb._ltree; - jmp.setDefX(0,bb); - next.setDef(next._inputs.find(bb),jmp); - rpo.add(jmp); - } - } - - _rpo_cfg(next,visit,rpos); - rpo.add(bb); - } - - // Should this test be inverted? - private static boolean shouldInvert(CFGNode t, CFGNode f, int bld) { - int tld = t.loopDepth(), fld = f.loopDepth(); - // These next two are symmetric and can happen in any order; if `tld < - // bld` is true, the `fld < bld` must be false, or else both directions - // exit the loop... and the IF test would not be in the loop. - - // true to exit a loop usually (and false falls into Yet Another Loop - // Block), invert if false is taking an empty backwards branch. - if( tld < bld ) return forwardsEmptyScan(f,bld); - // false to exit a loop, normally invert to true (except empty back) - if( fld < bld ) return !forwardsEmptyScan(t,bld); - - // Not exiting the loop; staying at this depth (or going deeper loop). - - // Jump/true to an empty backwards block - if( forwardsEmptyScan(t,bld) ) return false; - if( forwardsEmptyScan(f,bld) ) return true ; - - // Fall/false into a full block, Jump/true to an empty block. - if( f.nOuts()>1 && t.nOuts()==1 ) return false; - if( t.nOuts()>1 && f.nOuts()==1 ) return true ; - // Jump to a merge point, assuming other things are jumping there as well - if( f.out(0) instanceof RegionNode ) return true ; - if( t.out(0) instanceof RegionNode ) return false; - // Everything else equal, use pre-order - return t._pre > f._pre; - } - - // Do we continue forwards from 'c' to the Loop back edge, without doing - // anything else? Then this branch can directly jump to the loop head. - private static boolean forwardsEmptyScan( CFGNode c, int bld ) { - if( c.nOuts()!=1 || c.loopDepth()!=bld ) return false; - return c.uctrl() instanceof RegionNode cfg && - ((cfg instanceof LoopNode && cfg._ltree==c._ltree) || forwardsEmptyScan(cfg,bld)); - } - - // Is the CFG from "next" to the end empty? This means jumping to "next" - // will naturally fall into the end. - private static boolean backwardsEmptyScan(Ary rpo, CFGNode next) { - for( int i=rpo._len-1; rpo.at(i)!=next; i-- ) - if( rpo.at(i).nOuts()!=1 ) - return false; - return true; - } - - // -------------------------------------------------- - // Write encoding bits in order into a big byte array. - // Record opcode start and length. - public FunNode _fun; // Currently encoding function - private void writeEncodings() { - _opStart= new int [_code.UID()]; - _opLen = new byte[_code.UID()]; - for( CFGNode bb : _code._cfg ) { - if( !(bb instanceof MachNode mach0) ) - _opStart[bb._nid] = _bits.size(); - else if( bb instanceof FunNode fun ) { - _fun = fun; // Currently encoding function - _opStart[bb._nid] = _bits.size(); - mach0.encoding( this ); - _opLen[bb._nid] = (byte) (_bits.size() - _opStart[bb._nid]); - } - for( Node n : bb._outputs ) { - if( n instanceof MachNode mach && !(n instanceof FunNode) ) { - _opStart[n._nid] = _bits.size(); - mach.encoding( this ); - _opLen[n._nid] = (byte) (_bits.size() - _opStart[n._nid]); - } - } - } - } - - // -------------------------------------------------- - // Short-form RIP-relative support: replace short encodings with long - // encodings and expand the code, changing all the offsets. - private void compactShortForm() { - int len = _code._cfg._len; - int[] oldStarts = new int[len]; - for( int i=0; i 0 ) { // If no short-form ops, nothing to do here - int end = _bits.size(); - byte[] bits = new byte[end+grow]; - for( int i=len-1; i>=0; i-- ) { - int start = oldStarts[i]; - if( start==0 && i>1 ) continue; - int oldStart = oldStarts[i]; - int newStart = _opStart[_code._cfg.at(i)._nid]; - System.arraycopy(_bits.buf(),oldStart,bits,newStart,end-start); - end = start; - } - _bits.set(bits,bits.length); - } - - } - - - // -------------------------------------------------- - // Patch local encodings now - void patchLocalRelocations() { - // Walk the local code-address relocations - for( Node src : _internals.keySet() ) { - int start = _opStart[src._nid]; - Node dst = _internals.get(src); - int target = _opStart[dst._nid]; - ((RIPRelSize)src).patch(this, start, _opLen[src._nid], target - start); - } - } - - // -------------------------------------------------- - // Write the constant pool into the BAOS and optionally patch locally - void writeConstantPool( BAOS bits, boolean patch ) { - padN(16,bits); - - // radix sort the big constants by alignment - Ary[] raligns = new Ary[5]; - for( Node op : _bigCons.keySet() ) { - Relo relo = _bigCons.get(op); - int align = relo._t.alignment(); - Ary relos = raligns[align]==null ? (raligns[align]=new Ary<>(Relo.class)) : raligns[align]; - relos.add(relo); - } - - - // Types can be used more than once; collapse the dups - HashMap targets = new HashMap<>(); - - // By alignment - for( int align = 4; align >= 0; align-- ) { - Ary relos = raligns[align]; - if( relos == null ) continue; - for( Relo relo : relos ) { - // Map from relo to constant start and patch - Integer target = targets.get(relo._t); - if( target==null ) { - targets.put(relo._t,target = bits.size()); - // Write constant into constant pool - switch( relo._t ) { - case TypeTuple tt -> cpool(align,bits,tt); - case TypeStruct ts -> cpool(bits,ts); - // Simple primitive (e.g. larger int, float) - default -> addN(align,relo._t,bits); - } - } - // Record target address and opcode start - relo._target = target; - relo._opStart= _opStart[relo._op._nid]; - // Go ahead and locally patch in-memory - if( patch ) - ((RIPRelSize)relo._op).patch(this, relo._opStart, _opLen[relo._op._nid], relo._target - relo._opStart); - } - } - } - - // Constant tuples put all entries at same alignment - private void cpool(int align, BAOS bits, TypeTuple tt) { - for( Type tx : tt._types ) - addN(align,tx,bits); - } - - // Structs use internal field layout - private void cpool( BAOS bits, TypeStruct ts ) { - // Field order by offset - int[] layout = ts.layout(); - int off=0; // offset in the struct - for( int fn=0; fn SENTINEL_CALLOC; - case "write" -> SENTINEL_WRITE ; - default -> throw Utils.TODO(); - }; - ((RIPRelSize)src).patch(this, start, _opLen[src._nid], target - start); - } - } -} diff --git a/src/main/java/com/seaofnodes/simple/codegen/GlobalCodeMotion.java b/src/main/java/com/seaofnodes/simple/codegen/GlobalCodeMotion.java deleted file mode 100644 index 520a18a3..00000000 --- a/src/main/java/com/seaofnodes/simple/codegen/GlobalCodeMotion.java +++ /dev/null @@ -1,322 +0,0 @@ -package com.seaofnodes.simple.codegen; - -import com.seaofnodes.simple.IterPeeps.WorkList; -import com.seaofnodes.simple.node.*; -import com.seaofnodes.simple.type.*; -import com.seaofnodes.simple.util.Ary; -import com.seaofnodes.simple.util.Utils; -import java.util.*; - -public abstract class GlobalCodeMotion { - - // 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 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); - } - - // Break up shared global constants by functions - private static void breakUpGlobalConstants( Node start ) { - // For all global constants - for( int i=0; i< start.nOuts(); i++ ) { - Node con = start.out(i); - if( con instanceof MachNode mach && mach.isClone() ) { - breakUpGlobalConstants(con); - // While constant has users in different functions - while( true ) { - // Find a function user, and another function - FunNode fun = null; - boolean done=true; - for( Node use : con.outs() ) { - FunNode fun2 = use instanceof ReturnNode ret ? ret.fun() : use.cfg0().fun(); - if( fun==null || fun==fun2 ) fun=fun2; - else { done=false; break; } - } - // Single function user, so this constant is not shared - if( done ) { - if( con.in(0)==start ) i--; - con.setDef(0,fun); - break; - } - // Move function users to a private constant - Node con2 = mach.copy(); // Private constant clone - con2._inputs.set(0,null); // Preserve edge invariants from clone - con2.setDef(0,fun); - // Move function users to this private constant - for( int j=0; j 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()]; - // Breadth-first scheduling - breadth(code._stop,ns,late); - - // 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._isLoad ) - for( Node memuse : ld.mem()._outputs ) - if( late[memuse._nid]==null && - // New makes new memory, never crushes load memory - !(memuse instanceof NewNode) && - // Load-use directly defines memory - (memuse._type instanceof TypeMem || - // Load-use directly defines memory - memuse instanceof CallNode || - // Load-use indirectly defines memory - (memuse._type instanceof TypeTuple tt && tt._types[ld._alias] instanceof TypeMem)) ) - continue outer; - - // All uses done, schedule - _doSchedLate(n,ns,late); - } - - // Walk all inputs and put on worklist, as their last-use might now be done - for( Node def : n._inputs ) { - if( def!=null && late[def._nid]==null ) - work.push(def); - // if the def has a load use, maybe the load can fire - if( def!=null ) - for( Node out : def._outputs ) - if( out instanceof MemOpNode ld && ld._isLoad && late[ld._nid]==null ) - work.push(ld); - } - - 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) { - // 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)._idom(lca,null); - - // Loads may need anti-dependencies, raising their LCA - if( n instanceof MemOpNode load && load._isLoad ) - lca = find_anti_dep(lca,load,early,late); - - - // Nodes setting a single register and getting killed will stay close - // to the uses, since they will be forced to spill anyway. The kill - // check is very weak, and some may be hoisted only to spill in the RA. - if( n instanceof MachNode mach ) { - RegMask out = mach.outregmap(); - if( out!=null && out.size1() ) { - int reg = mach.outregmap().firstReg(); - // Look for kills - outer: - for( CFGNode lca2=lca; lca2 != early; lca2 = lca2.idom() ) { - if( lca2 instanceof MachNode mach2 ) { - for( int i=1; i best.idepth() || - best instanceof IfNode; - } - - private static CFGNode find_anti_dep(CFGNode lca, MemOpNode load, CFGNode early, CFGNode[] late) { - // We could skip final-field loads here. - // Walk LCA->early, flagging Load's block location choices - for( CFGNode cfg=lca; early!=null && cfg!=early.idom(); cfg = cfg.idom() ) - cfg._anti = load._nid; - // Walk load->mem uses, looking for Stores causing an anti-dep - for( Node mem : load.mem()._outputs ) { - switch( mem ) { - case MemOpNode st: - if( !st._isLoad && load._alias == st._alias ) { - assert late[mem._nid] != null; - lca = anti_dep( load, late[mem._nid], mem.cfg0(), lca, st ); - } - break; // Loads do not cause anti-deps on other loads - case CallNode call: - assert late[call._nid]!=null; - lca = anti_dep(load,late[call._nid],call.cfg0(),lca,call); - 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 - 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()); - - if( alloc.success() ) - convert2DAdjacency(alloc); - 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; i1 || // Has many users OR - // Only 1 user but effective use is remote block - effUseBlk != n.cfg0() )) - // Then fail the clonable; it should split or move - alloc.fail(alloc.lrg((Node)m)); - // Else clonable cannot move - else if( !tlrg.sub(killMask) ) - alloc.fail(tlrg); - } - } - } - - // Check for self-conflict live ranges. These must split, and only happens - // during the first round a particular LRG splits. - private static void selfConflict(RegAlloc alloc, Node n, LRG lrg) { - selfConflict(alloc,n,lrg,TMP.get(lrg)); - } - private static void selfConflict(RegAlloc alloc, Node n, LRG lrg, Node prior) { - if( prior!=null && prior != n ) { - lrg.selfConflict(prior); - lrg.selfConflict(n); - alloc.fail(lrg); // 2 unrelated values live at once same live range; self-conflict - } - } - - // Merge TMP into bb's live-out set; if changes put bb on WORK - private static void mergeLiveOut( RegAlloc alloc, CFGNode priorbb, int i ) { - CFGNode bb = priorbb.cfg(i); - if( bb == null ) return; // Start has no prior - while( !bb.blockHead() ) bb = bb.cfg0(); - - // Lazy get live-out set for bb - IdentityHashMap 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 ); - } - 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); - } - } - } - - - static void convert2DAdjacency( RegAlloc alloc ) { - // Convert the 2-D array of bits (a 1-D array of BitSets) into an - // adjacency matrix. - int maxlrg = alloc._LRGS.length; - for( int i=1; i=0; lrg=ifg.nextSetBit(lrg+1) ) { - LRG lrg1 = alloc._LRGS[lrg]; - lrg0.addNeighbor(lrg1); - lrg1.addNeighbor(lrg0); - } - } - } - } - - - // ------------------------------------------------------------------------ - // 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) { - int maxlrg = alloc._LRGS.length, nlrgs=0; - for( int i=1; 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 ) { - // Pick single-def clonables that are not right next to their single-use. - // Failing to color these will clone them closer to their uses. - if( !lrg._multiDef && lrg._machDef.isClone() ) { - Node def = ((Node)lrg._machDef); - Node use = ((Node)lrg._machUse); - CFGNode cfg = def.cfg0(); - if( cfg != use.cfg0() || // Different blocks OR - // Same block, but not close - cfg._outputs.find(def) < cfg._outputs.find(use)+1 ) - return 1000000; - } - - // Always pick callee-save registers as being very large area recovered - // and very cheap to spill. - if( lrg._machDef instanceof CalleeSaveNode ) - return 1000000-2-lrg._mask.firstReg(); - if( lrg._splitDef != null && lrg._splitDef. in(1) instanceof CalleeSaveNode && - lrg._splitUse != null && lrg._splitUse.out(0) instanceof ReturnNode ) - return 1000000-1; - - // 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 def = lrg._splitDef, use = lrg._splitUse; - int tidx=0, cnt=0; - - while( def != null || use != null ) { - if( cnt++ > 10 ) break; - - if( def != null ) { - short bias = biasColor( alloc, def, mask ); - if( bias >= 0 ) return bias; // Good bias - if( bias == -2 ) def = null; // Kill this side, no more searching - else if( (tidx=biasable(def)) == 0 ) def = null; - } - - if( use != null ) { - short bias = biasColor( alloc, use, mask ); - if( bias >= 0 ) return bias; // Good bias - if( bias == -2 ) use = null; // Kill this side, no more searching - else if( biasable(use)==0 ) use = null; - } - - if( def != null ) { - short bias = biasColorNeighbors( alloc, def, mask ); - if( bias >= 0 ) return bias; - // Advance def side - def = def.in(tidx); - if( alloc.lrg(def)==null ) def=null; - } - - if( use != null ) { - short bias = biasColorNeighbors( alloc, use, mask ); - if( bias >= 0 ) return bias; - use = use.out(0); - if( biasable(use)==0 ) use=null; - } - - } - 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 phi ) return phi.region() instanceof LoopNode ? 2 : 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 ) { - short reg = alrg._reg; - if( reg == -1 && alrg._mask.size1() ) - reg = alrg._mask.firstReg(); - if( reg != -1 ) { - mask.clr(alrg._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 deleted file mode 100644 index 009ddcdb..00000000 --- a/src/main/java/com/seaofnodes/simple/codegen/LRG.java +++ /dev/null @@ -1,233 +0,0 @@ -package com.seaofnodes.simple.codegen; - -import com.seaofnodes.simple.node.*; -import com.seaofnodes.simple.util.Ary; -import com.seaofnodes.simple.util.SB; -import com.seaofnodes.simple.util.Utils; -import java.util.IdentityHashMap; - -/** A Live Range - *

- * 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; - // Count of all defs, uses. Mostly interested 1 vs many - boolean _multiDef, _multiUse; - - // 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; - - public int nadj() { return _adj==null ? 0 : _adj._len; } - - 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 nadj() < _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( _machDef != lrg._machDef ) _multiDef=true; - if( _1regDefCnt==0 ) - _machDef = lrg._machDef; - else if( _machDef==lrg._machDef ) - _1regDefCnt--; - } - _1regDefCnt += lrg._1regDefCnt; - _multiDef |= lrg._multiDef; - - if( _machUse==null ) { - _machUse = lrg._machUse; - _uidx = lrg._uidx; - } else if( lrg._machUse!=null ) { - if( _machUse != lrg._machUse ) _multiUse=true; - if( _1regUseCnt==0 ) { - _machUse = lrg._machUse; - _uidx = lrg._uidx; - } - else if( _machUse==lrg._machUse ) - _1regUseCnt--; - } - _1regUseCnt += lrg._1regUseCnt; - _multiUse |= lrg._multiUse; - - // Fold deepest Split - _splitDef = deepSplit(_splitDef,lrg._splitDef); - _splitUse = deepSplit(_splitUse,lrg._splitUse); - - // Fold together masks - RegMask mask = _mask.and(lrg._mask); - if( mask==null ) - mask = _mask.copy().and(lrg._mask); - _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 && _machDef!=def ) - _multiDef = true; - 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 && _machUse!=use ) - _multiUse = true; - 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/LinkMem.java b/src/main/java/com/seaofnodes/simple/codegen/LinkMem.java deleted file mode 100644 index 2415203d..00000000 --- a/src/main/java/com/seaofnodes/simple/codegen/LinkMem.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.seaofnodes.simple.codegen; - -import com.seaofnodes.simple.node.Node; -import com.seaofnodes.simple.util.Utils; - -// In-memory linking -public class LinkMem { - final CodeGen _code; - LinkMem( CodeGen code ) { _code = code; } - - public CodeGen link() { - Encoding enc = _code._encoding; - - // Patch external calls internally - enc.patchGlobalRelocations(); - - // Write any large constants into a constant pool; they - // are accessed by RIP-relative addressing. - enc.writeConstantPool(enc._bits,true); - - return _code; - } -} diff --git a/src/main/java/com/seaofnodes/simple/codegen/ListScheduler.java b/src/main/java/com/seaofnodes/simple/codegen/ListScheduler.java deleted file mode 100644 index ddf5a679..00000000 --- a/src/main/java/com/seaofnodes/simple/codegen/ListScheduler.java +++ /dev/null @@ -1,261 +0,0 @@ -package com.seaofnodes.simple.codegen; - -import com.seaofnodes.simple.IterPeeps.WorkList; -import com.seaofnodes.simple.node.*; -import com.seaofnodes.simple.type.*; -import com.seaofnodes.simple.util.Ary; -import com.seaofnodes.simple.util.Utils; -import java.util.*; - -public abstract class ListScheduler { - - // 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); - - if( n instanceof NewNode || n instanceof CallNode ) - bb.fun()._hasCalls = true; - 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 deleted file mode 100644 index 2766414f..00000000 --- a/src/main/java/com/seaofnodes/simple/codegen/Machine.java +++ /dev/null @@ -1,97 +0,0 @@ -package com.seaofnodes.simple.codegen; - -import com.seaofnodes.simple.node.*; -import com.seaofnodes.simple.type.TypeFunPtr; - -abstract public class Machine { - // Human readable machine name. Something like "x86-64" or "arm" or "risc5" - public abstract String name(); - // Default opcode size for printing; 4 bytes for most 32-bit risc chips - public int defaultOpSize() { return 4; } - // Human-readable name for a register number, e.g. "RAX" or "R0" - public abstract String[] regs(); - // 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, as a 64bit mask - public abstract long callerSave(); - // List of never-save registers, e.g. RSP or a ZERO register if you have one - public abstract long neverSave(); - // Call Argument Mask. Passed in the function signature and argument - // number (2-based; 0 is for control and 1 for memory). Also passed in a 0 - // for the function itself, or for *outgoing* calls, the maximum stack slot - // given to the incoming function arguments (stack slots reserved for - // incoming arguments). - public abstract RegMask callArgMask(TypeFunPtr tfp, int arg, int maxArgSlot); - // Return register mask, based on signature (GPR vs FPR) - public abstract RegMask retMask(TypeFunPtr tfp); - // Return PC register - public abstract int rpc(); - // Return a MachNode unconditional branch - public abstract CFGNode jump(); - // Instruction select from ideal nodes - public abstract Node instSelect( Node n ); - - /** Stack Slot Numbering - -RA gives all registers a number, starting at 0. Stack slots continue this -numbering going up from the last register number. Common register numbers are -0-15 for GPRs, 16-31 for FPRs, 32 for flags, and stack slots starting at -register#33 going up. These numbers are machine-specific, YMMV, etc; e.g. RPC -is only in stack slot 0 on X86; other cpus start with the rpc in a register -which may spill into any generic spill slot. - -For ease of reading and printing, stack slots start again at slot#0 - but -during RA they are actually biased by CPU.MAX_REG. - -Stack Layout during Reg Alloc: - - Slot Value Description - ---- ------- ----------- -MAXREG+ R+1 argR maxArgSlot(calleR tfp), can be 0 - ... -MAXREG+ 1 arg0 - ---- -- Caller ------ -MAXREG+ 0 RPC -MAXREG+ R+E+N PAD optional alignment - -MAXREG+ + Spills Caller Frame Space; N spills -MAXREG+ R+E+1 Spills - -MAXREG+ R+E argE maxArgSlot(calleE tfp), can be 0 - ... -MAXREG+ R+2 arg0 - ---- -- Callee ------- - ... - -Slots defined by maxArgSlot do not have to contain the actual passed argument; -they can be "shadow slots". If they exist, they are property of the frame / -FunNode where the TFP comes from. - -For a given calleR, there are generally many calleE's and they all share the -low-range stack slots. - -Example:, Win64, main(int arg) { calloc(1,size); } - - Post-Warp: SP+56 shadow-main-arg -Pre-alloc Post-Alloc SP+48 RPC ---- ------- ---------- ------------- -40 spill#3 SP+40 SP+40 spill#3 -39 spill#2 SP+32 "" -38 spill#1 SP+24 "" -37 spill#0 SP+16 "" spill#0 -36 shadow-calloc#size SP+ 8 SP+ 8 shadow-calloc#size -35 shadow-calloc#1 SP+ 0 SP+ 0 shadow-calloc#1 ---- --- FRAME BASE ---------- ---- FRAME BASE ------- -34 shadow-main-arg SP+56 -33 RPC SP+48 - - - */ - - - // Maximum stack slot (or 0) for the args in this TFP. This will include - // shadow slots if defined in the ABI, even if all arguments are passed in - // registers. - public abstract short maxArgSlot(TypeFunPtr tfp); - -} diff --git a/src/main/java/com/seaofnodes/simple/codegen/RIPRelSize.java b/src/main/java/com/seaofnodes/simple/codegen/RIPRelSize.java deleted file mode 100644 index 777182dc..00000000 --- a/src/main/java/com/seaofnodes/simple/codegen/RIPRelSize.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.seaofnodes.simple.codegen; - -// This Node rip-relative encodings has sizes that vary by delta -public interface RIPRelSize { - // delta is the distance from the opcode *start* to the target start. Each - // hardware target adjusts as needed, X86 measures from the opcode *end* - // and small jumps are 2 bytes, so they'll need to subtract 2. - byte encSize(int delta); - - // Patch full encoding in. Used for both short and long forms. delta - // again is from the opcode *start*. - void patch( Encoding enc, int opStart, int opLen, int delta ); - -} diff --git a/src/main/java/com/seaofnodes/simple/codegen/RegAlloc.java b/src/main/java/com/seaofnodes/simple/codegen/RegAlloc.java deleted file mode 100644 index da521082..00000000 --- a/src/main/java/com/seaofnodes/simple/codegen/RegAlloc.java +++ /dev/null @@ -1,639 +0,0 @@ -package com.seaofnodes.simple.codegen; - -import com.seaofnodes.simple.util.Ary; -import com.seaofnodes.simple.util.Utils; -import com.seaofnodes.simple.node.*; -import java.util.Arrays; -import java.util.BitSet; -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,""); - } - } - } - - - 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 ) { return reg(n,null); } - String reg( Node n, FunNode fun ) { - LRG lrg = lrg(n); - if( lrg==null ) return null; - // No register yet, use LRG - if( lrg._reg == -1 ) return "V"+lrg._lrg; - // Chosen machine register unless stack-slot and past RA - String[] regs = _code._mach.regs(); - if( lrg._reg < regs.length || _code._phase.ordinal() <= CodeGen.Phase.RegAlloc.ordinal() || fun==null ) - return RegMask.reg(regs,lrg._reg); - // Stack-slot past RA uses the frame layout logic - return "[rsp+"+fun.computeStackOffset(_code,lrg._reg)+"]"; - } - - // ----------------------- - RegAlloc( CodeGen code ) { _code = code; } - - public void regAlloc() { - // Insert callee-save registers - String[] regs = _code._mach.regs(); - long neverSave= _code._mach.neverSave(); - for( CFGNode bb : _code._cfg ) - if( bb instanceof FunNode fun ) { - ReturnNode ret = fun.ret(); - int len = Math.min(regs.length,64); - for( int reg=0; reg= 7 ) // Really expect to be done soon - throw Utils.TODO("Allocator taking too long"); - round++; - } - postColor(); // Remove no-op spills - } - - 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 - // Conservative coalesce copies - Coalesce.coalesce(round,this) && - // Color attempt - IFG.color(round,this); // If colorable - } - - // ----------------------- - // 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. - - // Sort, to avoid non-deterministic HashMap ordering - 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, top level heuristic - 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() && (!lrg._multiDef || lrg._1regUseCnt==1) ) { - if( lrg._1regDefCnt <= 1 && - lrg._1regUseCnt <= 1 && - (lrg._1regDefCnt + lrg._1regUseCnt) > 0 ) - return splitEmptyMaskSimple(round,lrg); - // Repeated single-reg uses from a single def. Special for archs - // with more fixed regs. - if( !lrg._multiDef && lrg._1regDefCnt <= 1 && lrg._1regUseCnt > 2 ) - if( splitEmptyMaskByUse(round,lrg) ) - return true; - } - - // Generic split-by-loop depth. - 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() ) - // Force must-split, even if a prior split same block because register - // conflicts. 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/*true*/); - // Split just before use - if( lrg._1regUseCnt==1 || (lrg._1regDefCnt==1 && ((Node)lrg._machDef).nOuts()==1) ) - insertBefore((Node)lrg._machUse,lrg._uidx,"use/empty1",round,lrg); - return true; - } - - // Single-def live range with an empty mask. There are many single-reg - // uses. Theory is there's many repeats of the same reg amongst the uses. - // In of splitting once per use, start by splitting into groups based on - // required input register. - boolean splitEmptyMaskByUse( byte round, LRG lrg ) { - Node def = (Node)lrg._machDef; - - // Look at each use, and break into non-overlapping register classes. - Ary rclass = new Ary<>(RegMask.class); - boolean done=false; - int ncalls=0; - while( !done ) { - done = true; - for( Node use : def._outputs ) - if( use instanceof MachNode mach ) { - if( mach instanceof CallNode ) ncalls++; - for( int i=1; i 1 ) return false; - - // Split by class - Ary ns = new Ary<>(Node.class); - for( RegMask rmask : rclass ) { - ns.addAll(def._outputs); - Node split = makeSplit(def,"popular",round,lrg,rmask); - split.insertAfter( def ); - if( split.nIns()>1 ) split.setDef(1,def); - // all uses by class to split - for( int j=0; j < def._outputs._len; j++ ) { - Node use = def._outputs.at(j); - if( use instanceof MachNode mach && use!=split ) { - // Check all use inputs for n, in case there's several - for( int i = 1; i < use.nIns(); i++ ) - // Find a def input, and check register class - if( use.in( i ) == def && mach.regmap( i ).overlap( rmask ) ) { - RegMask m = mach.regmap( i ); - if( m!=null && mach.regmap( i ).overlap( rmask ) ) - // Modify use to use the split version specialized to this rclass - { use.setDefOrdered( i, split ); j--; break; } - } - } - } - ns.clear(); - } - return true; - } - - - // Put use into a register class, perhaps adding a class or perhaps - // narrowing a class (and causing a repeat) - private static boolean putIntoRegClass( Ary rclass, RegMask rmask ) { - for( int i=0; i 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 which extends the LRG. Split also before - // Phi slot 1 (and not all inputs), because Phis extend the live range. - // TODO: split before all inputs (except the last; at least 1 split here must be extra) - 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(); - insertBefore(phi,1,"use/self/phi",round,lrg); - } - // Split before two-address ops which extend the live range - if( def instanceof MachNode mach && mach.twoAddress()!= 0 ) - insertBefore(def,mach.twoAddress(),"use/self/two",round,lrg); - } - return true; - } - - - // Generic: split around the outermost loop with non-split def/uses. This - // covers both self-conflicts (once we split deep enough) and register - // pressure issues. - boolean splitByLoop( byte round, LRG lrg ) { - findAllLRG(lrg); - - // Find min loop depth for all non-split defs and uses. - long ld = (-1L<<32) | 9999; - for( Node n : _ns ) { - if( lrg(n)==lrg ) // This is a LRG def - ld = ldepth(ld,n,n.cfg0()); - // PhiNodes check all CFG inputs - if( n instanceof PhiNode phi ) { - 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 && min!=max ) continue; // Ignoring splits; since spilling need to split in a deeper loop - if( n.isDead() ) continue; // Some Cloneable went dead by other spill changes - // If this is a 2-address commutable op (e.g. AddX86, MulX86) and the rhs has only a single user, - // commute the inputs... which chops the LHS live ranges' upper bound to just the RHS. - if( n instanceof MachNode mach && lrg(n)==lrg && mach.twoAddress()==1 && mach.commutes() && n.in(2).nOuts()==1 ) - 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 adjacent - !(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(); - // if n will lower the min loop and is in the tail end of the loop - // header, splitting "around" the loop will not help. Treat n as being - // in the loop. - if( d < min ) { - if( cfg.uctrl() instanceof LoopNode loop && loop.entry()==cfg ) { - for( int i=cfg.nOuts()-2; i>=0; i-- ) { - Node out = cfg.out(i); - if( n==out ) - { d = loop.loopDepth(); break; } // Treat n as being "in the loop" - if( !((out instanceof MachNode mach && mach.isClone()) || out instanceof SplitNode ) ) - break; // Treat b as "normal", out of loop - } - } - } - - // lower min, raise max, and re-fold - 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; - _ns.push((Node)lrg._machDef); - _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, boolean skip) { - Node def = n.in(i); - // Effective block for use - CFGNode cfg = n instanceof PhiNode phi ? phi.region().cfg(i) : n.cfg0(); - // Use-side RegMask, if available - RegMask umask = n instanceof MachNode mach ? mach.regmap(i) : null; - // Def is a split ? - if( skip && def instanceof SplitNode ) { - boolean singleReg = umask!=null && umask.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; - } - makeSplit(def,kind,round,lrg,umask).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)); - } - void insertBefore(Node n, int i, String kind, byte round, LRG lrg) { - insertBefore(n,i,kind,round,lrg,true); - } - - // 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 umask ) { - // Clone simple constants if possible - Node split = def instanceof MachNode mach && mach.isClone() && (umask==null || mach.outregmap().overlap(umask)) - ? mach.copy() - : _code._mach.split(kind,round,lrg); - _lrgs.put(split,lrg); - 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() { - int maxReg = -1; - for( CFGNode bb : _code._cfg ) { // For all ops - if( bb instanceof FunNode ) - maxReg = -1; // Reset for new function - // Compute frame size, based on arguments and largest reg seen - if( bb instanceof ReturnNode ret ) - ret.fun().computeFrameAdjust(_code,maxReg); - // Raise frame size by max stack args passed, even if ignored - if( bb instanceof CallEndMach cend ) - maxReg = Math.max(maxReg,cend._xslot); - - 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); - } - // Check no clobbers - for( int idx = j-1; bb.out(idx) != hi; idx++) { - Node n = bb.out(idx); - if( lrg(n)!=null && lrg(n)._reg == defreg ) - return false; // Clobbered - } - lo.setDefOrdered(1,hi.in(1)); - return true; - } - - - 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( lrg(n)!=null && lrg(n)._reg == 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 deleted file mode 100644 index c7dda1da..00000000 --- a/src/main/java/com/seaofnodes/simple/codegen/RegMask.java +++ /dev/null @@ -1,126 +0,0 @@ -package com.seaofnodes.simple.codegen; - -import com.seaofnodes.simple.util.SB; -import com.seaofnodes.simple.util.Utils; - -/** RegMask - * A "register mask" - 1 bit set for each allowed register. In addition, - * "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<64 ) { - long bits0 = _bits0 >> (reg+1); - if( bits0!=0 ) - return (short)(reg+1+Long.numberOfTrailingZeros(bits0)); - reg=64; - } - long bits1 = _bits1 >> (reg-64+1); - if( bits1!=0 ) - return (short)(reg+1+Long.numberOfTrailingZeros(bits1)); - return -1; - } - - boolean isEmpty() { return _bits0==0 && _bits1==0; } - - public 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 ((_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("["); - String[] regs = mach.regs(); - for( int i=0; i<64; i++ ) - if( ((_bits0 >> i)&1) != 0 ) - sb.p(reg(regs,i)).p(","); - for( int i=0; i<64; i++ ) - if( ((_bits1 >> i)&1) != 0 ) - sb.p(reg(regs,i+64)).p(","); - return sb.unchar().p("]"); - } - - public static String reg(String[] regs, int i) { - return i 2 ) { - // Only valid to rotate constants if both are same associative ops - if( lhs.getClass() != op.getClass() ) return null; - lphi = pcon(lhs.in(2),op); // Will rotate with the Phi push - } - if( lphi==null ) return null; - if( lphi.region().nIns() <=2 ) return null; // Phi is collapsing - - // RHS is a constant or a Phi of constants - if( !(rhs instanceof ConstantNode) && pcon(rhs,op)==null ) - return null; - - // If both are Phis, must be same Region - if( rhs instanceof PhiNode && lphi.in(0) != rhs.in(0) ) - return null; - - // Note that this is the exact reverse of Phi pulling a common op down - // to reduce total op-count. We don't get in an endless push-up - // push-down peephole cycle because the constants all fold first. - Node[] ns = new Node[lphi.nIns()]; - ns[0] = lphi.in(0); - // Push constant up through the phi: x + (phi con0+con0 con1+con1...) - for( int i=1; i hi._nid; - } - - @Override Node copy(Node lhs, Node rhs) { return new AddNode(lhs,rhs); } - @Override Node copyF() { return new AddFNode(null,null); } -} diff --git a/src/main/java/com/seaofnodes/simple/node/AndNode.java b/src/main/java/com/seaofnodes/simple/node/AndNode.java deleted file mode 100644 index 70b57d6d..00000000 --- a/src/main/java/com/seaofnodes/simple/node/AndNode.java +++ /dev/null @@ -1,50 +0,0 @@ -package com.seaofnodes.simple.node; - -import com.seaofnodes.simple.Parser; -import com.seaofnodes.simple.type.Type; -import com.seaofnodes.simple.type.TypeInteger; - -public class AndNode extends ArithNode { - public AndNode(Parser.Lexer loc, Node lhs, Node rhs) { super(loc, lhs, rhs); } - - @Override public String label() { return "And"; } - @Override public String op() { return "&"; } - @Override public String glabel() { return "&"; } - - @Override long doOp( long x, long y ) { return x & y; } - @Override TypeInteger doOp(TypeInteger x, TypeInteger y) { - // Sharpen allowed bits if either value is narrowed - long mask = x.mask() & y.mask(); - return mask < 0 ? TypeInteger.BOT : TypeInteger.make(0,mask); - } - - @Override - public Node idealize() { - Node lhs = in(1); - 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). 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() ) - return swap12(); - - // Do we have ((x & (phi cons)) & con) ? - // Do we have ((x & (phi cons)) & (phi cons)) ? - // Push constant up through the phi: x & (phi con0&con0 con1&con1...) - Node phicon = AddNode.phiCon(this,true); - if( phicon!=null ) - return phicon; - - return super.idealize(); - } - @Override Node copy(Node lhs, Node rhs) { return new AndNode(_loc,lhs,rhs); } -} diff --git a/src/main/java/com/seaofnodes/simple/node/ArithNode.java b/src/main/java/com/seaofnodes/simple/node/ArithNode.java deleted file mode 100644 index 96067c8e..00000000 --- a/src/main/java/com/seaofnodes/simple/node/ArithNode.java +++ /dev/null @@ -1,101 +0,0 @@ -package com.seaofnodes.simple.node; - -import com.seaofnodes.simple.Parser; -import com.seaofnodes.simple.type.Type; -import com.seaofnodes.simple.type.TypeInteger; - -import java.util.BitSet; - -public abstract class ArithNode extends Node { - // Source location for late reported errors - Parser.Lexer _loc; - - public ArithNode( Parser.Lexer loc, Node lhs, Node rhs) { super(null, lhs, rhs); _loc = loc; } - @Override public String glabel() { return op(); } - abstract String op(); - - @Override - public StringBuilder _print1(StringBuilder sb, BitSet visited) { - in(1)._print0(sb.append("("), visited); - in(2)._print0(sb.append(op()), visited); - return sb.append(")"); - } - - abstract long doOp(long x, long y); - abstract TypeInteger doOp(TypeInteger x, TypeInteger y); - - // Generic airthmetic op math: high returns high; low returns low; 2 - // constants fold; only 2 non-constants call specialized math. - @Override - public final TypeInteger compute() { - Type t1 = in(1)._type, t2 = in(2)._type; - if( t1.isHigh() || t2.isHigh() ) - return TypeInteger.TOP; - if( t1 instanceof TypeInteger x && - t2 instanceof TypeInteger y ) - return x.isConstant() && y.isConstant() - ? con(x,y) - : doOp(x,y); - return TypeInteger.BOT; - } - - private TypeInteger con( Type t1, Type t2 ) { - return TypeInteger.constant( doOp( ((TypeInteger)t1).value(), ((TypeInteger)t2).value()) ); - } - - // Check for "op(Phi(con,x),Phi(con,y))" and push-up through the Phi. - // Note that this is the exact reverse of Phi pulling a common op down - // to reduce total op-count. We don't get in an endless push-up - // push-down peephole cycle because the constants all fold first. - // Returns "Phi(op(con,con),op(x,y))". - - // This check is slightly different from the - - // Expected to be called after other ideal checks are done. - @Override public Node idealize() { - if( in(1) instanceof PhiNode lhs && - in(2) instanceof PhiNode rhs && - lhs.nIns() >= 2 && !lhs.inProgress() && - lhs.region()==rhs.region() && - lhs.nIns()>2 && // A 1-input Phi will collapse already - // Disallow with self-looping phi; these will collapse - (lhs.in(2)!=lhs && rhs.in(2)!=rhs) ) { - // Profit check: only 1 instance of `this` will remain, all the - // others will fold to constants. - int cnt=0; - for( int i=1; i in(2)._nid ) - // Equals sorts by NID otherwise: non.high == non.low becomes non.low == non.high - return in(1)._type instanceof TypeFloat ? new EQF(in(2),in(1)) : new EQ(in(2),in(1)); - } - // Equals X==0 becomes a !X - if( (in(2)._type == ZERO || in(2)._type == Type.NIL) ) - return new NotNode(in(1)); - } - - // Do we have ((x * (phi cons)) * con) ? - // Do we have ((x * (phi cons)) * (phi cons)) ? - // Push constant up through the phi: x * (phi con0*con0 con1*con1...) - Node phicon = AddNode.phiCon(this,this instanceof EQ); - if( phicon!=null ) return phicon; - - return null; - } - - public static class EQ extends BoolNode { - public EQ(Node lhs, Node rhs) { super(lhs,rhs); } - public String op() { return "=="; } - TypeInteger doOp(TypeInteger i1, TypeInteger i2) { - if( i1==i2 && i1.isConstant() ) return TRUE; - if( i1._max < i2._min || i1._min > i2._max ) return FALSE; - return BOOL; - } - Node copy(Node lhs, Node rhs) { return new EQ(lhs,rhs); } - Node copyF() { return new EQF(null,null); } - } - - public static class NE extends BoolNode { - public NE(Node lhs, Node rhs) { super(lhs,rhs); } - public String op() { return "!="; } - TypeInteger doOp(TypeInteger i1, TypeInteger i2) { - if( i1==i2 && i1.isConstant() ) return TRUE; - if( i1._max < i2._min || i1._min > i2._max ) return FALSE; - return BOOL; - } - Node copy(Node lhs, Node rhs) { return new NE(lhs,rhs); } - Node copyF() { throw Utils.TODO(); } - } - - public static class LT extends BoolNode { - public LT(Node lhs, Node rhs) { super(lhs,rhs); } - public String op() { return "<" ; } - public String glabel() { return "<"; } - TypeInteger doOp(TypeInteger i1, TypeInteger i2) { - if( i1._max < i2._min ) return TRUE; - if( i1._min >= i2._max ) return FALSE; - return BOOL; - } - 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 "<="; } - public String glabel() { return "<="; } - TypeInteger doOp(TypeInteger i1, TypeInteger i2) { - if( i1._max <= i2._min ) return TRUE; - if( i1._min > i2._max ) return FALSE; - return BOOL; - } - Node copy(Node lhs, Node rhs) { return new LE(lhs,rhs); } - Node copyF() { return new LEF(null,null); } - } - - public static class EQF extends EQ { - public EQF(Node lhs, Node rhs) { super(lhs,rhs); } - @Override boolean doOp(double lhs, double rhs) { return lhs == rhs; } - @Override public boolean isFloat() { return true; } - @Override Node copy(Node lhs, Node rhs) { return new EQF(lhs,rhs); } - } - public static class LTF extends LT { - public LTF(Node lhs, Node rhs) { super(lhs,rhs); } - @Override boolean doOp(double lhs, double rhs) { return lhs < rhs; } - @Override public boolean isFloat() { return true; } - @Override Node copy(Node lhs, Node rhs) { return new LTF(lhs,rhs); } - } - public static class LEF extends LE { - public LEF(Node lhs, Node rhs) { super(lhs,rhs); } - @Override boolean doOp(double lhs, double rhs) { return lhs <= rhs; } - @Override public boolean isFloat() { return true; } - @Override Node copy(Node lhs, Node rhs) { return new LEF(lhs,rhs); } - } - - // Unsigned less that, for range checks. Not directly user writable. - public static class ULT extends BoolNode { - public ULT(Node lhs, Node rhs) { super(lhs,rhs); } - public String op() { return "u<" ; } - public String glabel() { return "u<"; } - TypeInteger doOp(TypeInteger i1, TypeInteger i2) { - if( Long.compareUnsigned(i1._max,i2._min) < 0 ) return TRUE; - if( Long.compareUnsigned(i1._min,i2._max) >= 0 ) return FALSE; - return BOOL; - } - Node copy(Node lhs, Node rhs) { return new ULT(lhs,rhs); } - } -} diff --git a/src/main/java/com/seaofnodes/simple/node/CFGNode.java b/src/main/java/com/seaofnodes/simple/node/CFGNode.java deleted file mode 100644 index b9c70e5f..00000000 --- a/src/main/java/com/seaofnodes/simple/node/CFGNode.java +++ /dev/null @@ -1,208 +0,0 @@ -package com.seaofnodes.simple.node; - -import com.seaofnodes.simple.Parser; -import com.seaofnodes.simple.codegen.CodeGen; -import com.seaofnodes.simple.type.*; -import com.seaofnodes.simple.util.Ary; -import com.seaofnodes.simple.util.Utils; -import java.util.BitSet; -import java.util.HashMap; -import java.util.HashSet; - -/** Control Flow Graph Nodes - *

- * CFG nodes have a immediate dominator depth (idepth) and a loop nesting - * depth(loop_depth). - *

- * idepth is computed lazily upon first request, and is valid even in the - * Parser, and is used by peepholes during parsing and afterward. - *

- * 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); - if( cfg != null ) { - _idepth = cfg._idepth; - _ltree = cfg._ltree; - _pre = cfg._pre; - } - } - - public CFGNode cfg(int idx) { return (CFGNode)in(idx); } - - // Block head is Start, Region, CProj, but not e.g. If, Return, Stop - public boolean blockHead() { return false; } - - // Get the one control following; error to call with more than one e.g. an - // IfNode or other multi-way branch. - public CFGNode uctrl() { - CFGNode c = null; - for( Node n : _outputs ) - if( n instanceof CFGNode cfg ) - { assert c==null; c = cfg; } - return c; - } - - // Used by the encoding / final BB layout - public CFGNode uctrlSkipEmpty() { - CFGNode x = this, y; - while( x.nOuts() == 1 && (y=x.uctrl())!=null && !(y instanceof CallNode) ) // Skip empty blocks - x = y; - return x; - } - - // ------------------------------------------------------------------------ - /** - * Immediate dominator tree depth, used to approximate a real IDOM during - * parsing where we do not have the whole program, and also peepholes - * change the CFG incrementally. - *

- * See {@link ...} - */ - public int _idepth; - public int idepth() { - return CodeGen.CODE.validIDepth(_idepth) ? _idepth : (_idepth=CodeGen.CODE.iDepthFrom(idom().idepth())); - } - - // Return the immediate dominator of this Node and compute dom tree depth. - public CFGNode idom(Node dep) { return cfg(0); } - public final CFGNode idom() { return idom(null); } - - // Return the LCA of two idoms - public CFGNode _idom(CFGNode rhs, Node dep) { - if( rhs==null ) return this; - CFGNode lhs = this; - while( lhs != rhs ) { - var comp = lhs.idepth() - rhs.idepth(); - if( comp >= 0 ) lhs = (dep==null ? lhs : dep.addDep(lhs)).idom(); - if( comp <= 0 ) rhs = (dep==null ? rhs : dep.addDep(rhs)).idom(); - if( lhs==null || rhs==null ) return null; - } - return lhs; - } - - // Anti-dependence field support - public int _anti; // Per-CFG field to help find anti-deps - - // 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; } - public int loopDepth() { return _ltree==null ? 0 : _ltree.depth(); } - - public LoopTree _ltree; - public int _pre; // Pre-order numbers for loop tree finding - static class LoopTree { - LoopTree _par; - final LoopNode _head; - int _depth; - LoopTree(LoopNode head) { _head = head; } - @Override public String toString() { return "LOOP"+_head._nid; } - int depth() { - return _depth==0 ? (_par==null ? 0 : (_depth = _par.depth()+1)) : _depth; - } - } - - // ------------------------------------------------------------------------ - // Tag all CFG Nodes with their containing LoopNode; LoopNodes themselves - // also refer to *their* containing LoopNode, as well as have their depth. - // Start is a LoopNode which contains all at depth 1. - public void buildLoopTree(StartNode start, StopNode stop) { - // Unlink all linked calls. This can remove RPC constants which - // shuffled the StartNode outputs so requires a while loop. - boolean done=false; - while(!done) { - done = true; - for( Node use : start._outputs ) - if( use instanceof FunNode fun ) - for( Node c : fun._inputs ) - if( c instanceof CallNode call ) - { call.unlink_all(); done=false; } - } - - _ltree = stop._ltree = Parser.XCTRL._ltree = new LoopTree((StartNode)this); - _bltWalk(2,null,stop, new BitSet()); - } - int _bltWalk( int pre, FunNode fun, StopNode stop, BitSet post ) { - // Pre-walked? - if( _pre!=0 ) return pre; - _pre = pre++; - // Pre-walk - for( Node use : _outputs ) - if( use instanceof CFGNode usecfg && !skip( usecfg ) ) - pre = usecfg._bltWalk( pre, use instanceof FunNode fuse ? fuse : fun, stop, post ); - - // Post-order work: find innermost loop - LoopTree inner = null, ltree; - for( Node use : _outputs ) { - if( !(use instanceof CFGNode usecfg) ) continue; - if( skip(usecfg) ) continue; - if( usecfg._type == Type.XCONTROL || // Do not walk dead control - usecfg._type == TypeTuple.IF_NEITHER ) // Nor dead IFs - continue; - // Child visited but not post-visited? - if( !post.get(usecfg._nid) ) { - // Must be a backedge to a LoopNode then - ltree = usecfg._ltree = new LoopTree((LoopNode)usecfg); - } else { - // Take child's loop choice, which must exist - ltree = usecfg._ltree; - // If falling into a loop, use the target loop's parent instead - if( ltree._head == usecfg ) { - if( ltree._par == null ) - // This loop never had an If test choose to take its - // exit, i.e. it is a no-exit infinite loop. - ltree._par = ltree._head.forceExit(fun,stop)._ltree; - ltree = ltree._par; - } - } - // Sort inner loops. The decision point is some branch far removed - // from either loop head OR either backedge so requires pre-order - // numbers to figure out innermost. - if( inner == null ) { inner = ltree; continue; } - if( inner == ltree ) continue; // No change - LoopTree outer = ltree._head._pre > inner._head._pre ? inner : ltree; - inner = ltree._head._pre > inner._head._pre ? ltree : inner; - inner._par = outer; - } - // Set selected loop - if( inner!=null ) - _ltree = inner; - // Tag as post-walked - post.set(_nid); - return pre; - } - - 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); - } - - - public String label( CFGNode target ) { - return (target instanceof LoopNode ? "LOOP" : "L")+target._nid; - } -} diff --git a/src/main/java/com/seaofnodes/simple/node/CProjNode.java b/src/main/java/com/seaofnodes/simple/node/CProjNode.java deleted file mode 100644 index a6e21ea0..00000000 --- a/src/main/java/com/seaofnodes/simple/node/CProjNode.java +++ /dev/null @@ -1,68 +0,0 @@ -package com.seaofnodes.simple.node; - -import com.seaofnodes.simple.Parser; -import com.seaofnodes.simple.type.Type; -import com.seaofnodes.simple.type.TypeMem; -import com.seaofnodes.simple.type.TypeTuple; -import com.seaofnodes.simple.util.Utils; -import java.util.BitSet; - -public class CProjNode extends CFGNode { - - // Which slice of the incoming multipart value - public int _idx; - - // Debugging label - public String _label; - - public CProjNode(Node ctrl, int idx, String label) { - super(ctrl); - _idx = idx; - _label = label; - } - public CProjNode(CProjNode c) { super(c); _idx = c._idx; _label = c._label; } - - @Override public String label() { return _label; } - - @Override public StringBuilder _print1(StringBuilder sb, BitSet visited) { return sb.append(_label); } - - @Override public boolean blockHead() { return true; } - - public CFGNode ctrl() { return cfg(0); } - - @Override - public Type compute() { - Type t = ctrl()._type; - return t instanceof TypeTuple tt ? tt._types[_idx] : Type.BOTTOM; - } - - @Override - public Node idealize() { - if( ctrl()._type instanceof TypeTuple tt ) { - if( tt._types[_idx]==Type.XCONTROL ) - return Parser.XCTRL; // We are dead - if( ctrl() instanceof IfNode && tt._types[1-_idx]==Type.XCONTROL ) // Only true for IfNodes - return ctrl().in(0); // We become our input control - } - - // Flip a negating if-test, to remove the not - if( ctrl() instanceof IfNode iff && addDep(iff.pred()) instanceof NotNode not ) - return new CProjNode(new IfNode(iff.ctrl(),not.in(1)).peephole(),1-_idx,_idx==0 ? "False" : "True"); - - // Copy of some other input - return ((MultiNode)ctrl()).pcopy(_idx); - } - - // Only called during basic-block layout, inverts a T/F CProj - public void invert() { - _label = _idx == 0 ? "False" : "True"; - _idx = 1-_idx; - } - - @Override - public boolean eq( Node n ) { return _idx == ((CProjNode)n)._idx; } - - @Override - int hash() { return _idx; } - -} diff --git a/src/main/java/com/seaofnodes/simple/node/CallEndMach.java b/src/main/java/com/seaofnodes/simple/node/CallEndMach.java deleted file mode 100644 index d26ea33b..00000000 --- a/src/main/java/com/seaofnodes/simple/node/CallEndMach.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.seaofnodes.simple.node; - -import com.seaofnodes.simple.codegen.*; -import com.seaofnodes.simple.util.SB; - -public class CallEndMach extends CallEndNode implements MachNode { - public CallEndMach( CallEndNode n ) { super(n); } - // MachNode specifics, shared across all CPUs - public int _xslot; - private RegMask _retMask; - private RegMask _kills; - public void cacheRegs(CodeGen code) { - // Return mask depends on TFP (either GPR or FPR) - _retMask = code._mach.retMask(call().tfp()); - // Kill mask is all caller-saves, and any mirror stack slots for args - // in registers. - RegMaskRW kills = code._callerSave.copy(); - // Start of stack slots - int maxReg = code._mach.regs().length; - // Incoming function arg slots, all low numbered in the RA - int fslot = fun()._maxArgSlot; - // Killed slots for this calls outgoing args - int xslot = code._mach.maxArgSlot(call().tfp()); - _xslot = (maxReg+fslot)+xslot; - for( int i=0; i1 ) { - 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); - } - - @Override - public Node idealize() { - - // Trivial inlining: call site calls a single function; single function - // is only called by this call site. - if( !_folding && nIns()==2 && in(0) instanceof CallNode call ) { - Node fptr = call.fptr(); - if( fptr.nOuts() == 1 && // Only user is this call - fptr instanceof ConstantNode && // We have an immediate call - // Function is being called, and its not-null - fptr._type instanceof TypeFunPtr tfp && tfp.notNull() && - // Arguments are correct - call.err()==null ) { - ReturnNode ret = (ReturnNode)in(1); - FunNode fun = ret.fun(); - // Expecting Start, and the Call - if( fun.nIns()==3 ) { - assert fun.in(1) instanceof StartNode && fun.in(2)==call; - // Disallow self-recursive inlining (loop unrolling by another name) - CFGNode idom = call; - while( !(idom instanceof FunNode) && idom!=null ) - idom = idom.idom(); - // Inline? - if( idom != fun && idom != null ) { - // Trivial inline: rewrite - _folding = true; - // Rewrite Fun so the normal RegionNode ideal collapses - fun._folding = true; - fun.setDef(1,Parser.XCTRL); // No default/unknown StartNode caller - fun.setDef(2,call.ctrl()); // Bypass the Call; - fun.ret().setDef(3,null); // Return is folding also - CodeGen.CODE.addAll(fun._outputs); - // Inlining immediately blows all cache idepth fields past the inline point. - // Bump the global version number invalidating them en-masse. - CodeGen.CODE.invalidateIDepthCaches(); - return this; - } - } else { - addDep(fun); - } - } else { // Function ptr has multiple users (so maybe multiple call sites) - addDep(fptr); - } - } - - return null; - } - - @Override public Node pcopy(int idx) { - return _folding ? in(1).in(idx) : null; - } -} diff --git a/src/main/java/com/seaofnodes/simple/node/CallNode.java b/src/main/java/com/seaofnodes/simple/node/CallNode.java deleted file mode 100644 index ba08c87a..00000000 --- a/src/main/java/com/seaofnodes/simple/node/CallNode.java +++ /dev/null @@ -1,176 +0,0 @@ -package com.seaofnodes.simple.node; - -import com.seaofnodes.simple.IterPeeps; -import com.seaofnodes.simple.Parser; -import com.seaofnodes.simple.codegen.CodeGen; -import com.seaofnodes.simple.type.Type; -import com.seaofnodes.simple.type.TypeFunPtr; -import com.seaofnodes.simple.util.Utils; -import java.util.BitSet; - -/** - * Call - */ -public class CallNode extends CFGNode { - - // Source location for late reported errors - public final Parser.Lexer _loc; - - public CallNode(Parser.Lexer loc, Node... nodes) { super(nodes); _loc = loc; } - public CallNode(CallNode call) { super(call); _loc = call._loc; } - - @Override public String label() { return "Call"; } - - @Override public StringBuilder _print1(StringBuilder sb, BitSet visited) { - String fname = name(); - if( fname == null ) fptr()._print0(sb,visited); - else sb.append(fname); - sb.append("( "); - for( int i=2; i0 && out(0) instanceof CallEndNode cend ) { - assert _cend()==cend; - return cend; - } else { - assert _cend()==null; - return null; - } - } - private CallEndNode _cend() { - CallEndNode cend=null; - for( Node n : _outputs ) - if( n instanceof CallEndNode cend0 ) - { assert cend == null; cend = cend0; } - return cend; - } - - // Get the one control following; error to call with more than one e.g. an - // IfNode or other multi-way branch. - @Override public CFGNode uctrl() { return cend(); } - - - @Override - public Type compute() { - return ctrl()._type; - } - - @Override - public Node idealize() { - CallEndNode cend = cend(); - if( cend==null ) return null; // Still building - - // Link: call calls target function. Linking makes the target FunNode - // point to this Call, and all his Parms point to the call arguments; - // also the CallEnd points to the Return. - Node progress = null; - if( fptr()._type instanceof TypeFunPtr tfp && tfp.nargs() == nargs() ) { - // If fidxs is negative, then infinite unknown functions - long fidxs = tfp.fidxs(); - if( fidxs > 0 ) { - // Wipe out the return which matching in the linker table - // Walk the (63 max) bits and link - for( ; fidxs!=0; fidxs = TypeFunPtr.nextFIDX(fidxs) ) { - int fidx = Long.numberOfTrailingZeros(fidxs); - TypeFunPtr tfp0 = tfp.makeFrom(fidx); - FunNode fun = CodeGen.CODE.link(tfp0); - if( fun!=null && !fun._folding && !linked(fun) ) - progress = link(fun); - } - } - } - - return progress; - } - - // True if Fun is linked to this Call - boolean linked( FunNode fun ) { - for( Node n : fun._inputs ) - if( n == this ) - return true; - return false; - } - - - // Link so this calls fun - private Node link( FunNode fun ) { - assert !linked(fun); - fun.addDef(this); - for( Node use : fun._outputs ) - if( use instanceof ParmNode parm ) - 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()); - return this; - } - - // 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 ) - 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) ) - throw Utils.TODO(); - if( !tfp.notNull() ) - return Parser.error( "Might be null calling "+tfp, _loc); - if( nargs() != tfp.nargs() ) - return Parser.error( "Expecting "+tfp.nargs()+" arguments, but found "+nargs(), _loc); - - // Check for args - for( int i=0; i - * Constants have no semantic inputs. However, we set Start as an input to - * Constants to enable a forward graph walk. This edge carries no semantic - * meaning, and it is present solely to allow visitation. - *

- * The Constant's value is the value stored in it. - */ - -public class ConstantNode extends Node { - public final Type _con; - public ConstantNode( Type type ) { - super(new Node[]{CodeGen.CODE._start}); - _con = _type = type; - } - public ConstantNode( ConstantNode con ) { super(con); _con = con._type; } - - public static Node make( Type type ) { - if( type==Type. CONTROL ) return new CtrlNode(); - if( type==Type.XCONTROL ) return new XCtrlNode(); - return new ConstantNode(type); - } - - @Override public String label() { return "#"+_con; } - @Override public String glabel() { return "#"+_con.gprint(); } - @Override public String uniqueName() { return "Con_" + _nid; } - - @Override - public StringBuilder _print1(StringBuilder sb, BitSet visited) { - if( _con instanceof TypeFunPtr tfp && tfp.isConstant() ) { - FunNode fun = CodeGen.CODE.link(tfp); - if( fun!=null && fun._name != null ) - return sb.append("{ ").append(fun._name).append("}"); - } - return sb.append(_con==null ? "---" : _con.toString()); - } - - @Override public boolean isConst() { return true; } - @Override public Type compute() { return _con; } - @Override public Node idealize() { return null; } - @Override public boolean eq(Node n) { return _con==((ConstantNode)n)._con; } - @Override int hash() { return _con.hashCode(); } -} diff --git a/src/main/java/com/seaofnodes/simple/node/CtrlNode.java b/src/main/java/com/seaofnodes/simple/node/CtrlNode.java deleted file mode 100644 index eda7d684..00000000 --- a/src/main/java/com/seaofnodes/simple/node/CtrlNode.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.seaofnodes.simple.node; - -import com.seaofnodes.simple.codegen.CodeGen; -import com.seaofnodes.simple.Parser; -import com.seaofnodes.simple.type.Type; -import java.util.BitSet; - -public class CtrlNode extends CFGNode { - public CtrlNode() { super(CodeGen.CODE._start); } - @Override public String label() { return "Ctrl"; } - @Override public StringBuilder _print1(StringBuilder sb, BitSet visited) { return sb.append("Cctrl"); } - @Override public boolean isConst() { return true; } - @Override public Type compute() { return Type.CONTROL; } - @Override public Node idealize() { return null; } -} diff --git a/src/main/java/com/seaofnodes/simple/node/DivFNode.java b/src/main/java/com/seaofnodes/simple/node/DivFNode.java deleted file mode 100644 index 633f3224..00000000 --- a/src/main/java/com/seaofnodes/simple/node/DivFNode.java +++ /dev/null @@ -1,41 +0,0 @@ -package com.seaofnodes.simple.node; - -import com.seaofnodes.simple.type.Type; -import com.seaofnodes.simple.type.TypeFloat; - -import java.util.BitSet; - -public class DivFNode extends Node { - public DivFNode(Node lhs, Node rhs) { super(null, lhs, rhs); } - - @Override public String label() { return "DivF"; } - - @Override public String glabel() { return "/"; } - - @Override - public StringBuilder _print1(StringBuilder sb, BitSet visited) { - in(1)._print0(sb.append("("), visited); - in(2)._print0(sb.append("/"), visited); - return sb.append(")"); - } - - @Override - public Type compute() { - if (in(1)._type instanceof TypeFloat i0 && - in(2)._type instanceof TypeFloat i1) { - if (i0.isConstant() && i1.isConstant()) - return TypeFloat.constant(i0.value()/i1.value()); - } - return in(1)._type.meet(in(2)._type); - } - - @Override - public Node idealize() { - // 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; - } - @Override Node copy(Node lhs, Node rhs) { return new DivFNode(lhs,rhs); } -} diff --git a/src/main/java/com/seaofnodes/simple/node/DivNode.java b/src/main/java/com/seaofnodes/simple/node/DivNode.java deleted file mode 100644 index 0de1c946..00000000 --- a/src/main/java/com/seaofnodes/simple/node/DivNode.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.seaofnodes.simple.node; - -import com.seaofnodes.simple.type.Type; -import com.seaofnodes.simple.type.TypeInteger; -import java.util.BitSet; - -public class DivNode extends ArithNode { - public DivNode(Node lhs, Node rhs) { super(null, lhs, rhs); } - - @Override public String label() { return "Div"; } - @Override public String op() { return "//"; } - - @Override long doOp( long x, long y ) { return y==0 ? 0 : x / y; } - @Override TypeInteger doOp(TypeInteger x, TypeInteger y) { - return TypeInteger.BOT; - } - - @Override - public Node idealize() { - // Div of 1. - if( in(2)._type == TypeInteger.TRUE ) - return in(1); - return super.idealize(); - } - - @Override Node copy(Node lhs, Node rhs) { return new DivNode(lhs,rhs); } - @Override Node copyF() { return new DivFNode(null,null); } -} diff --git a/src/main/java/com/seaofnodes/simple/node/ExternNode.java b/src/main/java/com/seaofnodes/simple/node/ExternNode.java deleted file mode 100644 index a10bb84a..00000000 --- a/src/main/java/com/seaofnodes/simple/node/ExternNode.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.seaofnodes.simple.node; - -import com.seaofnodes.simple.Parser; -import com.seaofnodes.simple.codegen.CodeGen; -import com.seaofnodes.simple.type.Type; -import com.seaofnodes.simple.type.TypeFunPtr; -import com.seaofnodes.simple.util.SB; -import java.util.BitSet; - -/** - A constant with external linkage. - */ - -public class ExternNode extends ConstantNode { - public final String _extern; - public ExternNode(Type t, String ex) { super(t); _extern = ex; } - - @Override public String label() { return "#"+_con+":"+_extern; } - @Override public String glabel() { return _con.print(new SB().p("#"), new BitSet(), true).p(":").p(_extern).toString(); } - @Override public String uniqueName() { return "Extern_" + _nid; } - - @Override public boolean eq(Node n) { return this==n; } -} diff --git a/src/main/java/com/seaofnodes/simple/node/FRefNode.java b/src/main/java/com/seaofnodes/simple/node/FRefNode.java deleted file mode 100644 index 6093b8a6..00000000 --- a/src/main/java/com/seaofnodes/simple/node/FRefNode.java +++ /dev/null @@ -1,35 +0,0 @@ -package com.seaofnodes.simple.node; - -import com.seaofnodes.simple.Parser; -import com.seaofnodes.simple.Var; -import com.seaofnodes.simple.type.Type; -import java.util.BitSet; - -/** - * A Forward Reference. Its any final constant, including functions. When - * the Def finally appears its plugged into the forward reference, which then - * peepholes to the Def. - */ -public class FRefNode extends ConstantNode { - public static final Type FREF_TYPE = Type.BOTTOM; - public final Var _n; - public FRefNode( Var n ) { super(FREF_TYPE); _n = n; } - - @Override public String label() { return "FRef"+_n; } - - @Override public String uniqueName() { return "FRef_" + _nid; } - - @Override public StringBuilder _print1(StringBuilder sb, BitSet visited) { - return sb.append("FRef_").append(_n); - } - - @Override public Node idealize() { - // When FRef finds its definition, idealize to it - return nIns()==1 ? null : in(1); - } - - public Parser.ParseException err() { return Parser.error("Undefined name '"+_n._name+"'",_n._loc); } - - @Override public boolean eq(Node n) { return this==n; } - @Override int hash() { return _n._idx; } -} diff --git a/src/main/java/com/seaofnodes/simple/node/FunNode.java b/src/main/java/com/seaofnodes/simple/node/FunNode.java deleted file mode 100644 index 4bd0fd97..00000000 --- a/src/main/java/com/seaofnodes/simple/node/FunNode.java +++ /dev/null @@ -1,227 +0,0 @@ -package com.seaofnodes.simple.node; - -import com.seaofnodes.simple.Parser; -import com.seaofnodes.simple.codegen.CodeGen; -import com.seaofnodes.simple.codegen.Encoding; -import com.seaofnodes.simple.codegen.RegMask; -import com.seaofnodes.simple.type.Type; -import com.seaofnodes.simple.type.TypeFunPtr; -import com.seaofnodes.simple.type.TypeTuple; -import com.seaofnodes.simple.util.SB; -import com.seaofnodes.simple.util.Utils; -import java.util.BitSet; -import static com.seaofnodes.simple.codegen.CodeGen.CODE; - -public class FunNode extends RegionNode { - - // When set true, this Call/CallEnd/Fun/Return is being trivially inlined - boolean _folding; - - private TypeFunPtr _sig; // Initial signature - private ReturnNode _ret; // Return pointer - - public String _name; // Debug name - - public FunNode( Parser.Lexer loc, TypeFunPtr sig, String name, Node... nodes ) { super(loc,nodes); _name=name; _sig = sig; } - 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"+_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. - @Override public CFGNode uctrl() { - for( Node n : _outputs ) - if( n instanceof CFGNode cfg && - (cfg instanceof RegionNode || cfg.cfg0()==this) ) - return cfg; - return null; - } - - public ParmNode rpc() { - ParmNode rpc = null; - for( Node n : _outputs ) - if( n instanceof ParmNode parm && parm._idx==0 ) - { assert rpc==null; rpc=parm; } - return rpc; - } - - // 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; } - public ReturnNode ret() { assert _ret!=null; return _ret; } - - // Signature can improve over time - public TypeFunPtr sig() { return _sig; } - public void setSig( TypeFunPtr sig ) { - assert sig.isa(_sig); - if( _sig != sig ) { - CODE.add(this); - _sig = sig; - } - } - - public void setName( String name ) { - if( _name==null ) _name=name; - else _name += "."+name; - } - - @Override - public Type compute() { - // Only dead if no callers after SCCP - if( unknownCallers() ) - return Type.CONTROL; - return super.compute(); - } - - @Override - public Node idealize() { - - // Some linked path dies - Node progress = deadPath(); - if( progress!=null ) { - if( nIns()==3 && in(2) instanceof CallNode call ) - CODE.add(call.cend()); // If Start and one call, check for inline - return progress; - } - - // 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. - // This in turn is valid arguments to every callable function. - // - // In a total compilation, we can start from Start and keep things - // more contained. - - // If no default/unknown caller, use the normal RegionNode ideal rules - // to collapse - if( unknownCallers() ) return null; - - // If down to a single input, become that input - if( nIns()==2 && !hasPhi() ) { - CODE.add( CODE._stop ); // Stop will remove dead path - CODE.add( _ret ); // Return will compute to TOP control - return in(1); // Collapse if no Phis; 1-input Phis will collapse on their own - } - - return null; - } - - // Bypass Region idom, always assume depth == 1, one more than Start, - // unless folding then just a ID on input#1 - @Override public int idepth() { - return _folding ? super.idepth() : CodeGen.CODE.iDepthAt(1); - } - // Bypass Region idom, always assume idom is Start - @Override public CFGNode idom(Node dep) { return _folding && nIns()==3 ? cfg(2) : (nIns()>1 ? cfg(1) : null); } - - // Always in-progress until we run out of unknown callers - public boolean unknownCallers() { return nIns()>=2 && in(1) instanceof StartNode; } - - @Override public boolean inProgress() { return unknownCallers(); } - - // Add a new function exit point. - public void addReturn(Node ctrl, Node mem, Node rez) { _ret.addReturn(ctrl,mem,rez); } - - // Build the function body - public BitSet body() { - - // Reverse up (stop to start) CFG only, collect bitmap. - BitSet cfgs = new BitSet(); - cfgs.set(_nid); - walkUp(ret(),cfgs ); - - // Top down (start to stop) all flavors. CFG limit to bitmap. - // If data use bottoms out in wrong CFG, returns false - but tries all outputs. - // If any output hits an in-CFG use (e.g. phi), then keep node. - BitSet body = new BitSet(); - walkDown(this, cfgs, body, new BitSet()); - return body; - } - - private static void walkUp(CFGNode n, BitSet cfgs) { - if( cfgs.get(n._nid) ) return; - cfgs.set(n._nid); - if( n instanceof RegionNode r ) - for( int i=1; i>3) - : stackSlot - _maxArgSlot; - return slotRotate*8; - } - -} diff --git a/src/main/java/com/seaofnodes/simple/node/IfNode.java b/src/main/java/com/seaofnodes/simple/node/IfNode.java deleted file mode 100644 index c5d687e5..00000000 --- a/src/main/java/com/seaofnodes/simple/node/IfNode.java +++ /dev/null @@ -1,97 +0,0 @@ -package com.seaofnodes.simple.node; - -import com.seaofnodes.simple.codegen.CodeGen; -import com.seaofnodes.simple.type.*; -import com.seaofnodes.simple.util.Utils; -import java.util.BitSet; - -public class IfNode extends CFGNode implements MultiNode { - - public IfNode(Node ctrl, Node pred) { - super(ctrl, pred); - CodeGen.CODE.add(this); // Because idoms are complex, just add it - } - public IfNode(IfNode iff) { super(iff); } - - @Override - public String label() { return "If"; } - - @Override - public StringBuilder _print1(StringBuilder sb, BitSet visited) { - sb.append("if( "); - if( in(1)==null ) sb.append("never"); - else in(1)._print0(sb, visited); - return sb.append(" )"); - } - - public Node ctrl() { return in(0); } - public Node pred() { return in(1); } - - // No one unique control follows - @Override public CFGNode uctrl() { return null; } - - @Override - public Type compute() { - // If the If node is not reachable then neither is any following Proj - if (ctrl()._type != Type.CONTROL && ctrl()._type != Type.BOTTOM ) - return TypeTuple.IF_NEITHER; - Node pred = pred(); - Type t = pred._type; - // High types mean NEITHER side is reachable. - // Wait until the type falls to decide which way to go. - if( t.isHigh() ) - return TypeTuple.IF_NEITHER; - // If constant is 0 then false branch is reachable - // Else true branch is reachable - if( t.isConstant() ) - return (t==Type.NIL || t==TypeInteger.ZERO || (t instanceof TypeFunPtr tfp && tfp._fidxs==0) ) ? TypeTuple.IF_FALSE : TypeTuple.IF_TRUE; - // If adding a zero makes a difference, the predicate must not have a zero/null - if( !t.makeZero().isa(t) ) - return TypeTuple.IF_TRUE; - - return TypeTuple.IF_BOTH; - } - - @Override - public Node idealize() { - // Hunt up the immediate dominator tree. If we find an identical if - // test on either the true or false branch, that side wins. - if( !pred()._type.isHighOrConst() ) - for( CFGNode dom = idom(), prior=this; dom!=null; prior = dom, dom = dom.idom() ) - if( addDep(dom) instanceof IfNode iff && addDep(iff.pred())==pred() && prior instanceof CProjNode prj ) { - setDef(1,con( prj._idx==0 ? 1 : 0 )); - return this; - } - return null; - } - - // MachNode variants need to support this and negate the conditional test. - // The following CProjs will be inverted by the caller. - public void negate() { throw Utils.TODO(); } - - // Negate the sense of a test - public static String negate( String bop ) { - return switch( bop ) { - case "<" -> ">="; - case "<=" -> ">" ; - case "==" -> "!="; - case "!=" -> "=="; - case ">" -> "<="; - case ">=" -> "<" ; - default -> throw Utils.TODO(); - }; - } - - // Swap compare operands - public static String swap( String bop ) { - return switch( bop ) { - case "<" -> ">" ; - case "<=" -> ">="; - case "==" -> "=="; - case "!=" -> "!="; - case ">" -> "<" ; - case ">=" -> "<="; - default -> throw Utils.TODO(); - }; - } -} diff --git a/src/main/java/com/seaofnodes/simple/node/LoadNode.java b/src/main/java/com/seaofnodes/simple/node/LoadNode.java deleted file mode 100644 index b58ea498..00000000 --- a/src/main/java/com/seaofnodes/simple/node/LoadNode.java +++ /dev/null @@ -1,233 +0,0 @@ -package com.seaofnodes.simple.node; - -import com.seaofnodes.simple.Parser; -import com.seaofnodes.simple.codegen.CodeGen; -import com.seaofnodes.simple.type.*; -import com.seaofnodes.simple.util.Utils; -import java.util.BitSet; - -/** - * Load represents extracting a value from inside a memory object, - * in chapter 10 this means Struct fields. - */ -public class LoadNode extends MemOpNode { - - /** - * Load a value from a ptr.field. - * - * @param name The field we are loading - * @param mem The memory alias node - this is updated after a Store - * @param ptr The ptr to the struct base from where we load a field - * @param off The offset inside the struct base - */ - public LoadNode(Parser.Lexer loc, String name, int alias, Type glb, Node mem, Node ptr, Node off) { - super(loc, name, alias, true, glb, mem, ptr, off); - } - - // GraphVis DOT code (must be valid Java identifiers) and debugger labels - @Override public String label() { return "ld_"+mlabel(); } - // GraphVis node-internal labels - @Override public String glabel() { return "." +_name; } - - @Override - public StringBuilder _print1(StringBuilder sb, BitSet visited) { return sb.append(".").append(_name); } - - @Override - public Type compute() { - if( !(mem()._type instanceof TypeMem mem) ) - return _declaredType; // No memory yet? Declared type - assert !_declaredType.isFRef(); - // No lifting if ptr might null-check - if( err()!=null || !(ptr()._type instanceof TypeMemPtr tmp) ) - return _declaredType; // No pointer yet? Declared type - Type t = tmp._obj.field(_name)._t; - if( t instanceof TypeConAry ary ) { - t = ary.elem(); // TODO: if offset is known, can peek the constant - } - // Lift from declared type and memory input - t = t.join(_declaredType).join(mem._t); - if( _declaredType.isFinal() ) - t = t.makeRO(); // Deep final applied - return t; - } - - @Override - public Node idealize() { - Node ptr = ptr(); - Node mem = mem(); - - if( mem instanceof CastNode cast ) { - setDef(1,cast.in(1)); - return this; - } - - // Simple Load-after-Store on same address. - if( mem instanceof StoreNode st && - ptr == st.ptr() && off() == st.off() ) { // Must check same object - assert _name.equals(st._name); // Equiv class aliasing is perfect - return extend(st.val()); - } - - // Simple load-after-MemMerge to a known alias can bypass. Happens when inlining. - if( mem instanceof MemMergeNode mem2 ) { - Node memA = mem2.alias(_alias); - for( Node ld : memA._outputs ) - if( ld instanceof LoadNode ) - CodeGen.CODE.add(ld); - setDef(1,memA); - return this; - } - - // Simple Load-after-New on same address. - if( mem instanceof ProjNode p && p.in(0) instanceof NewNode nnn && - ptr == nnn.proj(1) ) // Must check same object - return zero(nnn); // Load zero from new - - // Uplift control to a prior dominating load. - for( Node memuse : mem._outputs ) - // Find a prior load, has same mem,ptr,off but higher ctrl - if( memuse != this && memuse instanceof LoadNode ld && ptr==ld.ptr() && off()==ld.off() && - cfg0()!=null && cfg0()._idom(ld.cfg0(),this) == ld.cfg0() ) // Higher control means load is legal earlier - return ld; - - // Load-after-Store on same address, but bypassing provably unrelated - // stores. This is a more complex superset of the above two peeps. - // "Provably unrelated" is really weak. - if( ptr instanceof ReadOnlyNode ro ) - ptr = ro.in(1); - outer: - while( true ) { - switch( mem ) { - case StoreNode st: - if( ptr == addDep(st.ptr()) && off() == st.off() ) - return extend(castRO(st.val())); // Proved equal - // Can we prove unequal? Offsets do not overlap? - if( !off()._type.join(st.off()._type).isHigh() && // Offsets overlap - !neverAlias(ptr,st.ptr()) ) // And might alias - break outer; // Cannot tell, stop trying - // Pointers cannot overlap - mem = st.mem(); // Proved never equal - break; - case PhiNode phi: - // Assume related - addDep(phi); - break outer; - case ConstantNode top: break outer; // Assume shortly dead - case ProjNode mproj: // Memory projection - switch( mproj.in(0) ) { - case NewNode nnn1: - if( ptr instanceof ProjNode pproj && pproj.in(0) == mproj.in(0) ) - return zero(nnn1); - if( !(ptr instanceof ProjNode pproj && pproj.in(0) instanceof NewNode) ) - break outer; // Cannot tell, ptr not related to New - mem = nnn1.in(nnn1.findAlias(_alias));// Bypass unrelated New - break; - case StartNode start: break outer; - case CallEndNode cend: break outer; // TODO: Bypass no-alias call - default: throw Utils.TODO(); - } - break; - case CastNode cast: mem = cast.in(1); break; - case MemMergeNode merge: mem = merge.alias(_alias); break; - - default: - throw Utils.TODO(); - } - } - - // Push a Load up through a Phi, as long as it collapses on at least - // one arm. If at a Loop, the backedge MUST collapse - else we risk - // spinning the same transform around the loop indefinitely. - // BEFORE (2 Sts, 1 Ld): AFTER (1 St, 0 Ld): - // if( pred ) ptr.x = e0; val = pred ? e0 - // else ptr.x = e1; : e1; - // val = ptr.x; ptr.x = val; - if( mem() instanceof PhiNode memphi && memphi.region()._type == Type.CONTROL && memphi.nIns()== 3 && - // Offset can be hoisted - off() instanceof ConstantNode && - // Pointer can be hoisted - hoistPtr(ptr,memphi) ) { - - // Profit on RHS/Loop backedge - if( profit(memphi,2) || - // Else must not be a loop to count profit on LHS. - (!(memphi.region() instanceof LoopNode) && profit(memphi,1)) ) { - Node ld1 = ld(1); - Node ld2 = ld(2); - return new PhiNode(_name,_declaredType,memphi.region(),ld1,ld2); - } - } - - return null; - } - - // Load a flavored zero from a New - private Node zero(NewNode nnn) { - TypeStruct ts = nnn._ptr._obj; - Type zero = ts._fields[ts.findAlias(_alias)]._t.makeZero(); - return castRO(new ConstantNode(zero).peephole()); - } - - private Node ld( int idx ) { - Node mem = mem(), ptr = ptr(); - return new LoadNode(_loc,_name,_alias,_declaredType,mem.in(idx),ptr instanceof PhiNode && ptr.in(0)==mem.in(0) ? ptr.in(idx) : ptr, off()).peephole(); - } - - private static boolean neverAlias( Node ptr1, Node ptr2 ) { - return ptr1.in(0) != ptr2.in(0) && - // Unrelated allocations - ptr1 instanceof ProjNode && ptr1.in(0) instanceof NewNode && - ptr2 instanceof ProjNode && ptr2.in(0) instanceof NewNode; - } - - private static boolean hoistPtr(Node ptr, PhiNode memphi ) { - // Can I hoist ptr above the Region? - if( !(memphi.region() instanceof RegionNode r) ) - return false; // Dead or dying Region/Phi - // If ptr from same Region, then yes, just use hoisted split pointers - if( ptr instanceof PhiNode pphi && pphi.region() == r ) - return true; - - // No, so can we lift this ptr? - CFGNode cptr = ptr.cfg0(); - if( cptr != null ) - // Pointer is controlled high - // TODO: Really needs to be the LCA of all inputs is high - return cptr.idepth() <= r.idepth(); - - // Dunno without a longer walk - return false; - } - - // Profitable if we find a matching Store on this Phi arm. - private boolean profit(PhiNode phi, int idx) { - Node px = phi.in(idx); - if( px==null ) return false; - if( px._type instanceof TypeMem mem && mem._t.isHighOrConst() ) return true; - if( px instanceof StoreNode st1 && ptr()==addDep(st1.ptr() )&& off()==st1.off() ) return true; - addDep(px); - return false; - } - - // Read-Only is a deep property, and cannot be cast-away - private Node castRO(Node rez) { - if( ptr()._type.isFinal() && !rez._type.isFinal() ) - return new ReadOnlyNode(rez).peephole(); - return rez; - } - - // When a load bypasses a store, the store might truncate bits - and the - // load will need to zero/sign-extend. - private Node extend(Node val) { - if( !(_declaredType instanceof TypeInteger ti) ) return val; - if( ti._min==0 ) // Unsigned - return new AndNode(null,val,con(ti._max)); - // Signed extension - int shift = Long.numberOfLeadingZeros(ti._max)-1; - 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/LoopNode.java b/src/main/java/com/seaofnodes/simple/node/LoopNode.java deleted file mode 100644 index fc50e17f..00000000 --- a/src/main/java/com/seaofnodes/simple/node/LoopNode.java +++ /dev/null @@ -1,105 +0,0 @@ -package com.seaofnodes.simple.node; - -import com.seaofnodes.simple.Parser; -import com.seaofnodes.simple.codegen.CodeGen; -import com.seaofnodes.simple.type.Type; -import com.seaofnodes.simple.type.TypeInteger; -import com.seaofnodes.simple.type.TypeMem; -import com.seaofnodes.simple.util.Utils; -import java.util.BitSet; -import java.util.HashSet; - -public class LoopNode extends RegionNode { - public LoopNode( Parser.Lexer loc, Node entry ) { super(loc,null,entry,null); } - public LoopNode( LoopNode loop ) { super(loop); } - - public CFGNode entry() { return cfg(1); } - public CFGNode back () { return cfg(2); } - - @Override - public String label() { return "Loop"; } - - @Override - public Type compute() { - if( inProgress() ) return Type.CONTROL; - return entry()._type; - } - - // Bypass Region idom, same as the default idom() using use in(1) instead of in(0) - @Override public int idepth() { - return CodeGen.CODE.validIDepth(_idepth) ? _idepth : (_idepth=CodeGen.CODE.iDepthFrom(idom().idepth())); - } - // Bypass Region idom, same as the default idom() using use in(1) instead of in(0) - @Override public CFGNode idom(Node dep) { return entry(); } - - // If this is an unreachable loop, it may not have an exit. If it does not - // (i.e., infinite loop), force an exit to make it reachable. - public StopNode forceExit( FunNode fun, StopNode stop ) { - // Walk the backedge, then immediate dominator tree util we hit this - // Loop again. If we ever hit a CProj from an If (as opposed to - // directly on the If) we found our exit. - CFGNode x = back(); - while( x != this ) { - if( x instanceof CProjNode exit && exit.in(0) instanceof IfNode iff ) { - CFGNode other = iff.cproj(1-exit._idx); - if( other!=null && other._ltree != _ltree && nested(_ltree,other._ltree) ) - return stop; // Found an exit, not an infinite loop - } - x = x.idom(); - } - // Found a no-exit loop. Insert an exit - NeverNode iff = (NeverNode)new NeverNode(back()).peephole(); // Ideal never-branch - CProjNode t = new CProjNode(iff,0,"True" ).init(); - CProjNode f = new CProjNode(iff,1,"False").init(); - setDef(2,t); // True continues loop, False (never) exits loop - ReturnNode ret = fun.ret(); - iff._ltree = t._ltree = _ltree; - ret._ltree = f._ltree = stop._ltree; - - // Now fold control into the exit. Might have 1 valid exit, or an - // XCtrl or a bunch of prior NeverNode exits. - Node top = new ConstantNode(Type.TOP).peephole(); - Node memout = new MemMergeNode(false); - memout.addDef(f); // placeholder for control - for( Node u : _outputs ) - if( u instanceof PhiNode phi && phi._type.isa(TypeMem.BOT) ) - memout.addDef(phi); - - Node ctrl = ret.ctrl(), mem = ret.mem(), expr = ret.expr(); - if( ctrl!=null && ctrl._type != Type.XCONTROL ) { - // Perfect aligned exit? - if( !(ctrl instanceof RegionNode r && - mem instanceof PhiNode pmem && pmem.region()==r && - expr instanceof PhiNode prez && prez.region()==r ) ) { - // Nope, insert an aligned exit layer - RegionNode r = new RegionNode(_loc,null,ctrl).init(); - ctrl = r; r._ltree = stop._ltree; - mem = new PhiNode(r,mem ).init(); - expr = new PhiNode(r,expr).init(); - } - // Append new Never exit - ctrl.keep().addDef(f ); - mem .keep().addDef(memout); - expr.keep().addDef(top); - } else { - ctrl = f .keep(); - mem = memout.keep(); - expr = top .keep(); - } - ret.setDef(0,ctrl.unkeep()); - ret.setDef(1,mem .unkeep()); - ret.setDef(2,expr.unkeep()); - ret._type = null; // This may LIFT the Return type, from [XCtrl,mem,expr] to [Ctrl,mem,expr] - ret.setType(ret.compute()); - - return stop; - } - - private static boolean nested(LoopTree inner, LoopTree outer) { - for( ; inner!=null; inner = inner._par ) - if( inner == outer ) - return true; - return false; - } - -} diff --git a/src/main/java/com/seaofnodes/simple/node/MachConcreteNode.java b/src/main/java/com/seaofnodes/simple/node/MachConcreteNode.java deleted file mode 100644 index 9440a7ea..00000000 --- a/src/main/java/com/seaofnodes/simple/node/MachConcreteNode.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.seaofnodes.simple.node; - -import com.seaofnodes.simple.type.Type; -import com.seaofnodes.simple.util.Utils; -import java.io.ByteArrayOutputStream; -import java.util.BitSet; - -// Generic machine-specific class, has a few Node implementations that have to -// exist (abstract) but are not useful past the optimizer. -public abstract class MachConcreteNode extends Node implements MachNode { - - public MachConcreteNode(Node node) { super(node); } - public MachConcreteNode(Node[]nodes) { super(nodes); } - - @Override public String label() { return op(); } - @Override public Type compute () { throw Utils.TODO(); } - @Override public Node idealize() { throw Utils.TODO(); } - - @Override public StringBuilder _print1(StringBuilder sb, BitSet visited) { - sb.append("(").append(op()).append(","); - for( int i=1; i=nIns() || in(i)==null ) - setDefX(i,mem.in(i)); - } - } - setDef(1,mem.in(1)); - return this; - } - - return progress ? this : null; - } - - - public Node in( Var v ) { return in(v._idx); } - - public Node alias( int alias ) { - return alias < nIns() && in(alias)!=null ? in(alias) : in(1); - } - - Node alias( int alias, Node st ) { - while( alias >= nIns() ) addDef(null); - return setDef(alias,st); - } - - // Read or update from memory. - // A shared implementation allows us to create lazy phis both during - // lookups and updates; the lazy phi creation is part of chapter 8. - Node _mem( int alias, Node st ) { - // Memory projections are made lazily; if one does not exist - // then it must be START.proj(1) - Node old = alias(alias); - if( old instanceof ScopeNode loop ) { - MemMergeNode loopmem = loop.mem(); - Node memdef = loopmem.alias(alias); - // Lazy phi! - old = memdef instanceof PhiNode phi && loop.ctrl()==phi.region() - // Loop already has a real Phi, use it - ? memdef - // Set real Phi in the loop head - // The phi takes its one input (no backedge yet) from a recursive - // lookup, which might have insert a Phi in every loop nest. - : loopmem.alias(alias, new PhiNode(Parser.memName(alias), TypeMem.make(alias,Type.BOTTOM),loop.ctrl(),loopmem._mem(alias,null),null).peephole() ); - alias(alias,old); - } - // Memory projections are made lazily; expand as needed - return st==null ? old : alias(alias,st); // Not lazy, so this is the answer - } - - - void _merge( MemMergeNode that, RegionNode r) { - int len = Math.max(nIns(),that.nIns()); - for( int i = 1; i < len; i++) - if( alias(i) != that.alias(i) ) { // No need for redundant Phis - // If we are in lazy phi mode we need to a lookup - // by alias as it will trigger a phi creation - Node lhs = this._mem(i,null); - Node rhs = that._mem(i,null); - alias(i, new PhiNode(Parser.memName(i), lhs._type.meet(rhs._type).glb(true), r, lhs, rhs).peephole()); - } - } - - // Fill in the backedge of any inserted Phis - void _endLoopMem( ScopeNode scope, MemMergeNode back, MemMergeNode exit ) { - Node exit_def = exit.alias(1); - for( int i=1; i outs(); - - // Find a projection by index - default ProjNode proj( int idx ) { - for( Node out : outs() ) - if( out instanceof ProjNode prj && prj._idx==idx ) - return prj; - return null; - } - - // Find a projection by index - default CProjNode cproj( int idx ) { - for( Node out : outs() ) - if( out instanceof CProjNode prj && prj._idx==idx ) - return prj; - return null; - } - - // Return not-null if this projection index is a ideal copy. - // Called by ProjNode ideal and used to collapse Multis. - default Node pcopy(int idx) { return null; } -} diff --git a/src/main/java/com/seaofnodes/simple/node/NeverNode.java b/src/main/java/com/seaofnodes/simple/node/NeverNode.java deleted file mode 100644 index 516f218b..00000000 --- a/src/main/java/com/seaofnodes/simple/node/NeverNode.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.seaofnodes.simple.node; - -import com.seaofnodes.simple.Parser; -import com.seaofnodes.simple.type.TypeInteger; -import com.seaofnodes.simple.type.Type; -import com.seaofnodes.simple.type.TypeTuple; - -import java.util.BitSet; - -// "Never true" for infinite loop exits -public class NeverNode extends IfNode { - public NeverNode(NeverNode ctrl) { super(ctrl); } - public NeverNode(Node ctrl) { super(ctrl,null); } - - @Override public String label() { return "Never"; } - - @Override public StringBuilder _print1(StringBuilder sb, BitSet visited) { return sb.append("Never"); } - - @Override public Type compute() { return TypeTuple.IF_BOTH; } - - @Override public Node idealize() { return null; } -} diff --git a/src/main/java/com/seaofnodes/simple/node/NewNode.java b/src/main/java/com/seaofnodes/simple/node/NewNode.java deleted file mode 100644 index 1785af4e..00000000 --- a/src/main/java/com/seaofnodes/simple/node/NewNode.java +++ /dev/null @@ -1,131 +0,0 @@ -package com.seaofnodes.simple.node; - -import com.seaofnodes.simple.codegen.*; -import com.seaofnodes.simple.type.*; -import com.seaofnodes.simple.util.SB; - -import java.util.BitSet; - -/** - * Allocation! Allocate a chunk of memory, and pre-zero it. - * The inputs include control and size, and ALL aliases being set. - * The output is large tuple, one for every alias plus the created pointer. - * New is expected to be followed by projections for every alias. - */ -public class NewNode extends Node implements MultiNode { - - public final TypeMemPtr _ptr; - public final int _len; - - public NewNode(TypeMemPtr ptr, Node... nodes) { - super(nodes); - assert !ptr.nullable(); - _ptr = ptr; - _len = ptr._obj._fields.length; - // Control in slot 0 - assert nodes[0]._type==Type.CONTROL || nodes[0]._type == Type.XCONTROL; - // Malloc-length in slot 1 - assert nodes[1]._type instanceof TypeInteger || nodes[1]._type==Type.NIL; - for( int i=0; i<_len; i++ ) - assert ptr._obj._fields[i]._one || nodes[2 + i]._type.isa( TypeMem.BOT ); - } - - public NewNode(NewNode nnn) { super(nnn); _ptr = nnn._ptr; _len = nnn._len; } - - public Node mem (int idx) { return in(idx+2); } - - @Override public String label() { return "new_"+glabel(); } - @Override public String glabel() { - return _ptr._obj.isAry() ? "ary_"+_ptr._obj._fields[1]._t.str() : _ptr._obj.str(); - } - - @Override - public StringBuilder _print1(StringBuilder sb, BitSet visited) { - sb.append("new "); - return sb.append(_ptr._obj.str()); - } - - int findAlias(int alias) { - int idx = _ptr._obj.findAlias(alias); - assert idx!= -1; // Error, caller should be calling - return idx+2; // Skip ctrl, size - } - - // 0 - ctrl - // 1 - byte size - // 2-len+2 - aliases, one per field - // len+2 - 2*len+2 - initial values, one per field - public Node size() { return in(1); } - - @Override - public TypeTuple compute() { - Field[] fs = _ptr._obj._fields; - Type[] ts = new Type[fs.length+2]; - ts[0] = Type.CONTROL; - ts[1] = _ptr; - for( int i=0; i - * Generally fixed length, ordered, nulls allowed, no unused trailing space. - * Ordering is required because e.g. "a/b" is different from "b/a". - * The first input (offset 0) is often a {@link CFGNode} node. - */ - public Ary _inputs; - - /** - * Outputs reference Nodes that are not null and have this Node as an - * input. These nodes are users of this node, thus these are def-use - * references to Nodes. - *

- * Outputs directly match inputs, making a directed graph that can be - * walked in either direction. These outputs are typically used for - * efficient optimizations but otherwise have no semantics meaning. - */ - public Ary _outputs; - - - /** - * Current computed type for this Node. This value changes as the graph - * changes and more knowledge is gained about the program. - */ - public Type _type; - - - Node(Node... inputs) { - _nid = CODE.getUID(); // allocate unique dense ID - _inputs = new Ary<>(Node.class); - Collections.addAll(_inputs,inputs); - _outputs = new Ary<>(Node.class); - for( Node n : _inputs ) - if( n != null ) - n.addUse( this ); - } - - // Make a Node using the existing arrays of nodes. - // Used by any pass rewriting all Node classes but not the edges. - Node( Node n ) { - assert CodeGen.CODE._phase.ordinal() >= CodeGen.Phase.Select.ordinal(); - _nid = CODE.getUID(); // allocate unique dense ID - _inputs = new Ary<>(n==null ? new Node[0] : n._inputs.asAry()); - _outputs = new Ary<>(Node.class); - _type = n==null ? Type.BOTTOM : n._type; - _deps = null; - _hash = 0; - } - - // Easy reading label for debugger, e.g. "Add" or "Region" or "EQ" - public abstract String label(); - - // Unique label for graph visualization, e.g. "Add12" or "Region30" or "EQ99" - public String uniqueName() { - // Get rid of $ as graphviz doesn't like it - String label = label().replaceAll("\\$", ""); - return label + _nid; - } - - // Graphical label, e.g. "+" or "Region" or "==" - public String glabel() { return label(); } - - // Extra fun stuff, for assembly printing. Jump labels, parser locations, - // variable types, etc. - public String comment() { return null; } - - // ------------------------------------------------------------------------ - - // Debugger Printing. - - // {@code toString} is what you get in the debugger. It has to print 1 - // line (because this is what a debugger typically displays by default) and - // has to be robust with broken graph/nodes. - @Override - public final String toString() { return print(); } - - // This is a *deep* print. We print with a mutually recursive tik-tok - // style; the common _print0 calls the per-Node _print1, which calls back - // to _print0; - public final String print() { - return _print0(new StringBuilder(), new BitSet()).toString(); - } - - // This is the common print: check for repeats, check for DEAD and print - // "DEAD" else call the per-Node print1. - public final StringBuilder _print0(StringBuilder sb, BitSet visited) { - if (visited.get(_nid) && !(this instanceof ConstantNode) ) - return sb.append(label()); - visited.set(_nid); - return isDead() - ? sb.append(uniqueName()).append(":DEAD") - : _print1(sb, visited); - } - // Every Node implements this; a partial-line recursive print - abstract public StringBuilder _print1(StringBuilder sb, BitSet visited); - - public String p(int depth) { return IRPrinter.prettyPrint(this,depth); } - - public boolean isConst() { return false; } - - // ------------------------------------------------------------------------ - // Graph Node & Edge manipulation - - /** - * Gets the ith input node - * @param i Offset of the input node - * @return Input node or null - */ - public Node in(int i) { return _inputs.get(i); } - public Node out(int i) { return _outputs.get(i); } - public final Ary outs() { return _outputs; } - - public int nIns() { return _inputs.size(); } - - public int nOuts() { return _outputs.size(); } - - public boolean isUnused() { return nOuts() == 0; } - - public CFGNode cfg0() { return (CFGNode)in(0); } - - /** - * Change a def into a Node. Keeps the edges correct, by removing - * the corresponding use->def edge. This may make the original - * def go dead. This function is co-recursive with {@link #kill}. - *

- - * This method is the normal path for altering a Node, because it does the - * proper default edge maintenance. It also immediately kills - * Nodes that lose their last use; at times care must be taken to avoid - * killing Nodes that are being used without having an output Node. This - * definitely happens in the middle of recursive {@link #peephole} calls. - * - * @param idx which def to set - * @param new_def the new definition - * @return new_def for flow coding - */ - public N setDef(int idx, N new_def ) { - unlock(); - Node old_def = in(idx); - if( old_def == new_def ) return new_def; // No change - // If new def is not null, add the corresponding def->use edge - // This needs to happen before removing the old node's def->use edge as - // the new_def might get killed if the old node kills it recursively. - if( new_def != null ) - new_def.addUse(this); - // Set the new_def over the old (killed) edge - _inputs.set(idx,new_def); - if( old_def != null ) { // If the old def exists, remove a def->use edge - if( old_def.delUse(this) ) // If we removed the last use, the old def is now dead - old_def.kill(); // Kill old def - else CODE.add(old_def); // Else old lost a use, so onto worklist - } - moveDepsToWorklist(); - // 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. - public Node delDef(int idx) { - unlock(); - Node old_def = in(idx); - _inputs.del(idx); - if( old_def.delUse(this) ) // If we removed the last use, the old def is now dead - old_def.kill(); // Kill old def - old_def.moveDepsToWorklist(); - return this; - } - - // Insert the numbered input, sliding other inputs to the right - Node insertDef(int idx, Node new_def) { - _inputs.add(idx,null); - return setDef(idx,new_def); - } - - - /** - * Add a new def to an existing Node. Keep the edges correct by - * adding the corresponding def->use edge. - * - * @param new_def the new definition, appended to the end of existing definitions - * @return new_def for flow coding - */ - public Node addDef(Node new_def) { - unlock(); - // Add use->def edge - _inputs.add(new_def); - // If new def is not null, add the corresponding def->use edge - if( new_def != null ) - new_def.addUse(this); - return new_def; - } - - // Breaks the edge invariants, used temporarily - @SuppressWarnings("unchecked") - protected N addUse(Node n) { _outputs.add(n); return (N)this; } - - // Remove node 'use' from 'def's (i.e. our) output list, by compressing the list in-place. - // Return true if the output list is empty afterward. - // Error is 'use' does not exist; ok for 'use' to be null. - protected boolean delUse( Node use ) { - _outputs.del(_outputs.find(use)); - return _outputs.isEmpty(); - } - - // Shortcut for "popping" until n nodes. A "pop" is basically a - // setDef(last,null) followed by lowering the nIns() count. - public void popUntil(int n) { - unlock(); - while( nIns() > n ) { - Node old_def = _inputs.pop(); - if( old_def != null && // If it exists and - old_def.delUse(this) ) // If we removed the last use, the old def is now dead - old_def.kill(); // Kill old def - } - } - - /** - * Kill a Node with no uses, by setting all of its defs - * to null. This may recursively kill more Nodes, and is basically dead - * code elimination. - */ - public void kill( ) { - assert isUnused(); // Has no uses, so it is dead - unlock(); - moveDepsToWorklist(); - _type=null; // Flag as dead - while( nIns()>0 ) { // Set all inputs to null, recursively killing unused Nodes - Node old_def = _inputs.removeLast(); - // Revisit neighbor because removed use - if( old_def != null && CODE.add(old_def).delUse(this) ) - old_def.kill(); // If we removed the last use, the old def is now dead - } - assert isDead(); // Really dead now - } - - // Preserve CFG use-ordering when killing - public void killOrdered() { - CFGNode cfg = cfg0(); - cfg._outputs.remove(cfg._outputs.find(this)); - _inputs.set(0,null); - kill(); - } - - - // Mostly used for asserts and printing. - public boolean isDead() { return isUnused() && nIns()==0 && _type==null; } - - // Shortcuts to stop DCE mid-parse - // Add bogus null use to keep node alive - public N keep() { return addUse(null); } - // Remove bogus null. - @SuppressWarnings("unchecked") - public N unkeep() { - delUse(null); - return (N)this; - } - // Test "keep" status - public boolean iskeep() { return _outputs.find(null) != -1; } - public void unkill() { - if( unkeep().isUnused() ) - kill(); - } - public void isKill() { - if( isUnused() ) - kill(); - } - - - // Replace self with nnn in the graph, making 'this' go dead - public void subsume( Node nnn ) { - assert nnn!=this; - while( nOuts() > 0 ) { - Node n = _outputs.removeLast(); - n.unlock(); - int idx = n._inputs.find(this); - n._inputs.set(idx,nnn); - nnn.addUse(n); - CODE.add(n); - CODE.addAll(n._outputs); - } - 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); - if( cfg instanceof CProjNode && cfg.in(0) instanceof NeverNode nvr ) - cfg = nvr.cfg0(); - 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 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 - - /** - * Try to peephole at this node and return a better replacement Node. - * Always returns some not-null Node (often this). - */ - public final Node peephole( ) { - if( _type==null ) // Brand-new node, never peeped before - JSViewer.show(); - Node n = peepholeOpt(); - if( n!=null ) // Made progress? - JSViewer.show(); // Show again - return n==null ? this : deadCodeElim(n._nid >= _nid ? n.peephole() : n); // Cannot return null for no-progress - } - - /** - * Try to peephole at this node and return a better replacement Node if - * possible. We compute a {@link Type} and then check and replace: - *

    - *
  • if the Type {@link Type#isConstant}, we replace with a {@link ConstantNode}
  • - *
  • in a future chapter we will look for a - * Common Subexpression - * to eliminate.
  • - *
  • we ask the Node for a better replacement. The "better replacement" - * is things like {@code (1+2)} becomes {@code 3} and {@code (1+(x+2))} becomes - * {@code (x+(1+2))}. By canonicalizing expressions we fold common addressing - * math constants, remove algebraic identities and generally simplify the - * code.
  • - * - * Unlike peephole above, this explicitly returns null for no-change, or not-null - * for a better replacement (which can be this). - *
- */ - public final Node peepholeOpt( ) { - CODE.iterCnt(); - // Compute initial or improved Type - Type old = setType(compute()); - - // Replace constant computations from non-constants with a constant - // node. If peeps are disabled, still allow high Phis to collapse; - // they typically come from dead Regions, and we want the Region to - // collapse, which requires the Phis to die first. - if( !isConst() && _type.isHighOrConst() ) - return ConstantNode.make(_type).peephole(); - - // Global Value Numbering - if( _hash==0 ) { - Node n = CODE._gvn.get(this); // Will set _hash as a side effect - if( n==null ) - CODE._gvn.put(this,this); // Put in table now - else { - // Because of random worklist ordering, the two equal nodes - // might have different types. Because of monotonicity, both - // types are valid. To preserve monotonicity, the resulting - // shared Node has to have the best of both types. - n.setType(n._type.join(_type)); - _hash = 0; // Clear, since it never went in the table - return deadCodeElim(n);// Return previous; does Common Subexpression Elimination - } - } - - // Ask each node for a better replacement - Node n = idealize(); - if( n != null ) // Something changed - return n; // Report progress - - if( old!=_type ) return this; // Report progress; - CODE.iterNop(); - return null; // No progress - } - - // m is the new Node, self is the old. - // Return 'm', which may have zero uses but is alive nonetheless. - // If self has zero uses (and is not 'm'), {@link #kill} self. - private Node deadCodeElim(Node m) { - // If self is going dead and not being returned here (Nodes returned - // from peephole commonly have no uses (yet)), then kill self. - if( m != this && isUnused() && !isDead() ) { - // Killing self - and since self recursively kills self's inputs we - // might end up killing 'm', which we are returning as a live Node. - // So we add a bogus extra null output edge to stop kill(). - m.keep(); // Keep m alive - kill(); // Kill self because replacing with 'm' - m.unkeep(); // Okay to peephole m - } - return m; - } - - /** - * This function needs to be - * Monotonic - * as it is part of a Monotone Analysis Framework. - * See for example this set of slides. - *

- * For Chapter 2, all our Types are really integer constants, and so all - * the needed properties are trivially true, and we can ignore the high - * theory. Much later on, this will become important and allow us to do - * many fancy complex optimizations trivially... because theory. - *

- * {@link #compute()} needs to be stand-alone, and cannot recursively call compute - * on its inputs, because programs are cyclic (have loops!) and this will just - * infinitely recurse until stack overflow. Instead, compute typically - * computes a new type from the {@link #_type} field of its inputs. - */ - public abstract Type compute(); - - // Set the type. Assert monotonic progress. - // If changing, add users to worklist. - public Type setType(Type type) { - Type old = _type; - assert old==null || type.isa(old); // Since _type not set, can just re-run this in assert in the debugger - if( old == type ) return old; - _type = type; // Set _type late for easier assert debugging - CODE.addAll(_outputs); - moveDepsToWorklist(); - return old; - } - - @SuppressWarnings("unchecked") - public N init() { _type = compute(); return (N)this; } - - /** - * This function rewrites the current Node into a more "idealized" form. - * This is the bulk of our peephole rewrite rules, and we use this to - * e.g. turn arbitrary collections of adds and multiplies with mixed - * constants into a normal form that's easy for hardware to implement. - * Example: An array addressing expression: - *

   ary[idx+1]
- * might turn into Sea-of-Nodes IR: - *
   (ary+12)+((idx+1) * 4)
- * This expression can then be idealized into: - *
   ary + ((idx*4) + (12 + (1*4)))
- * And more folding: - *
   ary + ((idx<<2) + 16)
- * And during code-gen: - *
   MOV4 Rary,[Ridx<<2 +16] // or some such hardware-specific notation 
- *

- * {@link #idealize} has a very specific calling convention: - *

    - *
  • If NO change is made, return {@code null} - *
  • If ANY change is made, return not-null; this can be {@code this} - *
  • The returned Node does NOT call {@link #peephole} on itself; the {@link #peephole} call will recursively peephole it. - *
  • Any NEW nodes that are not directly returned DO call {@link #peephole}. - *
- *

- * Examples: - * - * - * - * - * - *
before after return comment
{@code (x+5) }{@code (x+5) }{@code null }No change
{@code (5+x) }{@code (x+5) }{@code this }Swapped arguments
{@code ((x+1)+2)}{@code (x+(1+2))}{@code (x+_) }Returns 2 new Nodes
- * - * The last entry deserves more discussion. The new Node {@code (1+2)} - * created in {@link #idealize} calls {@link #peephole} (which then folds - * into a constant). The other new Node {@code (x+3)} does not call - * peephole, because it is returned and peephole itself will recursively - * call peephole. - *

- * Since idealize calls peephole and peephole calls idealize, you must be - * careful that all idealizations are monotonic: all transforms - * remove some feature, so that the set of available transforms always - * shrinks. If you don't, you risk an infinite peephole loop! - * - * @return Either a new or changed node, or null for no changes. - */ - public abstract Node idealize(); - - - // Some of the peephole rules get complex, and search further afield than - // just the nearest neighbor. These peepholes can fail the pattern match - // on a node some distance away, and if that node ever changes we should - // retry the peephole. Track a set of Nodes dependent on `this`, and - // revisit them if `this` changes. - Ary _deps; - - /** - * Add a node to the list of dependencies. Only add it if its not an input - * or output of this node, that is, it is at least one step away. The node - * being added must benefit from this node being peepholed. - */ - N addDep( N dep ) { - // Running peepholes during the big assert cannot have side effects - // like adding dependencies. - if( CODE._midAssert ) return dep; - if( dep._deps==null ) dep._deps = new Ary<>(Node.class); - if( dep._deps .find(this) != -1 ) return dep; // Already on list - if( dep._inputs .find(this) != -1 ) return dep; // No need for deps on immediate neighbors - if( dep._outputs.find(this) != -1 ) return dep; - dep._deps.add(this); - return dep; - } - - // Move the dependents onto a worklist, and clear for future dependents. - public void moveDepsToWorklist( ) { - if( _deps==null ) return; - CODE.addAll(_deps); - _deps.clear(); - } - - - // Two nodes are equal if they have the same inputs and the same "opcode" - // which means the same Java class, plus same internal parts. - @Override public final boolean equals( Object o ) { - if( o==this ) return true; - if( o.getClass() != getClass() ) return false; - Node n = (Node)o; - int len = _inputs.size(); - if( len != n._inputs.size() ) return false; - for( int i=0; i>13) ^ n._nid; - if( hash==0 ) hash = 0xDEADBEEF; // Bad hash, so use some junky thing - return (_hash=hash); - } - // Subclasses add extra hash info (such as ConstantNodes constant) - int hash() { return 0; } - - // ------------------------------------------------------------------------ - // - - /** Is this Node Memory related */ - public boolean isMem() { return false; } - - /** Pinned in the schedule; these are data nodes whose input#0 is not allowed to change */ - public boolean isPinned() { return false; } - - // Semantic change to the graph (so NOT a peephole), used by the Parser. - // If any input is a float, flip to a float-flavored opcode and widen any - // non-float input. - public final Node widen() { - if( !hasFloatInput() ) return this; - Node flt = copyF(); - if( flt==null ) return this; - for( int i=1; i(Node.class); - n._outputs = new Ary<>(Node.class); - n._deps = null; - n._hash = 0; - return n; - } - - // Report any post-optimize errors - public Parser.ParseException err() { return null; } - - // Common integer constants - public static ConstantNode con(long x) { return (ConstantNode)(new ConstantNode(TypeInteger.constant(x)).peephole()); } - - // Utility to walk the entire graph applying a function; return the first - // not-null result. - final public E walk( Function pred ) { - assert CODE._visit.isEmpty(); - E rez = _walk(pred); - CODE._visit.clear(); - return rez; - } - - private E _walk( Function pred ) { - if( CODE._visit.get(_nid) ) return null; // Been there, done that - CODE._visit.set(_nid); - E x = pred.apply(this); - if( x != null ) return x; - for( Node def : _inputs ) if( def != null && (x = def._walk(pred)) != null ) return x; - for( Node use : _outputs ) if( use != null && (x = use._walk(pred)) != null ) return x; - return null; - } - - /** - * Debugging utility to find a Node by index - */ - 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/NotNode.java b/src/main/java/com/seaofnodes/simple/node/NotNode.java deleted file mode 100644 index 4b80e61c..00000000 --- a/src/main/java/com/seaofnodes/simple/node/NotNode.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.seaofnodes.simple.node; - -import com.seaofnodes.simple.type.*; -import com.seaofnodes.simple.util.Utils; -import java.util.BitSet; - -public class NotNode extends Node { - public NotNode(Node in) { super(null, in); } - - @Override public String label() { return "Not"; } - - @Override public String glabel() { return "!"; } - - @Override - public StringBuilder _print1(StringBuilder sb, BitSet visited) { - in(1)._print0(sb.append("(!"), visited); - return sb.append(")"); - } - - @Override - public TypeInteger compute() { - Type t0 = in(1)._type; - if( t0.isHigh() ) return (TypeInteger)TypeInteger.BOOL.dual(); - if( t0 == Type.NIL || t0 == TypeInteger.ZERO ) return TypeInteger.TRUE; - if( t0 instanceof TypeNil tn && tn.notNull() ) return TypeInteger.FALSE; - if( t0 instanceof TypeInteger i && (i._min > 0 || i._max < 0) ) return TypeInteger.FALSE; - return TypeInteger.BOOL; - } - - @Override - public Node idealize() { return null; } -} diff --git a/src/main/java/com/seaofnodes/simple/node/OrNode.java b/src/main/java/com/seaofnodes/simple/node/OrNode.java deleted file mode 100644 index ac0d8454..00000000 --- a/src/main/java/com/seaofnodes/simple/node/OrNode.java +++ /dev/null @@ -1,45 +0,0 @@ -package com.seaofnodes.simple.node; - -import com.seaofnodes.simple.Parser; -import com.seaofnodes.simple.type.Type; -import com.seaofnodes.simple.type.TypeInteger; - -public class OrNode extends ArithNode { - public OrNode(Parser.Lexer loc, Node lhs, Node rhs) { super(loc, lhs, rhs); } - - @Override public String label() { return "Or"; } - @Override public String op() { return "|"; } - @Override public String glabel() { return "|"; } - - - @Override long doOp( long x, long y ) { return x | y; } - @Override TypeInteger doOp(TypeInteger x, TypeInteger y) { - return TypeInteger.BOT; - } - - @Override - public Node idealize() { - Node lhs = in(1); - Node rhs = in(2); - Type t1 = lhs._type; - Type t2 = rhs._type; - - // Or of 0. We do not check for (0|x) because this will already - // canonicalize to (x|0) - if( t2.isConstant() && t2 instanceof TypeInteger i && i.value()==0 ) - return lhs; - - // Move constants to RHS: con*arg becomes arg*con - if ( t1.isConstant() && !t2.isConstant() ) - return swap12(); - - // Do we have ((x | (phi cons)) | con) ? - // Do we have ((x | (phi cons)) | (phi cons)) ? - // Push constant up through the phi: x | (phi con0|con0 con1|con1...) - Node phicon = AddNode.phiCon(this,true); - if( phicon!=null ) return phicon; - - return super.idealize(); - } - @Override Node copy(Node lhs, Node rhs) { return new OrNode(_loc,lhs,rhs); } -} diff --git a/src/main/java/com/seaofnodes/simple/node/ParmNode.java b/src/main/java/com/seaofnodes/simple/node/ParmNode.java deleted file mode 100644 index a14a4fb9..00000000 --- a/src/main/java/com/seaofnodes/simple/node/ParmNode.java +++ /dev/null @@ -1,45 +0,0 @@ -package com.seaofnodes.simple.node; - -import com.seaofnodes.simple.type.Type; -import java.util.BitSet; - -public class ParmNode extends PhiNode { - - // Argument indices are mapped one-to-one on CallNode inputs - public final int _idx; // Argument index - - public ParmNode(String label, int idx, Type declaredType, Node... inputs) { - super(label,declaredType,inputs); - _idx = idx; - } - public ParmNode(ParmNode parm) { super(parm, parm._label, parm._minType ); _idx = parm._idx; } - - @Override public String label() { return MemOpNode.mlabel(_label); } - - @Override public String glabel() { return _label; } - - public FunNode fun() { return (FunNode)in(0); } - - @Override - public StringBuilder _print1(StringBuilder sb, BitSet visited) { - if( "main".equals(fun()._name) && _label.equals("arg") ) - return sb.append("arg"); - sb.append("Parm_").append(_label).append("("); - for( Node in : _inputs ) { - if (in == null) sb.append("____"); - else in._print0(sb, visited); - sb.append(","); - } - sb.setLength(sb.length()-1); - sb.append(")"); - return sb; - } - - - // Always in-progress until we run out of unknown callers - @Override public boolean inProgress() { return in(0) instanceof FunNode fun && fun.inProgress(); } - - @Override public boolean eq( Node n ) { - return ((ParmNode)n)._idx==_idx && super.eq(n); - } -} diff --git a/src/main/java/com/seaofnodes/simple/node/PhiNode.java b/src/main/java/com/seaofnodes/simple/node/PhiNode.java deleted file mode 100644 index fcbacbc9..00000000 --- a/src/main/java/com/seaofnodes/simple/node/PhiNode.java +++ /dev/null @@ -1,271 +0,0 @@ -package com.seaofnodes.simple.node; - -import com.seaofnodes.simple.*; -import com.seaofnodes.simple.codegen.CodeGen; -import com.seaofnodes.simple.type.*; -import com.seaofnodes.simple.util.Utils; -import java.util.BitSet; - -public class PhiNode extends Node { - - public final String _label; - - // The Phi type we compute must stay within the domain of the Phi. Example - // Int stays Int, Ptr stays Ptr, Control stays Control, Mem stays Mem. - Type _minType; - - public PhiNode(String label, Type minType, Node... inputs) { - super(inputs); - _label = label; - assert minType!=null; - _minType = minType; - } - // Used by ParmNode - public PhiNode(PhiNode phi, String label, Type minType) { super(phi); _label = label; _type = _minType = minType; } - // Used by instruction Selection - public PhiNode(PhiNode phi) { this(phi,phi._label,phi._minType ); } - // Used by the infinite-loop exit breaker - public PhiNode(RegionNode r, Node sample) { - super(new Node[]{r}); - _label = ""; - _minType = sample._type; - while( nIns() < r.nIns() ) - addDef(sample); - } - - @Override public String label() { return "Phi_"+MemOpNode.mlabel(_label); } - - @Override public String glabel() { return "φ_"+_label; } - - @Override - public StringBuilder _print1(StringBuilder sb, BitSet visited) { - if( !(region() instanceof RegionNode r) || r.inProgress() ) - sb.append("Z"); - sb.append("Phi("); - for( Node in : _inputs ) { - if (in == null) sb.append("____"); - else in._print0(sb, visited); - sb.append(","); - } - sb.setLength(sb.length()-1); - sb.append(")"); - return sb; - } - - public CFGNode region() { return (CFGNode)in(0); } - @Override public boolean isMem() { return _minType instanceof TypeMem; } - @Override public boolean isPinned() { return true; } - - @Override - public Type compute() { - if( !(region() instanceof RegionNode r) ) - return region()._type==Type.XCONTROL ? (_type instanceof TypeMem ? TypeMem.TOP : Type.TOP) : _type; - // During parsing Phis have to be computed type pessimistically. - if( r.inProgress() ) return _minType; - // Set type to local top of the starting type - //Type t = _minType.dual(); - Type t = Type.TOP; - for (int i = 1; i < nIns(); i++) - // If the region's control input is live, add this as a dependency - // to the control because we can be peeped should it become dead. - if( addDep(r.in(i))._type != Type.XCONTROL ) { - if( in(i)._type==Type.BOTTOM ) - return Type.BOTTOM; - t = t.meet(in(i)._type); - } - t = t.join( _minType ); - return t; - } - - @Override - public Node idealize() { - if( !(region() instanceof RegionNode r ) ) - return in(1); // Input has collapse to e.g. starting control. - // Can upgrade minType even while in-progress - if( _minType instanceof TypeMemPtr tmp && _minType.isFRef() ) { - TypeMemPtr tmp2 = (TypeMemPtr)CodeGen.CODE.P.TYPES.get(tmp._obj._name); - if( tmp2!=null && tmp2 != _minType ) { - _minType = tmp2; - return this; - } - } - if( r.inProgress() || r.nIns()<=1 ) - return null; // Input is in-progress - - // If we have only a single unique input, become it. - Node live = singleUniqueInput(); - if( live != null ) { - if( live._type.isa(_type) ) - return live; - // Keep the Phi upcast - return new CastNode(_type,null,live); - } - - // No bother if region is going to fold dead paths soon - for( int i=1; i 0 && idom.in(0) != iff ) idom = idom.idom(); - if( idom instanceof CProjNode proj && proj._idx==1 ) - return val; - } - } - } - - return null; - } - - // Same op on all Phi paths; all ops have only the Phi as a use. - // None have a control input. - private boolean same_op() { - for( int i=1; i 1 ) { // Too many users, but addDep in case lose users - for( Node out : op._outputs ) - if( out!=null && out!=this ) - addDep(out); - return false; - } - for( int j=1; j3 && // No Phi users, just a control user - in(1) instanceof CProjNode p1 && - in(2) instanceof CProjNode p2 && - addDep(p1.in(0))==addDep(p2.in(0)) && - p1.in(0) instanceof IfNode iff ) { - // Replace with the iff.ctrl directly - if( nIns()==3 ) return iff.ctrl(); - // Just delete the path for fat Regions - setDef(1,iff.ctrl()); - return delDef(2); - } - - // Flatten stack regions (no loops involved) - if( getClass() == RegionNode.class ) { - for( int i=1; i2 && !hasMidUser(region) ) { - assert !region.inProgress(); - // Fold Phis - for( Node use : _outputs ) { - if( use instanceof PhiNode phi ) { - PhiNode phi2 = phi.in(i) instanceof PhiNode phi2x && phi2x.region()==region ? phi2x : null; - for( int j=1; j1 && in(nIns()-1) == null; } - - // Never equal if inProgress - @Override public boolean eq( Node n ) { return !inProgress(); } - -} diff --git a/src/main/java/com/seaofnodes/simple/node/ReturnNode.java b/src/main/java/com/seaofnodes/simple/node/ReturnNode.java deleted file mode 100644 index 7971733a..00000000 --- a/src/main/java/com/seaofnodes/simple/node/ReturnNode.java +++ /dev/null @@ -1,166 +0,0 @@ -package com.seaofnodes.simple.node; - -import com.seaofnodes.simple.Parser; -import com.seaofnodes.simple.codegen.CodeGen; -import com.seaofnodes.simple.codegen.Encoding; -import com.seaofnodes.simple.codegen.RegMask; -import com.seaofnodes.simple.type.*; -import com.seaofnodes.simple.util.SB; -import com.seaofnodes.simple.util.Utils; -import java.util.BitSet; - -/** - * The Return node has two inputs. The first input is a control node and the - * second is the data node that supplies the return value. - *

- * In this presentation, Return functions as a Stop node, since multiple return statements are not possible. - * The Stop node will be introduced in Chapter 6 when we implement if statements. - *

- * The Return's output is the value from the data node. - */ -public class ReturnNode extends CFGNode { - - public FunNode _fun; - - public ReturnNode(Node ctrl, Node mem, Node data, Node rpc, FunNode fun ) { - super(ctrl, mem, data, rpc); - _fun = fun; - } - public ReturnNode( ReturnNode ret, FunNode fun ) { super(ret); _fun = fun; } - - public Node ctrl() { return in(0); } - public Node mem () { return in(1); } - public Node expr() { return in(2); } - public Node rpc () { return in(3); } - @Override public FunNode fun() { return _fun; } - - @Override - public String label() { return "Return"; } - - @Override - public StringBuilder _print1(StringBuilder sb, BitSet visited) { - sb.append("return "); - if( expr()==null ) sb.append("----"); - else expr()._print0(sb, visited); - return sb.append(";"); - } - - // No one unique control follows; can be many call end sites - @Override public CFGNode uctrl() { return null; } - - @Override - public Type compute() { - if( inProgress () ) return TypeTuple.RET; // In progress - return TypeTuple.make(ctrl()._type,mem()._type,expr()._type); - } - - @Override public Node idealize() { - if( inProgress () ) return null; - if( _fun.isDead() ) return null; - - // Upgrade signature based on return type - Type ret = expr()._type; - TypeFunPtr fcn = _fun.sig(); - if( ret != fcn.ret() && ret.isa(fcn.ret()) ) - _fun.setSig(fcn.makeFrom(ret)); - - // If dead (cannot be reached; infinite loop), kill the exit values - if( ctrl()._type==Type.XCONTROL && - !(mem() instanceof ConstantNode && expr() instanceof ConstantNode) ) { - Node top = new ConstantNode(Type.TOP).peephole(); - setDef(1,top); - setDef(2,top); - return this; - } - - return null; - } - - public boolean inProgress() { - return ctrl().getClass() == RegionNode.class && ((RegionNode)ctrl()).inProgress(); - } - - // Gather parse-time return types for error reporting - private Type mt = Type.TOP; - private boolean ti=false, tf=false, tp=false, tn=false; - - // Add a return exit to the current parsing function - void addReturn( Node ctrl, Node rmem, Node expr ) { - assert inProgress(); - - // Gather parse-time return types for error reporting - Type t = expr._type; - mt = mt.meet(t); - ti |= t instanceof TypeInteger x; - tf |= t instanceof TypeFloat x; - tp |= t instanceof TypeMemPtr x; - tn |= t==Type.NIL; - - // Merge path into the One True Return - RegionNode r = (RegionNode)ctrl(); - // Assert that the Phis are in particular outputs; not reordered or shuffled - PhiNode mem = (PhiNode)r.out(0); assert mem._minType == TypeMem.BOT; - PhiNode rez = (PhiNode)r.out(1); assert rez._minType == Type.BOTTOM; - // Pop "inProgress" null off - r ._inputs.pop(); - mem._inputs.pop(); - rez._inputs.pop(); - // Add new return point - r .addDef(ctrl); - mem.addDef(rmem); - rez.addDef(expr); - // Back to being inProgress - r .addDef(null); - mem.addDef(null); - rez.addDef(null); - } - - @Override public Parser.ParseException err() { - return expr()._type/*mt*/==Type.BOTTOM ? mixerr(ti,tf,tp,tn,_fun._loc) : null; - } - - static Parser.ParseException mixerr( boolean ti, boolean tf, boolean tp, boolean tn, Parser.Lexer loc ) { - if( !ti && !tf && !tp && !tn ) - return Parser.error("No defined return type", loc); - SB sb = new SB().p("No common type amongst "); - if( ti ) sb.p("int and "); - if( tf ) sb.p("f64 and "); - if( tp || tn ) sb.p("reference and "); - return Parser.error(sb.unchar(5).toString(),loc); - } - - - // ------------ - // MachNode specifics, shared across all CPUs - public String op() { - return _fun._frameAdjust > 0 ? "addi" : "ret"; - } - // Correct Nodes outside the normal edges - public void postSelect(CodeGen code) { - FunNode fun = (FunNode)rpc().in(0); - _fun = fun; - fun.setRet(this); - } - public RegMask regmap(int i) { - return i==2 - ? CodeGen.CODE._mach.retMask(_fun.sig()) - : CodeGen.CODE._retMasks[i]; - } - public RegMask outregmap() { return null; } - public void encoding( Encoding enc ) { throw Utils.TODO(); } - public void asm(CodeGen code, SB sb) { - int frameAdjust = fun()._frameAdjust; - if( frameAdjust>0 ) - sb.p("rsp += #").p(frameAdjust).p("\nret"); - // Post code-gen, just print the "ret" - if( code._phase.ordinal() <= CodeGen.Phase.RegAlloc.ordinal() ) - // Prints return reg (either RAX or XMM0), RPC and then the - // callee-save registers. - for( int i=2; i>"; } - @Override public String glabel() { return ">>"; } - - @Override long doOp( long x, long y ) { return x >> y; } - @Override TypeInteger doOp( TypeInteger x, TypeInteger y ) { - if( y.isConstant() ) { - int log = (int)y.value(); - return TypeInteger.make(-1L<<(63-log),(1L<<(63-log))-1); - } - return TypeInteger.BOT; - } - - @Override - public Node idealize() { - Node lhs = in(1); - Node rhs = in(2); - Type t2 = rhs._type; - - // Sar of 0. - if( t2.isConstant() && t2 instanceof TypeInteger i && (i.value()&63)==0 ) - return lhs; - - // TODO: x >> 3 >> (y ? 1 : 2) ==> x >> (y ? 4 : 5) - - return super.idealize(); - } - @Override Node copy(Node lhs, Node rhs) { return new SarNode(_loc,lhs,rhs); } -} diff --git a/src/main/java/com/seaofnodes/simple/node/ScopeNode.java b/src/main/java/com/seaofnodes/simple/node/ScopeNode.java deleted file mode 100644 index 36746a81..00000000 --- a/src/main/java/com/seaofnodes/simple/node/ScopeNode.java +++ /dev/null @@ -1,468 +0,0 @@ -package com.seaofnodes.simple.node; - -import com.seaofnodes.simple.Parser; -import com.seaofnodes.simple.Var; -import com.seaofnodes.simple.codegen.CodeGen; -import com.seaofnodes.simple.type.*; -import com.seaofnodes.simple.util.Ary; -import com.seaofnodes.simple.util.AryInt; -import com.seaofnodes.simple.util.Utils; - -import java.util.*; - -/** - * The Scope node is purely a parser helper - it tracks names to nodes with a - * stack of variables. - */ -public class ScopeNode extends MemMergeNode { - - /** - * The control is a name that binds to the currently active control - * node in the graph - */ - public static final String CTRL = "$ctrl"; - public static final String ARG0 = "arg"; - public static final String MEM0 = "$mem"; - - // All active/live variables in all nested scopes, all run together. - // Maps 1-to-1 to the _inputs array. - public final Ary _vars; - - // Lexical scope is typed: - public static class Kind { - // Basic block scoping - public static class Block extends Kind { } - // Function scope - public static class Func extends Kind { } - // Struct definition, mapping types to fields - public static class Define extends Kind { public Define(TypeMemPtr tmp) { _tmp=tmp; } public final TypeMemPtr _tmp; } - // Struct allocation, mapping values to fields - public static class Alloc extends Kind { public Alloc (TypeMemPtr tmp) { _tmp=tmp; } public final TypeMemPtr _tmp; } - - public int _lexSize; // Number of Vars in this scope - } - public final Ary _kinds; - // Lexical scope nesting depth - public int depth() { return _kinds._len; } - - // Extra guards; tested predicates and casted results - private final Ary _guards; - - // A new ScopeNode - public ScopeNode() { - super(true); - _vars = new Ary<>(Var .class); - _kinds = new Ary<>(Kind.class); - _guards = new Ary<>(Node.class); - } - - @Override public String label() { return "Scope"; } - - @Override - public StringBuilder _print1(StringBuilder sb, BitSet visited) { - sb.append("Scope[ "); - int j=1; - for( int i=0; i N ctrl(N n) { return setDef(0,n); } - public Node mem(Node n) { return setDef(1,n); } - - public void push( Kind kind ) { - kind._lexSize = _vars.size(); - _kinds.push(kind); - } - - // Pop a lexical scope - public void pop() { - promote(); // Promote forward references to the next outer scope - int n = _kinds.pop()._lexSize; - popUntil(n); // Pop off inputs going out of scope - _vars.setLen(n); // Pop off variables going out of scope - } - - - // Look for forward references in the last lexical scope and promote to the - // next outer lexical scope. At the last scope declare them an error. - public void promote() { - Kind kind = _kinds.last(); - int n = kind._lexSize; - for( int i=n; i=0; i-- ) - if( v._idx >= _kinds.at(i)._lexSize ) - return _kinds.at(i); - throw Utils.TODO(); - } - - // Return the kind for the defining scope, or a function scope if found - // first. Block scopes means the variable can be r/w directly in the - // scope, Function scopes require final constants (no capture), and - // Constructor scopes mean an instance variable reference. - - // Walk up-lexical scope looking for defining lexical Kind. - // - Skip any amount of nested block scopes - // - Walking out of a function requires v be a final constant (for now) - - // and we can stop walking. - - // - Walking out of a method and (skipping Blocks) into the matching struct - // is OK, and this is a instance var load. - - // - Walking out of a method and into the wrong struct is an error; - // including nested structs containing (no nested classes yet) - - // Returns error, forward-ref, ok containing struct, ok containing function/block. - - public String outOfFunction( Var v ) { - //if( v==null ) return null; // Prolly forward reference - //int i; for( i=_lexSize._len-1; i>=0 && v._idx<_lexSize.at(i); i-- ) - // if( _kinds.at(i)=="{->}" ) return "{->}"; - //return _kinds.at(i); - throw Utils.TODO(); - } - - - // Find name in reverse, return an index into _vars or -1. Linear scan - // instead of hashtable, but probably doesn't matter until the scan - // typically hits many dozens of variables. - public int find( String name ) { - for( int i=_vars.size()-1; i>=0; i-- ) - if( var(i)._name.equals(name) ) - return i; - return -1; - } - - /** - * Create a new variable name in the current scope - */ - public boolean define( String name, Type declaredType, boolean xfinal, Node init, Parser.Lexer loc ) { - assert _kinds.isEmpty() || name.charAt(0)!='$' ; // Later scopes do not define memory - if( depth() > 0 ) - for( int i=_vars.size()-1; i>=_kinds.last()._lexSize; i-- ) { - Var n = var(i); - if( n._name.equals(name) ) { - if( !n.isFRef() ) return false; // Double define - FRefNode fref = (FRefNode)in(n._idx); // Get forward ref - if( !xfinal || !declaredType.isConstant() ) throw fref.err(); // Must be a final constant - n.defFRef(declaredType,xfinal,loc); // Declare full correct type, final, source location - setDef(n._idx,fref.addDef(init)); // Set FRef to defined; tell parser also - } - } - Var v = new Var(nIns(),name,declaredType,xfinal,loc,init==Parser.XCTRL); - _vars.add(v); - // Creating a forward reference - if( init==Parser.XCTRL ) - init = new FRefNode(v).init(); - addDef(init); - return true; - } - - // Read from memory - public Node mem( int alias ) { return mem()._mem(alias,null); } - // Write to memory - public void mem( int alias, Node st ) { mem()._mem(alias,st); } - - - /** - * Lookup a name in all scopes starting from most deeply nested. - * - * @param name Name to be looked up - * @return null if not found, or the implementing Node - */ - public Var lookup( String name ) { - int idx = find(name); - // -1 is missed in all scopes, not found - return idx == -1 ? null : update(var(idx),null); - } - - /** - * Redefine an existing name - * - * @param name Name being redefined - * @param n The node to bind to the name - */ - public void update( String name, Node n ) { - int idx = find(name); - assert idx>=0; - update(var(idx),n); - } - - public Var update( Var v, Node st ) { - Node old = in(v._idx); - if( old instanceof ScopeNode loop ) { - // Lazy Phi! - Node def = loop.in(v._idx); - old = def instanceof PhiNode phi && loop.ctrl()==phi.region() - // Loop already has a real Phi, use it - ? def - // Set real Phi in the loop head - // The phi takes its one input (no backedge yet) from a recursive - // lookup, which might have insert a Phi in every loop nest. - : loop.setDef(v._idx,new PhiNode(v._name, v.type(), loop.ctrl(), loop.in(loop.update(v,null)._idx),null).peephole()); - setDef(v._idx,old); - } - //assert !v._final || st==null; - if( st!=null ) setDef(v._idx,st); // Set new value - return v; - } - - /** - * Duplicate a ScopeNode; including all levels, up to Nodes. So this is - * neither shallow (would dup the Scope but not the internal HashMap - * tables), nor deep (would dup the Scope, the HashMap tables, but then - * also the program Nodes). - *

- * If the {@code loop} flag is set, the edges are filled in as the original - * Scope, as an indication of Lazy Phis at loop heads. The goal here is to - * not make Phis at loop heads for variables which are never touched in the - * loop body. - *

- * The new Scope is a full-fledged Node with proper use<->def edges. - */ - public ScopeNode dup() { return dup(false); } - public ScopeNode dup(boolean loop) { - ScopeNode dup = new ScopeNode(); - // Our goals are: - // 1) duplicate the name bindings of the ScopeNode across all stack levels - // 2) Make the new ScopeNode a user of all the nodes bound - // 3) Ensure that the order of defs is the same to allow easy merging - dup._vars .addAll(_vars ); - dup._kinds .addAll(_kinds ); - dup._guards .addAll(_guards ); - // The dup'd guards all need dup'd keepers, to keep proper accounting - // when later removing all guards - for( Node n : _guards ) - if( !(n instanceof CFGNode) ) - n.keep(); - dup.addDef(ctrl()); // Control input is just copied - - // Memory input is a shallow copy - MemMergeNode memdup = new MemMergeNode(true), mem = mem(); - memdup.addDef(null); - memdup.addDef(loop ? this : mem.in(1)); - for( int i=2; i 0 ) { - Node cast = _guards.at(--i); - if( cast instanceof CFGNode ) continue; // Marker between guard sets - Node xpred = _guards.at(--i); - if( xpred == pred ) - return cast; - } - return pred; - } - - // Kill guards also - @Override public void kill() { - for( Node n : _guards ) - if( !(n instanceof CFGNode) ) - n.unkill(); - _guards.clear(); - // Can have lazy uses remaining - if( isUnused() ) - super.kill(); - } - - - private void replace( Node old, Node cast ) { - assert old!=null && old!=cast; - for( int i=0; i>shf) != x._min || - ((x._max<>shf) != x._max ) ) - return TypeInteger.make(x._min< (x << i) + (c << i) - if( lhs instanceof AddNode add && addDep(add).in(2)._type instanceof TypeInteger c && c.isConstant() ) { - long sum = c.value() << shl.value(); - if( Integer.MIN_VALUE <= sum && sum <= Integer.MAX_VALUE ) - return new AddNode(new ShlNode(_loc,add.in(1),rhs).peephole(), Parser.con(sum) ); - } - } - - // TODO: x << 3 << (y ? 1 : 2) ==> x << (y ? 4 : 5) - - return super.idealize(); - } - @Override Node copy(Node lhs, Node rhs) { return new ShlNode(_loc,lhs,rhs); } -} diff --git a/src/main/java/com/seaofnodes/simple/node/ShrNode.java b/src/main/java/com/seaofnodes/simple/node/ShrNode.java deleted file mode 100644 index 269f6b7f..00000000 --- a/src/main/java/com/seaofnodes/simple/node/ShrNode.java +++ /dev/null @@ -1,34 +0,0 @@ -package com.seaofnodes.simple.node; - -import com.seaofnodes.simple.Parser; -import com.seaofnodes.simple.type.Type; -import com.seaofnodes.simple.type.TypeInteger; - -public class ShrNode extends ArithNode { - public ShrNode(Parser.Lexer loc, Node lhs, Node rhs) { super(loc, lhs, rhs); } - - @Override public String label() { return "Shr"; } - @Override public String op() { return ">>>"; } - @Override public String glabel() { return ">>>"; } - - @Override long doOp( long x, long y ) { return x >>> y; } - @Override TypeInteger doOp( TypeInteger x, TypeInteger y ) { - return x == TypeInteger.ZERO ? x : TypeInteger.BOT; - } - - @Override - public Node idealize() { - Node lhs = in(1); - Node rhs = in(2); - Type t2 = rhs._type; - - // Shr of 0. - if( t2.isConstant() && t2 instanceof TypeInteger i && (i.value()&63)==0 ) - return lhs; - - // TODO: x >>> 3 >>> (y ? 1 : 2) ==> x >>> (y ? 4 : 5) - - return super.idealize(); - } - @Override Node copy(Node lhs, Node rhs) { return new ShrNode(_loc,lhs,rhs); } -} diff --git a/src/main/java/com/seaofnodes/simple/node/SplitNode.java b/src/main/java/com/seaofnodes/simple/node/SplitNode.java deleted file mode 100644 index d061cdbd..00000000 --- a/src/main/java/com/seaofnodes/simple/node/SplitNode.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.seaofnodes.simple.node; - -import com.seaofnodes.simple.codegen.CodeGen; -import com.seaofnodes.simple.type.Type; -import com.seaofnodes.simple.util.SB; -import java.util.BitSet; - -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 public StringBuilder _print1(StringBuilder sb, BitSet visited) { - sb.append("mov("); - if( in(1) == null ) sb.append("---"); - else in(1)._print0(sb,visited); - return sb.append(")"); - } - @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 deleted file mode 100644 index e3806de7..00000000 --- a/src/main/java/com/seaofnodes/simple/node/StartNode.java +++ /dev/null @@ -1,56 +0,0 @@ -package com.seaofnodes.simple.node; - -import com.seaofnodes.simple.codegen.CodeGen; -import com.seaofnodes.simple.Parser; -import com.seaofnodes.simple.type.*; -import java.util.Arrays; -import java.util.BitSet; -import java.util.HashSet; -import static com.seaofnodes.simple.util.Utils.TODO; - -/** - * The Start node represents the start of the function. - *

- * Start initially has 1 input (arg) from outside and the initial control. - * In ch10 we also add mem aliases as structs get defined; each field in struct - * adds a distinct alias to Start's tuple. - */ -public class StartNode extends LoopNode implements MultiNode { - - final Type _arg; - - public StartNode(Type arg) { super(null,null); _arg = arg; _type = compute(); } - public StartNode(StartNode start) { super(start); _arg = start==null ? null : start._arg; } - - @Override public String label() { return "Start"; } - - @Override - public StringBuilder _print1(StringBuilder sb, BitSet visited) { - return sb.append(label()); - } - - @Override public CFGNode cfg0() { return null; } - - // Get the one control following; error to call with more than one e.g. an - // IfNode or other multi-way branch. For Start, its "main" - @Override public CFGNode uctrl() { - // Find "main", it's the start. - CFGNode C = null; - for( Node use : _outputs ) - if( use instanceof FunNode fun && fun.sig().isa(CodeGen.CODE._main) ) - { assert C==null; C = fun; } - return C; - } - - - @Override public TypeTuple compute() { - return TypeTuple.make(Type.CONTROL,TypeMem.TOP,_arg); - } - - @Override public Node idealize() { return null; } - - // No immediate dominator, and idepth==0 - @Override public int idepth() { return CodeGen.CODE.iDepthAt(0); } - @Override public CFGNode idom(Node dep) { return null; } - -} diff --git a/src/main/java/com/seaofnodes/simple/node/StopNode.java b/src/main/java/com/seaofnodes/simple/node/StopNode.java deleted file mode 100644 index 4b03bf4e..00000000 --- a/src/main/java/com/seaofnodes/simple/node/StopNode.java +++ /dev/null @@ -1,75 +0,0 @@ -package com.seaofnodes.simple.node; - -import com.seaofnodes.simple.type.Type; -import com.seaofnodes.simple.util.Utils; -import java.util.BitSet; - -public class StopNode extends CFGNode { - - public final String _src; - - public StopNode(String src) { - super(); - _src = src; - _type = compute(); - } - public StopNode(StopNode stop) { super(stop); _src = stop==null ? null : stop._src; } - - @Override - public String label() { - return "Stop"; - } - - @Override - public StringBuilder _print1(StringBuilder sb, BitSet visited) { - // For the sake of many old tests, and single value prints as "return val" - ReturnNode ret1 = ret(); - if( ret1!=null ) return ret1._print0(sb,visited); - sb.append("Stop[ "); - for( Node ret : _inputs ) - if( ret!=null ) { - String name = ((ReturnNode)ret).fun()._name; - if( name== null || !name.startsWith("sys.") ) - ret._print0(sb, visited).append(" "); - } - return sb.append("]"); - } - - @Override public boolean blockHead() { return true; } - - - // If a single Return, return it. - // Otherwise, null because ambiguous. - public ReturnNode ret() { - Node ret1 = this; - for( Node ret : _inputs ) { - String name = ((ReturnNode)ret).fun()._name; - if( name==null || !name.startsWith("sys.") ) - ret1 = ret1==this ? ((ReturnNode)ret) : null; - } - return ret1==this ? null : (ReturnNode)ret1; - } - - @Override - public Type compute() { - return Type.BOTTOM; - } - - @Override - public Node idealize() { - int len = nIns(); - for( int i=0; i= store_size && not_affected_bits >= store_size) { - setDef(4, shl.in(1)); - return this; - } - } - return null; - } - - // Check that "mem" has no uses except "this" - private boolean checkOnlyUse(Node mem) { - if( mem.nOuts()==1 ) return true; - // Add deps on the other uses (can be e.g. ScopeNode mid-parse) so that - // when the other uses go away we can retry. - for( Node use : mem._outputs ) - if( use != this ) - addDep(use); - return false; - } - - @Override - public Parser.ParseException err() { - Parser.ParseException err = super.err(); - if( err != null ) return err; - TypeMemPtr tmp = (TypeMemPtr)ptr()._type; - if( tmp._obj.field(_name)._final && !_init ) - return Parser.error("Cannot modify final field '"+_name+"'",_loc); - return null; - } -} diff --git a/src/main/java/com/seaofnodes/simple/node/StructNode.java b/src/main/java/com/seaofnodes/simple/node/StructNode.java deleted file mode 100644 index b5b59b81..00000000 --- a/src/main/java/com/seaofnodes/simple/node/StructNode.java +++ /dev/null @@ -1,47 +0,0 @@ -package com.seaofnodes.simple.node; - -import com.seaofnodes.simple.type.*; -import com.seaofnodes.simple.util.SB; -import java.util.BitSet; - -/** - * Build a compound object - */ -public class StructNode extends Node { - - public final TypeStruct _ts; - public StructNode(TypeStruct ts) { _ts=ts; assert !ts._open; } - - @Override public String label() { return _ts==null ? "STRUCT?" : _ts.str(); } - - @Override - public StringBuilder _print1(StringBuilder sb, BitSet visited) { - if( _ts==null ) return sb.append("STRUCT?"); - sb.append(_ts._name).append(" {"); - for( int i=0; i> ").p(code.reg(in(2))); - } - -} 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 deleted file mode 100644 index 38373e67..00000000 --- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/AsrIARM.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.seaofnodes.simple.node.cpus.arm; - -import com.seaofnodes.simple.codegen.CodeGen; -import com.seaofnodes.simple.codegen.Encoding; -import com.seaofnodes.simple.codegen.RegMask; -import com.seaofnodes.simple.node.MachConcreteNode; -import com.seaofnodes.simple.node.MachNode; -import com.seaofnodes.simple.node.Node; -import com.seaofnodes.simple.util.SB; - -public class AsrIARM extends MachConcreteNode implements MachNode { - final int _imm; - AsrIARM(Node asr, int imm) { - super(asr); - _inputs.pop(); - _imm = imm; - } - @Override public String op() { return "asri"; } - @Override public RegMask regmap(int i) { return arm.RMASK; } - @Override public RegMask outregmap() { return arm.WMASK; } - @Override public void encoding( Encoding enc ) { - short rd = enc.reg(this); - short rn = enc.reg(in(1)); - assert _imm > 0; - enc.add4(arm.imm_shift(arm.OPI_ASR,_imm, 0b111111, rn, rd)); - } - // 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(" >> #").p(_imm); - } -} \ No newline at end of file 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 deleted file mode 100644 index b9ede544..00000000 --- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/BranchARM.java +++ /dev/null @@ -1,77 +0,0 @@ -package com.seaofnodes.simple.node.cpus.arm; - -import com.seaofnodes.simple.codegen.*; -import com.seaofnodes.simple.node.*; -import com.seaofnodes.simple.util.SB; -import com.seaofnodes.simple.util.Utils; - -// Jump on flags, uses flags -public class BranchARM extends IfNode implements MachNode, RIPRelSize { - String _bop; - BranchARM(IfNode iff, String bop ) { - super(iff); - _bop = bop; - } - @Override public String op() { return "j"+_bop; } - @Override public String label() { return op(); } - - @Override public void postSelect(CodeGen code) { - Node set = in(1); - if( set==null ) return; // Never-node cutout - 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; } - @Override public void negate() { _bop = negate(_bop); } - - @Override public void encoding( Encoding enc ) { - // Assuming that condition flags are already set. These flags are set - // by comparison (or sub). No need for regs because it uses flags - if( in(1)!=null ) { - // B.cond - enc.add4( arm.b_cond(arm.OP_BRANCH, 0, arm.make_condition(_bop)) ); - } else { - if( _bop=="!=" ) return; // Inverted, no code - enc.add4(arm.b(arm.OP_UJMP, 0)); - } - enc.jump(this,cproj(0)); - } - - // Delta is from opcode start - @Override public byte encSize(int delta) { - if( in(1)==null && _bop=="!=" ) return 0; // Inverted never-node, no code - if( -(1<<19) <= delta && delta < (1<<19) ) return 4; - // 2 word encoding needs a tmp register, must teach RA - throw Utils.TODO(); - } - - // Delta is from opcode start - @Override public void patch( Encoding enc, int opStart, int opLen, int delta ) { - assert !( in(1)==null && _bop=="!=" ); // Inverted never-node, no code no patch - if( opLen==4 ) { - enc.patch4(opStart,arm.b_cond(arm.OP_BRANCH, delta, arm.make_condition(_bop))); - } else { - throw Utils.TODO(); - } - } - - @Override public void asm(CodeGen code, SB sb) { - if( in(1)!=null ) { // Never-node - String src = code.reg(in(1)); - if( src!="flags" ) sb.p(src).p(" "); - } else if( _bop=="!=" ) { - sb.p("never"); - return; - } - CFGNode prj = cproj(0).uctrlSkipEmpty(); - if( !prj.blockHead() ) prj = prj.cfg0(); - sb.p(label(prj)); - } - @Override public String comment() { return "L"+cproj(1)._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 deleted file mode 100644 index a2cb9bba..00000000 --- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/CallARM.java +++ /dev/null @@ -1,50 +0,0 @@ -package com.seaofnodes.simple.node.cpus.arm; - -import com.seaofnodes.simple.codegen.*; -import com.seaofnodes.simple.node.*; -import com.seaofnodes.simple.type.TypeFunPtr; -import com.seaofnodes.simple.util.SB; - -public class CallARM extends CallNode implements MachNode, RIPRelSize { - final TypeFunPtr _tfp; - final String _name; - CallARM(CallNode call, TypeFunPtr tfp) { - super(call); - _inputs.pop(); // Pop constant target - assert tfp.isConstant(); - _tfp = tfp; - FunNode fun = CodeGen.CODE.link(tfp); - _name = fun==null ? ((ExternNode)call.fptr())._extern : fun._name; // Can be null for extern calls - } - - @Override public String op() { return "call"; } - @Override public String label() { return op(); } - @Override public String name() { return _name; } - @Override public TypeFunPtr tfp() { return _tfp; } - @Override public RegMask regmap(int i) { return arm.callInMask(_tfp,i,fun()._maxArgSlot); } - @Override public RegMask outregmap() { return null; } - @Override public int nargs() { return nIns()-2; } // Minus control, memory, fptr - - @Override public void encoding( Encoding enc ) { - FunNode fun = CodeGen.CODE.link(_tfp); - if( fun==null ) enc.external(this,_name); - else enc.relo(this); - // BL - enc.add4(arm.b(arm.OP_CALL,0)); // Target patched at link time - } - - // Delta is from opcode start, but X86 measures from the end of the 5-byte encoding - @Override public byte encSize(int delta) { return 4; } - - // Delta is from opcode start - @Override public void patch( Encoding enc, int opStart, int opLen, int delta ) { - enc.patch4(opStart,arm.b(arm.OP_CALL,delta)); - } - - @Override public void asm(CodeGen code, SB sb) { - sb.p(_name); - for( int i=0; i= 1L<<12 ) throw Utils.TODO(); - enc.add4(arm.imm_inst(arm.OPI_ADD, -sz&0xFFF, arm.RSP, arm.RSP)); - } -} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/I2F8ARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/I2F8ARM.java deleted file mode 100644 index f1fa162f..00000000 --- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/I2F8ARM.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.seaofnodes.simple.node.cpus.arm; - -import com.seaofnodes.simple.codegen.*; -import com.seaofnodes.simple.node.*; -import com.seaofnodes.simple.util.SB; - -public class I2F8ARM extends MachConcreteNode implements MachNode { - I2F8ARM(Node i2f8) { super(i2f8); } - @Override public String op() { return "cvt"; } - @Override public RegMask regmap(int i) { return arm.RMASK; } - @Override public RegMask outregmap() { return arm.DMASK; } - @Override public void encoding( Encoding enc ) { - // SCVTF - short self = (short)(enc.reg(this )-arm.D_OFFSET); - short reg1 = enc.reg(in(1)); - int body = arm.float_cast(arm.OP_FLOAT_C, 1, reg1, self); - enc.add4(body); - } - - // General form: "i2f8 (flt)int_value" - @Override public void asm(CodeGen code, SB sb) { - sb.p(code.reg(this)).p(" = ").p("(flt)").p(code.reg(in(1))); - } - -} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/IntARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/IntARM.java deleted file mode 100644 index ee74b95e..00000000 --- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/IntARM.java +++ /dev/null @@ -1,67 +0,0 @@ -package com.seaofnodes.simple.node.cpus.arm; - -import com.seaofnodes.simple.codegen.*; -import com.seaofnodes.simple.node.ConstantNode; -import com.seaofnodes.simple.node.MachNode; -import com.seaofnodes.simple.type.Type; -import com.seaofnodes.simple.type.TypeInteger; -import com.seaofnodes.simple.util.SB; - -// Integer constants -public class IntARM extends ConstantNode implements MachNode { - IntARM( ConstantNode con ) { super(con); } - - @Override public String op() { return "ldi"; } - @Override public RegMask regmap(int i) { return null; } - @Override public RegMask outregmap() { return arm.WMASK; } - - @Override public boolean isClone() { return true; } - @Override public IntARM copy() { return new IntARM(this); } - - @Override public void encoding( Encoding enc ) { - short self = enc.reg(this); - long x = _con==Type.NIL ? 0 : ((TypeInteger)_con).value(); - int nb0 = 0; - int nb1 = 0; - // Count number of 0000 and FFFF blocks - for (int i=0; i<64; i+=16) { - int block = (int)(x >> i) & 0xFFFF; - if (block == 0) nb0++; - if (block == 0xFFFF) nb1++; - } - int pattern; - int op; - if(nb0 >= nb1) { - // More 0 blocks then F blocks, use movz - pattern = 0; - op = arm.OP_MOVZ; - } else { - // More F blocks then 0 blocks, use movn - pattern = 0xFFFF; - op = arm.OP_MOVN; - } - int invert = pattern; - for (int i=0; i<4; i++) { - int block = (int)x & 0xFFFF; - x >>= 16; - if (block != pattern) { - enc.add4(arm.mov(op, i, block ^ invert, self)); - op = arm.OP_MOVK; - invert = 0; - } - } - if (op != arm.OP_MOVK) { - // All blocks are the same, special case - enc.add4(arm.mov(op, 0, 0, self)); - } - } - - // Human-readable form appended to the SB. Things like the encoding, - // indentation, leading address or block labels not printed here. - // Just something like "ld4\tR17=[R18+12] // Load array base". - // General form: "op\tdst=src+src" - @Override public void asm(CodeGen code, SB sb) { - String reg = code.reg(this); - _con.print(sb.p(reg).p(" = #")); - } -} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/LoadARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/LoadARM.java deleted file mode 100644 index 1e5d2094..00000000 --- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/LoadARM.java +++ /dev/null @@ -1,39 +0,0 @@ -package com.seaofnodes.simple.node.cpus.arm; - -import com.seaofnodes.simple.codegen.*; -import com.seaofnodes.simple.node.LoadNode; -import com.seaofnodes.simple.node.Node; -import com.seaofnodes.simple.type.TypeFloat; -import com.seaofnodes.simple.util.SB; - -// Load 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 - -public class LoadARM extends MemOpARM { - LoadARM(LoadNode ld, Node base, Node idx, int off) { - super(ld, base, idx, off, 0); - } - @Override public String op() { return "ld"+_sz; } - @Override public RegMask outregmap() { return arm.MEM_MASK; } - - private static final int[] OP_LOADS = new int[]{ arm.OP_LOAD_IMM_8, arm.OP_LOAD_IMM_16, arm.OP_LOAD_IMM_32, arm.OP_LOAD_IMM_64, }; - private int imm_op() { - return _declaredType == TypeFloat.F32 ? arm.OPF_LOAD_IMM_32 - : _declaredType == TypeFloat.F64 ? arm.OPF_LOAD_IMM_64 - : OP_LOADS[_declaredType.log_size()]; - } - - private static final int[] OP_LOAD_RS = new int[]{ arm.OP_LOAD_R_8 , arm.OP_LOAD_R_16 , arm.OP_LOAD_R_32 , arm.OP_LOAD_R_64, }; - - // ldr(immediate - unsigned offset) | ldr(register) - @Override public void encoding( Encoding enc ) { - ldst_encode(enc, imm_op(), OP_LOAD_RS[_declaredType.log_size()], this, size()); - } - @Override public void asm(CodeGen code, SB sb) { - sb.p(code.reg(this)).p(","); - asm_address(code,sb); - } -} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/LslARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/LslARM.java deleted file mode 100644 index 80ce493f..00000000 --- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/LslARM.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.seaofnodes.simple.node.cpus.arm; - -import com.seaofnodes.simple.codegen.*; -import com.seaofnodes.simple.node.*; -import com.seaofnodes.simple.util.SB; - -// Logical Shift Left (register) -public class LslARM extends MachConcreteNode implements MachNode { - LslARM(Node asr) {super(asr);} - @Override public String op() { return "shr"; } - @Override public RegMask regmap(int i) { return arm.RMASK; } - @Override public RegMask outregmap() { return arm.WMASK; } - - @Override public void encoding( Encoding enc ) { arm.shift_reg(enc,this,arm.OP_LSL); } - @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/LslIARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/LslIARM.java deleted file mode 100644 index 7fe16cbe..00000000 --- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/LslIARM.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.seaofnodes.simple.node.cpus.arm; - -import com.seaofnodes.simple.codegen.CodeGen; -import com.seaofnodes.simple.codegen.Encoding; -import com.seaofnodes.simple.codegen.RegMask; -import com.seaofnodes.simple.node.MachConcreteNode; -import com.seaofnodes.simple.node.MachNode; -import com.seaofnodes.simple.node.Node; -import com.seaofnodes.simple.util.SB; - -public class LslIARM extends MachConcreteNode implements MachNode { - final int _imm; - LslIARM(Node lsl, int imm) { - super(lsl); - _inputs.pop(); - _imm = imm; - } - @Override public String op() { return "lsli"; } - @Override public RegMask regmap(int i) { return arm.RMASK; } - @Override public RegMask outregmap() { return arm.WMASK; } - @Override public void encoding( Encoding enc ) { - short rd = enc.reg(this); - short rn = enc.reg(in(1)); - assert _imm > 0; - // UBFM , , #(- MOD 64), #(63-) - // immr must be (- MOD 64) = 64 - shift - enc.add4(arm.imm_shift(arm.OPI_LSL, 64 - _imm, (64 - _imm) - 1, rn, rd)); - } - // General form: "lsli rd = rs1 << imm" - @Override public void asm(CodeGen code, SB sb) { - sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" << #").p(_imm); - } -} \ No newline at end of file diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/LsrARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/LsrARM.java deleted file mode 100644 index fc005e04..00000000 --- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/LsrARM.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.seaofnodes.simple.node.cpus.arm; - -import com.seaofnodes.simple.codegen.*; -import com.seaofnodes.simple.node.*; -import com.seaofnodes.simple.util.SB; - -// Logical Shift Right (register) -public class LsrARM extends MachConcreteNode implements MachNode { - LsrARM(Node asr) {super(asr);} - @Override public String op() { return "shr"; } - @Override public RegMask regmap(int i) { return arm.RMASK; } - @Override public RegMask outregmap() { return arm.WMASK; } - - @Override public void encoding( Encoding enc ) { arm.shift_reg(enc,this,arm.OP_LSR); } - @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/LsrIARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/LsrIARM.java deleted file mode 100644 index 78844b0a..00000000 --- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/LsrIARM.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.seaofnodes.simple.node.cpus.arm; - -import com.seaofnodes.simple.codegen.*; -import com.seaofnodes.simple.node.*; -import com.seaofnodes.simple.util.SB; - -// logical right shift immediate -public class LsrIARM extends MachConcreteNode implements MachNode { - final int _imm; - LsrIARM(Node lsr, int imm) { - super(lsr); - _inputs.pop(); - _imm = imm; - } - @Override public String op() { return "lsri"; } - @Override public RegMask regmap(int i) { return arm.RMASK; } - @Override public RegMask outregmap() { return arm.WMASK; } - @Override public void encoding( Encoding enc ) { - short rd = enc.reg(this); - short rn = enc.reg(in(1)); - assert _imm > 0; - enc.add4(arm.imm_shift(arm.OPI_LSR,_imm, 0b111111, rn,rd)); - } - // General form: "lsri rd = rs1 >>> imm" - @Override public void asm(CodeGen code, SB sb) { - sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" >>> #").p(_imm); - } -} \ No newline at end of file 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 deleted file mode 100644 index e877931d..00000000 --- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/MemOpARM.java +++ /dev/null @@ -1,68 +0,0 @@ -package com.seaofnodes.simple.node.cpus.arm; - -import com.seaofnodes.simple.codegen.*; -import com.seaofnodes.simple.node.*; -import com.seaofnodes.simple.type.*; -import com.seaofnodes.simple.util.SB; -import com.seaofnodes.simple.util.Utils; -import java.lang.StringBuilder; -import java.util.BitSet; - -public abstract class MemOpARM extends MemOpNode implements MachNode { - final int _off; // Limit 9 bits sized, or (13 bits< 0 ) - enc.add4(arm.imm_inst(arm.OPI_ADD, (frameAdjust*-8)&0xFFF, arm.RSP, arm.RSP)); - enc.add4(arm.ret(arm.OP_RET)); - } -} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/SetARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/SetARM.java deleted file mode 100644 index 01b46b8d..00000000 --- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/SetARM.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.seaofnodes.simple.node.cpus.arm; - -import com.seaofnodes.simple.codegen.*; -import com.seaofnodes.simple.node.*; -import com.seaofnodes.simple.util.SB; - -// conditional set? -// e.g CSET, stores flag info into GPRS after compare -public class SetARM extends MachConcreteNode implements MachNode { - final String _bop; // One of <,<=,== - SetARM(Node cmp, String bop) { - super(cmp); - _inputs.setLen(1); // Pop the cmp inputs - // Replace with the matched cmp - _inputs.push(cmp); - _bop = bop; - } - @Override public String op() { return "set"+_bop; } - @Override public RegMask regmap(int i) { assert i==1; return arm.FLAGS_MASK; } - @Override public RegMask outregmap() { return arm.WMASK; } - - @Override public void encoding( Encoding enc ) { - int body = arm.cset(arm.OP_CSET,0b11111, arm.make_condition(_bop), 0b011111, enc.reg(this)); - enc.add4(body); - } - - @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); - } -} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/SplitARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/SplitARM.java deleted file mode 100644 index 5af45892..00000000 --- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/SplitARM.java +++ /dev/null @@ -1,65 +0,0 @@ -package com.seaofnodes.simple.node.cpus.arm; - -import com.seaofnodes.simple.codegen.*; -import com.seaofnodes.simple.node.FunNode; -import com.seaofnodes.simple.node.Node; -import com.seaofnodes.simple.node.SplitNode; -import com.seaofnodes.simple.util.SB; -import com.seaofnodes.simple.util.Utils; - -public class SplitARM extends SplitNode { - SplitARM(String kind, byte round) { super(kind,round,new Node[2]);} - @Override public RegMask regmap(int i) { return arm.SPLIT_MASK; } - @Override public RegMask outregmap() { return arm.SPLIT_MASK; } - - @Override public void encoding( Encoding enc ) { - short dst = enc.reg(this ); - short src = enc.reg(in(1)); - - boolean dstX = dst >= arm.D_OFFSET; - boolean srcX = src >= arm.D_OFFSET; - - if (dst >= arm.MAX_REG) { - // store to sp - if(src >= arm.MAX_REG) { - throw Utils.TODO(); - } - int off = enc._fun.computeStackOffset(enc._code,dst); - if( srcX ) src -= arm.D_OFFSET; - enc.add4(arm.load_str_imm(arm.OP_STORE_IMM_64, off, arm.RSP, src, 8)); - return; - } - - if(src >= arm.MAX_REG) { - // Load from SP - int off = enc._fun.computeStackOffset(enc._code,src); - if( dstX ) dst -= arm.D_OFFSET; - enc.add4(arm.load_str_imm(arm.OP_LOAD_IMM_64, off, arm.RSP, dst, 8)); - return; - } - - // pick opcode based on regs - if(!dstX && !srcX) { - // GPR->GPR - enc.add4(arm.mov_reg(arm.OP_MOV, src, dst)); - } else if(dstX && srcX) { - // FPR->FPR - // fmov reg - enc.add4(arm.f_mov_reg(arm.OP_FMOV_REG, src - arm.D_OFFSET,dst - arm.D_OFFSET)); - } else if(!srcX && dstX) { - // GPR->FPR - // FMOV(general) 64 bits to DOUBLE-PRECISION - enc.add4(arm.f_mov_general(arm.OP_FMOV, 0b01, 0, 0b111, src, dst - arm.D_OFFSET)); - } else if( srcX && !dstX) { - //FPF->GPR - // FMOV(general) DOUBLE-PRECISION to 64 bits - enc.add4(arm.f_mov_general(arm.OP_FMOV, 0b01, 0, 0b110, src - arm.D_OFFSET, dst)); - } - } - - // General form: "mov dst = src" - @Override public void asm(CodeGen code, SB sb) { - FunNode fun = code._encoding==null ? null : code._encoding._fun; - sb.p(code.reg(this,fun)).p(" = ").p(code.reg(in(1),fun)); - } -} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/StoreARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/StoreARM.java deleted file mode 100644 index 91385ac9..00000000 --- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/StoreARM.java +++ /dev/null @@ -1,43 +0,0 @@ -package com.seaofnodes.simple.node.cpus.arm; - -import com.seaofnodes.simple.codegen.*; -import com.seaofnodes.simple.node.Node; -import com.seaofnodes.simple.node.StoreNode; -import com.seaofnodes.simple.type.TypeFloat; -import com.seaofnodes.simple.util.SB; - -// Store memory addressing on ARM -// Support imm, reg(direct), or reg+off(indirect) addressing -// base - base pointer, offset is added to base -// null - index never allowed (no [reg+reg] mode) -// off - offset added to base -// imm - immediate value to store, only if val is null -// val - value to store or null - -//e.g s.cs[0] = 67; // C -// base = s.cs, off = 4, imm = 67, val = null -public class StoreARM extends MemOpARM { - StoreARM(StoreNode st, Node base, Node idx, int off, Node val) { - super(st, base, idx, off, 0, val); - } - @Override public String op() { return "st"+_sz; } - @Override public RegMask outregmap() { return null; } - - private static final int[] OP_STORES = new int[]{ arm.OP_STORE_IMM_8, arm.OP_STORE_IMM_16, arm.OP_STORE_IMM_32, arm.OP_STORE_IMM_64, }; - private int imm_op() { - return _declaredType == TypeFloat.F32 ? arm.OPF_STORE_IMM_32 - : _declaredType == TypeFloat.F64 ? arm.OPF_STORE_IMM_64 - : OP_STORES[_declaredType.log_size()]; - } - - private static final int[] OP_STORE_RS = new int[]{ arm.OP_STORE_R_8, arm.OP_STORE_R_16, arm.OP_STORE_R_32, arm.OP_STORE_R_64, }; - - @Override public void encoding( Encoding enc ) { - ldst_encode(enc, imm_op(), OP_STORE_RS[_declaredType.log_size()], val(), size()); - } - @Override public void asm(CodeGen code, SB sb) { - asm_address(code,sb).p(","); - if( val()==null ) sb.p("#").p(_imm); - else sb.p(code.reg(val())); - } -} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/SubARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/SubARM.java deleted file mode 100644 index e3b95e50..00000000 --- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/SubARM.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.seaofnodes.simple.node.cpus.arm; - -import com.seaofnodes.simple.codegen.*; -import com.seaofnodes.simple.node.*; -import com.seaofnodes.simple.util.SB; - -public class SubARM extends MachConcreteNode implements MachNode { - SubARM(Node sub) { super(sub); } - @Override public String op() { return "sub"; } - @Override public RegMask regmap(int i) { return arm.RMASK; } - @Override public RegMask outregmap() { return arm.WMASK; } - - @Override public void encoding( Encoding enc ) { arm.r_reg(enc,this,arm.OP_SUB); } - - // General form: "sub # rd = rs1 - rs2" - @Override public void asm(CodeGen code, SB sb) { - sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" - ").p(code.reg(in(2))); - } -} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/SubFARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/SubFARM.java deleted file mode 100644 index a252e542..00000000 --- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/SubFARM.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.seaofnodes.simple.node.cpus.arm; - -import com.seaofnodes.simple.codegen.*; -import com.seaofnodes.simple.node.*; -import com.seaofnodes.simple.util.SB; - -public class SubFARM extends MachConcreteNode implements MachNode { - SubFARM(Node subf) {super(subf);} - @Override public String op() { return "subf"; } - @Override public RegMask regmap(int i) { return arm.DMASK; } - @Override public RegMask outregmap() { return arm.DMASK; } - @Override public void encoding( Encoding enc ) { arm.f_scalar(enc,this,arm.OPF_SUB); } - // Default on double precision for now(64 bits) - // General form: "vsub.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))); - } -} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/SubIARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/SubIARM.java deleted file mode 100644 index b50107c9..00000000 --- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/SubIARM.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.seaofnodes.simple.node.cpus.arm; - -import com.seaofnodes.simple.codegen.*; -import com.seaofnodes.simple.node.*; -import com.seaofnodes.simple.node.MachConcreteNode; -import com.seaofnodes.simple.type.TypeInteger; -import com.seaofnodes.simple.util.SB; - -public class SubIARM extends MachConcreteNode implements MachNode { - final int _imm; - SubIARM(Node sub, int imm) { - super(sub); - _inputs.pop(); - _imm = imm; - } - @Override public String op() { return _imm == -1 ? "dec" : "subi"; } - @Override public RegMask regmap(int i) { return arm.RMASK; } - @Override public RegMask outregmap() { return arm.WMASK; } - - @Override public void encoding( Encoding enc ) { - arm.imm_inst(enc,this, in(1), arm.OPI_SUB,_imm); - } - - // General form: "subi rd = rs1 - imm" - @Override public void asm(CodeGen code, SB sb) { - sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" - #").p(_imm); - } -} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/TFPARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/TFPARM.java deleted file mode 100644 index 29d490ef..00000000 --- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/TFPARM.java +++ /dev/null @@ -1,56 +0,0 @@ -package com.seaofnodes.simple.node.cpus.arm; - -import com.seaofnodes.simple.codegen.*; -import com.seaofnodes.simple.node.ConstantNode; -import com.seaofnodes.simple.node.MachNode; -import com.seaofnodes.simple.node.Node; -import com.seaofnodes.simple.type.TypeFunPtr; -import com.seaofnodes.simple.util.SB; -import com.seaofnodes.simple.util.Utils; - -public class TFPARM extends ConstantNode implements MachNode, RIPRelSize { - TFPARM( ConstantNode con ) { super(con); } - @Override public String op() { return "ldx"; } - @Override public RegMask regmap(int i) { return null; } - @Override public RegMask outregmap() { return arm.WMASK; } - @Override public boolean isClone() { return true; } - @Override public TFPARM copy() { return new TFPARM(this); } - @Override public void encoding( Encoding enc ) { - enc.relo(this); - short self = enc.reg(this); - // adrp x0, 0 - int adrp = arm.adrp(1,0, arm.OP_ADRP, 0,self); - // add x0, x0, 0 - enc.add4(adrp); - arm.imm_inst(enc,arm.OPI_ADD,0, 0); - } - - @Override public byte encSize(int delta) { - return 8; - } - - // Delta is from opcode start - @Override public void patch( Encoding enc, int opStart, int opLen, int delta ) { - short rpc = enc.reg(this); - if(opLen == 8 ) { - // ARM encoding delta is from PC & 0xFFF - int target = opStart+delta; - int base = opStart & ~0xFFF; - delta = target-base; - int adrp_delta = delta >> 12; - // patch upper 20 bits via adrp - enc.patch4(opStart, arm.adrp(1, adrp_delta & 0b11, 0b10000, adrp_delta >> 2, rpc)); - // low 12 bits via add - enc.patch4(opStart+4, arm.imm_inst_l(arm.OPI_ADD, delta & 0xfff, rpc)); - } else { - // should not happen as one instruction is 4 byte, and TFP arm encodes 2. - throw Utils.TODO(); - } - } - - @Override public void asm(CodeGen code, SB sb) { - String reg = code.reg(this); - _con.print(sb.p(reg).p(" #")); - } - @Override public boolean eq(Node n) { return this==n; } -} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/TMPARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/TMPARM.java deleted file mode 100644 index cdcaa433..00000000 --- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/TMPARM.java +++ /dev/null @@ -1,54 +0,0 @@ -package com.seaofnodes.simple.node.cpus.arm; - -import com.seaofnodes.simple.codegen.*; -import com.seaofnodes.simple.node.ConstantNode; -import com.seaofnodes.simple.node.MachNode; -import com.seaofnodes.simple.node.Node; -import com.seaofnodes.simple.type.TypeMemPtr; -import com.seaofnodes.simple.util.SB; -import com.seaofnodes.simple.util.Utils; - -public class TMPARM extends ConstantNode implements MachNode, RIPRelSize { - TMPARM( ConstantNode con ) { super(con); } - @Override public String op() { return "ldp"; } - @Override public RegMask regmap(int i) { return null; } - @Override public RegMask outregmap() { return arm.WMASK; } - @Override public boolean isClone() { return true; } - @Override public TMPARM copy() { return new TMPARM(this); } - @Override public void encoding( Encoding enc ) { - enc.largeConstant(this,((TypeMemPtr)_con)._obj,0,-1); - short dst = enc.reg(this); - // adrp x0, 0 - enc.add4(arm.adrp(1,0, arm.OP_ADRP, 0,dst)); - // add x0, x0, 0 - arm.imm_inst(enc,arm.OPI_ADD,0, dst); - } - - @Override public byte encSize(int delta) { - return 8; - } - - // Delta is from opcode start - @Override public void patch( Encoding enc, int opStart, int opLen, int delta ) { - short dst = enc.reg(this); - if(opLen == 8 ) { - // ARM encoding delta is from PC & 0xFFF - int target = opStart+delta; - int base = opStart & ~0xFFF; - delta = target-base; - int adrp_delta = delta >> 12; - // patch upper 20 bits via adrp - enc.patch4(opStart, arm.adrp(1, adrp_delta & 0b11, 0b10000, adrp_delta >> 2, dst)); - // low 12 bits via add - enc.patch4(opStart+4, arm.imm_inst_l(arm.OPI_ADD, delta & 0xfff, dst)); - } else { - throw Utils.TODO(); - } - } - - @Override public void asm(CodeGen code, SB sb) { - String reg = code.reg(this); - _con.print(sb.p(reg).p(" #")); - } - @Override public boolean eq(Node n) { return this==n; } -} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/UJmpARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/UJmpARM.java deleted file mode 100644 index 2dd5fd6b..00000000 --- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/UJmpARM.java +++ /dev/null @@ -1,49 +0,0 @@ -package com.seaofnodes.simple.node.cpus.arm; - -import com.seaofnodes.simple.codegen.*; -import com.seaofnodes.simple.node.*; -import com.seaofnodes.simple.type.Type; -import com.seaofnodes.simple.util.SB; -import com.seaofnodes.simple.util.Utils; -import java.util.BitSet; - -// unconditional jump -public class UJmpARM extends CFGNode implements MachNode, RIPRelSize { - UJmpARM() { } - @Override public String op() { return "jmp"; } - @Override public String label() { return op(); } - @Override public StringBuilder _print1( StringBuilder sb, BitSet visited ) { - return sb.append("jmp "); - } - @Override public RegMask regmap(int i) {return null; } - @Override public RegMask outregmap() { return null; } - @Override public Type compute() { throw Utils.TODO(); } - @Override public Node idealize() { throw Utils.TODO(); } - @Override public void encoding( Encoding enc ) { - enc.jump(this,uctrl()); - int body = arm.b(arm.OP_UJMP, 0); - enc.add4(body); - } - - // Delta is from opcode start - @Override public byte encSize(int delta) { - if( -(1<<26) <= delta && delta < (1<<26) ) return 4; - // 2 word encoding needs a tmp register, must teach RA - throw Utils.TODO(); - } - - // Delta is from opcode start - @Override public void patch( Encoding enc, int opStart, int opLen, int delta ) { - if( opLen==4 ) { - enc.patch4(opStart,arm.b(arm.OP_UJMP, delta)); - } else { - throw Utils.TODO(); - } - } - - @Override public void asm(CodeGen code, SB sb) { - CFGNode target = uctrl(); - //assert target.nOuts() > 1; // Should optimize jmp to empty targets - sb.p(label(target)); - } -} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/XorARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/XorARM.java deleted file mode 100644 index 52bda021..00000000 --- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/XorARM.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.seaofnodes.simple.node.cpus.arm; - -import com.seaofnodes.simple.codegen.*; -import com.seaofnodes.simple.node.*; -import com.seaofnodes.simple.util.SB; - -public class XorARM extends MachConcreteNode implements MachNode{ - XorARM(Node xor) {super(xor);} - @Override public String op() { return "xor"; } - @Override public String glabel() { return "^"; } - @Override public RegMask regmap(int i) { return arm.RMASK; } - @Override public RegMask outregmap() { return arm.WMASK; } - @Override public void encoding( Encoding enc ) { arm.r_reg(enc,this,arm.OP_XOR); } - // General form: "rd = x1 ^ x2" - @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/XorIARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/XorIARM.java deleted file mode 100644 index 23b29644..00000000 --- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/XorIARM.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.seaofnodes.simple.node.cpus.arm; - -import com.seaofnodes.simple.codegen.*; -import com.seaofnodes.simple.node.*; -import com.seaofnodes.simple.node.MachConcreteNode; -import com.seaofnodes.simple.node.MachNode; -import com.seaofnodes.simple.util.SB; - -public class XorIARM extends MachConcreteNode implements MachNode { - final int _imm; - XorIARM(Node xor, int imm) { - super(xor); - _inputs.pop(); - _imm = imm; - } - - @Override public String op() { return "xori"; } - @Override public RegMask regmap(int i) { return arm.RMASK; } - @Override public RegMask outregmap() { return arm.WMASK; } - - // General form: "xori rd = rs1 ^ imm" - @Override public void encoding( Encoding enc ) { arm.imm_inst_n(enc,this, in(1), arm.OPI_XOR,_imm); } - @Override public void asm(CodeGen code, SB sb) { - sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" ^ #").p(_imm); - } -} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/arm.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/arm.java deleted file mode 100644 index 9dbf724d..00000000 --- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/arm.java +++ /dev/null @@ -1,816 +0,0 @@ -package com.seaofnodes.simple.node.cpus.arm; - -import com.seaofnodes.simple.codegen.*; -import com.seaofnodes.simple.node.*; -import com.seaofnodes.simple.type.*; -import com.seaofnodes.simple.util.Utils; - -public class arm extends Machine { - public arm( CodeGen code ) { - if( !"SystemV".equals(code._callingConv) ) - throw new IllegalArgumentException("Unknown calling convention "+code._callingConv); - } - - // ARM64 - @Override public String name() { return "arm"; } - - // GPR(S) - public static final int X0 = 0, X1 = 1, X2 = 2, X3 = 3, X4 = 4, X5 = 5, X6 = 6, X7 = 7; - public static final int X8 = 8, X9 = 9, X10 = 10, X11 = 11, X12 = 12, X13 = 13, X14 = 14, X15 = 15; - public static final int X16 = 16, X17 = 17, X18 = 18, X19 = 19, X20 = 20, X21 = 21, X22 = 22, X23 = 23; - public static final int X24 = 24, X25 = 25, X26 = 26, X27 = 27, X28 = 28, X29 = 29, X30 = 30, RSP = 31; - - // Floating point registers - public static final int D0 = 32, D1 = 33, D2 = 34, D3 = 35, D4 = 36, D5 = 37, D6 = 38, D7 = 39; - public static final int D8 = 40, D9 = 41, D10 = 42, D11 = 43, D12 = 44, D13 = 45, D14 = 46, D15 = 47; - public static final int D16 = 48, D17 = 49, D18 = 50, D19 = 51, D20 = 52, D21 = 53, D22 = 54, D23 = 55; - public static final int D24 = 56, D25 = 57, D26 = 58, D27 = 59, D28 = 60, D29 = 61, D30 = 62, D31 = 63; - - static final int FLAGS = 64; - static final int MAX_REG = 65; - public static final int D_OFFSET = 32; - - static final String[] REGS = new String[] { - "X0", "X1", "X2", "X3", "X4", "X5", "X6", "X7", - "X8", "X9", "X10", "X11", "X12", "X13", "X14", "X15", - "X16", "X17", "X18", "X19", "X20", "X21", "X22", "X23", - "X24", "X25", "X26", "X27", "X28", "X29", "RPC", "RSP", - "D0", "D1", "D2", "D3", "D4", "D5", "D6", "D7", - "D8", "D9", "D10", "D11", "D12", "D13", "D14", "D15", - "D16", "D17", "D18", "D19", "D20", "D21", "D22", "D23", - "D24", "D25", "D26", "D27", "D28", "D29", "D30", "D31", - "flags" - }; - @Override public String[] regs() { return REGS; } - - // from (x0-x30) - // General purpose register mask: pointers and ints, not floats - static final long RD_BITS = 0xFFFFFFFFL; - static final RegMask RMASK = new RegMask(RD_BITS); - static final long WR_BITS = 0x7FFFFFFFL; // All the GPRs, not RSP - static final RegMask WMASK = new RegMask(WR_BITS); - - // Float mask from(d0–d31) - static final long FP_BITS = 0xFFFFFFFFL<>55) == ti.value(); - } - // True if signed 12-bit immediate - private static boolean imm12(TypeInteger ti) { - // 52 = 64-12 - return ti.isConstant() && ((ti.value()<<52)>>52) == ti.value(); - } - - - // Can we encode this in ARM's 12-bit LOGICAL immediate form? - // Some combination of shifted bit-masks. - private static int imm12Logical(TypeInteger ti) { - if( !ti.isConstant() ) return -1; - if( !ti.isConstant() ) return -1; - long val = ti.value(); - if (val == 0 || val == -1) return -1; // Special cases are not allowed - int immr = 0; - // Rotate until we have 0[...]1 - - // Rotate until: - // The number is negative (MSB is 1) - // Or the LSB is not 1 - while (val < 0 || (val & 1)==0) { - // circular rotation - val = (val >>> 63) | (val << 1); - immr++; - } - // Start by assuming that val might be made of two 32-bit chunks that repeat. - int size = 32; - long pattern = val; - // Is upper half of pattern the same as the lower? - while ((pattern & ((1L<> size)) { - // Then only take one half - pattern >>= size; - size >>= 1; - } - size <<= 1; - int imms = Long.bitCount(pattern); - // Pattern should now be zeros followed by ones 0000011111 - if (pattern != (1L<=0 && imm12 >= 0; // Caller zeros high order bits - return (opcode << 22) | (imm12 << 10) | (rn << 5) | rd; - } - - public static int imm_shift(int opcode, int imm, int imms, int rn, int rd) { - assert 0 <= rn && rn < 32; - assert 0 <= rd && rd < 32; - return (opcode << 22) | (1 << 22) | (imm << 16) | (imms << 10) | (rn << 5) | rd; - } - - public static void imm_inst(Encoding enc, Node n,Node n2, int opcode, int imm12) { - short self = enc.reg(n); - short reg1 = enc.reg(n2); - - int body = imm_inst(opcode, imm12&0xFFF, reg1, self); - enc.add4(body); - } - - public static void imm_inst_subs(Encoding enc, Node n,Node n2, int opcode, int imm12) { - short reg1 = enc.reg(n2); - // 31 = 11111 - int body = imm_inst(opcode, imm12&0xFFF, reg1, 31); - enc.add4(body); - } - - // for cases where rs1 and dst are the same, eg add x0, x0, 1 - public static void imm_inst(Encoding enc, int opcode, int imm12, int self) { - int body = imm_inst(opcode, imm12&0xFFF, self, self); - enc.add4(body); - } - - public static void imm_inst_n(Encoding enc, Node n, Node n2, int opcode, int imm13) { - short self = enc.reg(n); - short reg1 = enc.reg(n2); - int body = imm_inst_n(opcode, imm13, reg1, self); - enc.add4(body); - } - - // nth bit comes from immediate and not opcode - public static int imm_inst_n(int opcode, int imm13, int rn, int rd) { - assert 0 <= rn && rn < 32; - assert 0 <= rd && rd < 32; - assert 0 <= imm13 && imm13 <= 0x1FFF; - return (opcode << 23) | (imm13 << 10) | (rn << 5) | rd; - } - - public static int imm_inst_l(int opcode, int imm12, int self) { - int body = imm_inst(opcode, imm12&0xFFF, self, self); - return body; - } - - // for cases where rs1 and dst are the same, eg add x0, x0, 1 - public static int imm_inst_l(Encoding enc, Node n, int opcode, int imm12) { - short self = enc.reg(n); - short reg1 = enc.reg(n.in(1)); - int body = imm_inst(opcode, imm12&0xFFF, reg1, self); - return body; - } - - // for normal add, reg1, reg2 cases (reg-to-reg) - // using shifted-reg form - public static int r_reg(int opcode, int shift, int rm, int imm6, int rn, int rd) { - assert 0 <= rm && rm < 32; - assert 0 <= rn && rn < 32; - assert 0 <= rd && rd < 32; - return (opcode << 24) | (shift << 21) | (rm << 16) | (imm6 << 10) | (rn << 5) | rd; - } - public static void r_reg(Encoding enc, Node n, int opcode) { - short self = enc.reg(n); - short reg1 = enc.reg(n.in(1)); - short reg2 = enc.reg(n.in(2)); - int body = r_reg(opcode, 0, reg2, 0, reg1, self >= 32 ? reg1: self); - enc.add4(body); - } - - public static void r_reg_subs(Encoding enc, Node n, int opcode) { - short reg1 = enc.reg(n.in(1)); - short reg2 = enc.reg(n.in(2)); - // 31 = 11111 - int body = r_reg(opcode, 0, reg2, 0, reg1, 31); - enc.add4(body); - } - - public static int shift_reg(int opcode, int rm, int op2, int rn, int rd) { - assert 0 <= rn && rn < 32; - assert 0 <= rm && rm < 32; - assert 0 <= rd && rd < 32; - return (opcode << 21) | (rm << 16) | (op2 << 10) | (rn << 5) | rd; - } - public static void shift_reg(Encoding enc, Node n, int op2) { - short self = enc.reg(n); - short reg1 = enc.reg(n.in(1)); - short reg2 = enc.reg(n.in(2)); - int body = shift_reg(0b10011010110, reg2, op2, reg1, self); - enc.add4(body); - } - - // MUL can be considered an alias for MADD with the third operand Ra being set to 0 - public static int madd(int opcode, int rm, int ra, int rn, int rd) { - assert 0 <= rn && rn < 32; - assert 0 <= rd && rd < 32; - assert 0 <= rm && rm < 32; - return (opcode << 21) | (rm << 16) | (ra << 10) | (rn << 5) | rd; - } - public static void madd(Encoding enc, Node n, int opcode, int ra) { - short self = enc.reg(n); - short reg1 = enc.reg(n.in(1)); - short reg2 = enc.reg(n.in(2)); - int body = madd(opcode, reg2, ra, reg1, self); - enc.add4(body); - } - - // encodes movk, movn, and movz - public static int mov(int opcode, int shift, int imm16, int rd) { - assert 0 <= rd && rd < 32; - return (opcode << 23) | (shift << 21) | (imm16 << 5) | rd; - } - - public static int mov_reg(int opcode, int src, int rd) { - assert 0 <= rd && rd < 32; - return (opcode << 21) | (src << 16) | 0b11111 << 5 | rd; - } - - public static int ret(int opcode) { - return (opcode << 10); - } - - // FMOV (scalar, immediate) - public static int f_scalar(int opcode, int ftype, int rm, int op, int rn, int rd) { - assert 0 <= rn && rn < 32; - assert 0 <= rd && rd < 32; - assert 0 <= rm && rm < 32; - return (opcode << 24) | (ftype << 22) | (1 << 21) | (rm << 16) | (op << 10) | (rn << 5) | rd; - } - - public static int f_mov_reg(int opcode, int rn, int rd) { - assert 0 <= rn && rn < 32; - assert 0 <= rd && rd < 32; - return (opcode << 24) | (0b01100000010000 << 10) | (rn << 5) | rd; - } - public static int f_mov_general(int opcode, int ftype, int rmode, int opcode1, int rn, int rd) { - assert 0 <= rn && rn < 32; - assert 0 <= rd && rd < 32; - return (opcode << 24) |(ftype << 22) | (1 << 21) | (rmode << 19) | (opcode1 << 16) | (rn << 5) | rd; - } - public static void f_scalar(Encoding enc, Node n, int op ) { - short self = (short)(enc.reg(n) -D_OFFSET); - short reg1 = (short)(enc.reg(n.in(1))-D_OFFSET); - short reg2 = (short)(enc.reg(n.in(2))-D_OFFSET); - int body = f_scalar(0b00011110, 1, reg2, op, reg1, self); - enc.add4(body); - } - - // share same encoding with LDR (literal, SIMD&FP) flavour - public static int load_pc(int opcode, int offset, int rt) { - offset>>=2; - return (opcode << 24) | (offset << 5) | rt; - } - // int l - public static int adrp(int op, int imlo,int opcode, int imhi, int rd) { - assert 0 <= rd && rd < 32; - return (op << 31) | (imlo << 29) |(opcode << 24) | (imhi << 5) | rd; - } - - // [Rptr+Roff] - public static int indr_adr(int opcode, int off, STORE_LOAD_OPTION option, int s, int ptr, int rt) { - assert 0 <= ptr && ptr < 32; - assert 0 <= rt && rt < 32; - return (opcode << 21) | (off << 16) | (option.ordinal() << 13) | (s << 12) | (2 << 10) | (ptr << 5) | rt; - } - // [Rptr+imm9] - public static int load_str_imm(int opcode, int imm12, int ptr, int rt, int size) { - assert 0 <= ptr && ptr < 32; - assert 0 <= rt && rt < 32; - - if(size == 8) imm12 = imm12 >> 3; - if(size == 4) imm12 = imm12 >> 2; - if(size == 2) imm12 = imm12 >> 1; - // size == 1 imm12 = imm12 - return (opcode << 22) | ((imm12) << 10) | (ptr << 5) | rt; - } - - // encoding for vcvt, size is encoded in operand - // , - // F32.S32 - //encoded as op = 0b00, size = 0b10. - // VCVT..

, - // opcode is broken down into 4 pieces - public static int f_convert(int opcode_1, int opcode_2, int opcode_3, int opcode_4, int vd, int vm) { - return (opcode_1 << 28) | (opcode_2 << 24) | (opcode_3 << 20) | (opcode_4 << 16) | - (vd << 12) | (0x01100010 << 4) | vm; - } - public static int float_cast(int opcode, int ftype, int rn, int rd) { - assert 0 <= rd && rd < 32; - assert 0 <= rn && rn < 32; - return (opcode << 24) | (ftype << 22) | (2176 << 10) | (rn << 5) | rd; - } - - - // ftype = 3 - public static int f_cmp(int opcode, int ftype, int rm, int rn) { - assert 0 <= rn && rn < 32; - assert 0 <= rm && rm < 32; - // Todo: |8 is not needed - return (opcode << 24) | (ftype << 21) | (rm << 16) | (8 << 10) | (rn << 5); - } - public static void f_cmp(Encoding enc, Node n) { - short reg1 = (short)(enc.reg(n.in(1))-D_OFFSET); - short reg2 = (short)(enc.reg(n.in(2))-D_OFFSET); - int body = f_cmp(0b00011110, 3, reg1, reg2); - enc.add4(body); - } - - public static COND make_condition(String bop) { - return switch (bop) { - case "==" -> COND.EQ; - case "!=" -> COND.NE; - case "<" -> COND.LT; - case "<=" -> COND.LE; - case ">=" -> COND.GE; - case ">" -> COND.GT; - default -> throw Utils.TODO(); - }; - } - - public static int b_cond(int opcode, int delta, COND cond) { - // 24-5 == 19bits offset range - assert -(1<<19) <= delta && delta < (1<<19); - assert (delta&3)==0; - delta>>=2; - delta &= (1L<<19)-1; // Zero extend - return (opcode << 24) | ((delta)<< 5) | cond.ordinal(); - } - - public static int cond_set(int opcode, int rm, COND cond, int rn, int rd) { - assert 0 <= rd && rd < 32; - assert 0 <= rn && rn < 32; - return (opcode << 21) | (rm << 16) | (cond.ordinal() << 12) | (rn << 5) | rd; - } - - // Branch with Link to Register calls a subroutine at an address in a register, setting register X30 to PC+4. - public static int blr(int opcode, int rd) { - assert 0 <= rd && rd < 32; - return opcode << 10 | rd << 5; - } - public static int b(int opcode, int delta) { - assert -(1<<26) <= delta && delta < (1<<26); - assert (delta&3)==0; - delta>>=2; - delta &= (1L<<26)-1; // Zero extend - return (opcode << 26) | delta; - } - // no aligned assert(assert (delta&3)==0;) - public static int b_calloc(int opcode, int delta) { - assert -(1<<26) <= delta && delta < (1<<26); - delta>>=2; - delta &= (1L<<26)-1; // Zero extend - return (opcode << 26) | delta; - } - - @Override public RegMask callArgMask(TypeFunPtr tfp, int idx, int maxArgSlot ) { return callInMask(tfp,idx,maxArgSlot); } - static RegMask callInMask(TypeFunPtr tfp, int idx, int maxArgSlot ) { - if( idx==0 ) return CodeGen.CODE._rpcMask; - if( idx==1 ) return null; - // Count floats in signature up to index - if( idx-2 >= tfp.nargs() ) return null; // Anti-dependence - // Count floats in signature up to index - int fcnt=0; - for( int i=2; i> 6) & 0x3F; - int imms = imm12 & 0x3F; - int size; - if ((imm12 & 0x1000) != 0) { - size = 64; - } else { - size = 31-(imms >> 1); - size |= size >> 1; - size |= size >> 2; - size |= size >> 4; - size++; - imms &= ~((32-size) << 1); - } - long val = (2L << imms)-1; - while (size < 64) { - val |= val << size; - size <<= 1; - } - val = (val >>> immr) | val << (64-immr); - return val; - } - - // Return the max stack slot used by this signature, or 0 - @Override public short maxArgSlot( TypeFunPtr tfp ) { - int icnt=0, fcnt=0; // Count of ints, floats - for( int i=0; i new AddFARM(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 CastMach(cast); - case CallEndNode cend-> new CallEndMach(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 MinusNode neg -> new NegARM(neg); - case MulFNode mulf -> new MulFARM(mulf); - case MulNode mul -> new MulARM(mul); - case NewNode nnn -> new NewARM(nnn); - case NotNode not -> new NotARM(not); - case OrNode or -> or(or); - case ParmNode parm -> new ParmARM(parm); - case PhiNode phi -> new PhiNode(phi); - case ProjNode prj -> new ProjARM(prj); - case ReadOnlyNode read -> new ReadOnlyMach(read); - case ReturnNode ret -> new RetARM(ret,ret.fun()); - case SarNode sar -> asr(sar); - case ShlNode shl -> lsl(shl); - case ShrNode shr -> lsr(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 -> new I2F8ARM(tfn); - case XorNode xor -> xor(xor); - - case LoopNode loop -> new LoopNode(loop); - case RegionNode region-> new RegionNode(region); - default -> throw Utils.TODO(); - }; - } - - private Node cmp(BoolNode bool){ - Node cmp = _cmp(bool); - return new SetARM(cmp, IfNode.negate(bool.op())); - } - private Node _cmp(BoolNode bool) { - if( bool.isFloat() ) - return new CmpFARM(bool); - return bool.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti - ? new CmpIARM(bool, (int)ti.value()) - : new CmpARM(bool); - } - - 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 - String op = "!="; - if( iff.in(1) instanceof BoolNode bool ) op = bool.op(); - else if( iff.in(1)==null ) op = "=="; // Never-node cutout - else iff.setDef(1, new BoolNode.NE(iff.in(1), new ConstantNode(TypeInteger.ZERO))); - return new BranchARM(iff, op); - } - - private Node add(AddNode add) { - return add.in(2) instanceof ConstantNode off && off._con instanceof TypeInteger ti && imm12(ti) - ? new AddIARM(add, (int)ti.value()) - : new AddARM(add); - } - - private Node sub(SubNode sub) { - return sub.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti && imm12(ti) - ? new SubIARM(sub, (int)(ti.value())) - : new SubARM(sub); - } - - 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 TMPARM(con); - case TypeNil tn -> throw Utils.TODO(); - // TOP, BOTTOM, XCtrl, Ctrl, etc. Never any executable code. - case Type t -> t==Type.NIL ? new IntARM(con) : new ConstantNode(con); - }; - } - - private Node call(CallNode call){ - return call.fptr() instanceof ConstantNode con && con._con instanceof TypeFunPtr tfp - ? new CallARM(call, tfp) - : new CallRRARM(call); - } - - private Node or(OrNode or) { - int imm12; - return or.in(2) instanceof ConstantNode off && off._con instanceof TypeInteger ti && (imm12 = imm12Logical(ti)) != -1 - ? new OrIARM(or, imm12) - : new OrARM(or); - } - - private Node xor(XorNode xor) { - int imm12; - return xor.in(2) instanceof ConstantNode off && off._con instanceof TypeInteger ti && (imm12 = imm12Logical(ti)) != -1 - ? new XorIARM(xor, imm12) - : new XorARM(xor); - } - - private Node and(AndNode and) { - int imm12; - return and.in(2) instanceof ConstantNode off && off._con instanceof TypeInteger ti && (imm12 = imm12Logical(ti)) != -1 - ? new AndIARM(and, imm12) - : new AndARM(and); - } - - private Node asr(SarNode asr) { - return asr.in(2) instanceof ConstantNode off && off._con instanceof TypeInteger ti && ti.value() >= 0 && ti.value() < 63 - ? new AsrIARM(asr, (int)ti.value()) - : new AsrARM(asr); - } - - private Node lsl(ShlNode lsl) { - return lsl.in(2) instanceof ConstantNode off && off._con instanceof TypeInteger ti && ti.value() >= 0 && ti.value() < 63 - ? new LslIARM(lsl, (int)ti.value()) - : new LslARM(lsl); - } - - private Node lsr(ShrNode lsr) { - return lsr.in(2) instanceof ConstantNode off && off._con instanceof TypeInteger ti && ti.value() >= 0 && ti.value() < 63 - ? new LsrIARM(lsr, (int)ti.value()) - : new LsrARM(lsr); - } - - - private static int off; - private static Node idx; - private Node st(StoreNode st) { - return new StoreARM(address(st),st.ptr(),idx,off,st.val()); - } - - // 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 && imm9(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/riscv/AUIPC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AUIPC.java deleted file mode 100644 index 2cd00845..00000000 --- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AUIPC.java +++ /dev/null @@ -1,42 +0,0 @@ -package com.seaofnodes.simple.node.cpus.riscv; - -import com.seaofnodes.simple.codegen.*; -import com.seaofnodes.simple.node.ConstantNode; -import com.seaofnodes.simple.node.MachNode; -import com.seaofnodes.simple.type.Type; -import com.seaofnodes.simple.util.SB; -import com.seaofnodes.simple.util.Utils; - -// Add upper 20bits to PC. Immediate comes from the relocation info. -public class AUIPC extends ConstantNode implements MachNode, RIPRelSize { - AUIPC( Type tfp ) { super(tfp); } - @Override public RegMask regmap(int i) { return null; } - @Override public RegMask outregmap() { return riscv.WMASK; } - @Override public boolean isClone() { return true; } - @Override public AUIPC copy() { return new AUIPC(_con); } - @Override public String op() { return "auipc"; } - @Override public void encoding( Encoding enc ) { - enc.relo(this); - short dst = enc.reg(this); - enc.add4(riscv.u_type(riscv.OP_AUIPC, dst, 0)); - } - - // Delta is from opcode start, but X86 measures from the end of the 5-byte encoding - @Override public byte encSize(int delta) { return 4; } - - // Delta is from opcode start - @Override public void patch( Encoding enc, int opStart, int opLen, int delta ) { - //short rpc = enc.reg(this); - //// High half is where the TFP constant used to be, the last input - //short auipc = enc.reg(in(_inputs._len-1)); - //enc.patch4(opStart,riscv.i_type(0x67, rpc, 0, auipc, delta)); - throw Utils.TODO(); - } - - @Override public void asm(CodeGen code, SB sb) { - String reg = code.reg(this); - sb.p(reg).p(" = PC+#"); - if( _con == null ) sb.p("---"); - else _con.print(sb); - } -} 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 deleted file mode 100644 index 8636df9f..00000000 --- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AddFRISC.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.seaofnodes.simple.node.cpus.riscv; - -import com.seaofnodes.simple.codegen.*; -import com.seaofnodes.simple.node.*; -import com.seaofnodes.simple.util.SB; - -// fadd.d -public class AddFRISC extends MachConcreteNode implements MachNode { - AddFRISC(Node addf) {super(addf);} - @Override public String op() { return "addf"; } - @Override public RegMask regmap(int i) { assert i==1 || i==2; return riscv.FMASK; } - @Override public RegMask outregmap() { return riscv.FMASK; } - @Override public void encoding( Encoding enc ) { riscv.rf_type(enc,this,riscv.RM.RNE,1); } - @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/riscv/AddIRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AddIRISC.java deleted file mode 100644 index 511b0583..00000000 --- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AddIRISC.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.seaofnodes.simple.node.cpus.riscv; - -import com.seaofnodes.simple.node.Node; -import com.seaofnodes.simple.util.Utils; - -public class AddIRISC extends ImmRISC { - // Used to inst-selection as a direct match against an ideal Add/Sub - public AddIRISC( Node add, int imm12, boolean pop ) { super(add,imm12,pop); } - @Override public String op() { return "addi"; } - @Override public String glabel() { return "+"; } - @Override int opcode() { return riscv.OP_IMM; } - @Override int func3() {return 0;} - @Override public AddIRISC copy() { - // Clone the AddI, using the same inputs-only code used during inst select. - // Output edges are missing. - AddIRISC add = new AddIRISC(in(1),_imm12,false); - // Copys happen when output edges should be valid, so correct missing output edges. - in(1)._outputs.add(add); - return add; - } -} 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 deleted file mode 100644 index 7010916f..00000000 --- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AddRISC.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.seaofnodes.simple.node.cpus.riscv; - -import com.seaofnodes.simple.codegen.*; -import com.seaofnodes.simple.node.*; -import com.seaofnodes.simple.util.SB; - -public class AddRISC extends MachConcreteNode implements MachNode { - public AddRISC( Node add ) { super(add); } - AddRISC( Node base, Node off ) { - super(new Node[3]); - _inputs.set(1,base); - _inputs.set(2,off ); - } - @Override public String op() { return "add"; } - @Override public RegMask regmap(int i) { return riscv.RMASK; } - @Override public RegMask outregmap() { return riscv.WMASK; } - @Override public void encoding( Encoding enc ) { riscv.r_type(enc,this,0,0); } - @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/riscv/AndIRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AndIRISC.java deleted file mode 100644 index 842c8bdc..00000000 --- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AndIRISC.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.seaofnodes.simple.node.cpus.riscv; - -import com.seaofnodes.simple.node.Node; -import com.seaofnodes.simple.util.Utils; - -public class AndIRISC extends ImmRISC { - public AndIRISC( Node and, int imm) { super(and,imm); } - @Override public String op() { return "andi"; } - @Override public String glabel() { return "&"; } - @Override int opcode() { return riscv.OP_IMM; } - @Override int func3() {return 7;} -} 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 deleted file mode 100644 index 1845985e..00000000 --- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AndRISC.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.seaofnodes.simple.node.cpus.riscv; - -import com.seaofnodes.simple.codegen.*; -import com.seaofnodes.simple.node.*; -import com.seaofnodes.simple.util.SB; - -public class AndRISC extends MachConcreteNode implements MachNode { - public AndRISC(Node and) { super(and); } - @Override public String op() { return "and"; } - @Override public RegMask regmap(int i) { return riscv.RMASK; } - @Override public RegMask outregmap() { return riscv.WMASK; } - @Override public void encoding( Encoding enc ) { riscv.r_type(enc,this,0b111,0); } - @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/riscv/BranchRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/BranchRISC.java deleted file mode 100644 index b7284265..00000000 --- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/BranchRISC.java +++ /dev/null @@ -1,93 +0,0 @@ -package com.seaofnodes.simple.node.cpus.riscv; - -import com.seaofnodes.simple.codegen.*; -import com.seaofnodes.simple.node.*; -import com.seaofnodes.simple.util.SB; -import com.seaofnodes.simple.util.Utils; -import java.util.BitSet; - -// Conditional branch such as: BEQ -public class BranchRISC extends IfNode implements MachNode, RIPRelSize { - String _bop; - // label is obtained implicitly - public BranchRISC( IfNode iff, String bop, Node n1, Node n2 ) { - super(iff); - _bop = bop; - _inputs.setX(1,n1); - _inputs.setX(2,n2); - } - - @Override public String op() { return "b"+_bop; } - @Override public String label() { return op(); } - @Override public String comment() { return "L"+cproj(1)._nid; } - @Override public RegMask regmap(int i) { return riscv.RMASK; } - @Override public RegMask outregmap() { return null; } - @Override public void negate() { - _bop = negate(_bop); - // Cannot encode ">" or "<=", so flip these - if( _bop.equals(">") || _bop.equals("<=") ) { - swap12(); // Cannot negate the test, so swap the operands - _bop = swap(_bop); // Swap test to match - } - } - - @Override public StringBuilder _print1(StringBuilder sb, BitSet visited) { - sb.append("if( "); - if( in(1)==null ) sb.append("0"); - else in(1)._print0(sb,visited); - sb.append(_bop); - if( in(2)==null ) sb.append("0"); - else in(2)._print0(sb,visited); - return sb.append(" )"); - } - - @Override public void encoding( Encoding enc ) { - if( in(1)==null && _bop=="!=" ) return; // Inverted never-node, no code - enc.jump(this,cproj(0)); - if( in(1)==null ) // Never node - enc.add4(riscv.j_type(riscv.OP_JAL, 0, 0)); - else { - // Todo: relocs (for offset - immf) - short src1 = enc.reg(in(1)); - short src2 = in(2)==null ? (short)riscv.ZERO : enc.reg(in(2)); - enc.add4(riscv.b_type(riscv.OP_BRANCH, riscv.jumpop(_bop), src1, src2, 0)); - } - } - - // Delta is from opcode start - @Override public byte encSize(int delta) { - if( in(1)==null && _bop=="!=" ) return 0; // Inverted never-node, no code - if( -4*1024 <= delta && delta < 4*1024 ) return 4; - // 2 word encoding needs a tmp register, must teach RA - throw Utils.TODO(); - } - - // Delta is from opcode start - @Override public void patch( Encoding enc, int opStart, int opLen, int delta ) { - assert !( in(1)==null && _bop=="!=" ); // Inverted never-node, no code no patch - if( in(1)==null ) { // Never node - enc.patch4(opStart,riscv.j_type(riscv.OP_JAL, 0, delta)); - } else { - short src1 = enc.reg(in(1)); - short src2 = in(2)==null ? (short)riscv.ZERO : enc.reg(in(2)); - if( opLen==4 ) { - enc.patch4(opStart,riscv.b_type(riscv.OP_BRANCH, riscv.jumpop(_bop), src1, src2, delta)); - } else { - throw Utils.TODO(); - } - } - } - - @Override public void asm(CodeGen code, SB sb) { - if( in(1)==null && _bop=="!=" ) { - sb.p("never"); - return; - } - String src1 = in(1)==null ? "#0" : code.reg(in(1)); - String src2 = in(2)==null ? "#0" : code.reg(in(2)); - sb.p(src1).p(" ").p(_bop).p(" ").p(src2).p(" "); - CFGNode prj = cproj(0).uctrlSkipEmpty(); - if( !prj.blockHead() ) prj = prj.cfg0(); - sb.p(label(prj)); - } -} 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 deleted file mode 100644 index a676ba60..00000000 --- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CallRISC.java +++ /dev/null @@ -1,63 +0,0 @@ -package com.seaofnodes.simple.node.cpus.riscv; - -import com.seaofnodes.simple.codegen.*; -import com.seaofnodes.simple.node.*; -import com.seaofnodes.simple.type.TypeFunPtr; -import com.seaofnodes.simple.util.SB; -import com.seaofnodes.simple.util.Utils; - -public class CallRISC extends CallNode implements MachNode, RIPRelSize { - final TypeFunPtr _tfp; - final String _name; - CallRISC( CallNode call, TypeFunPtr tfp ) { - super(call); - assert tfp.isConstant(); - _inputs.pop(); // Pop constant target - _tfp = tfp; - FunNode fun = CodeGen.CODE.link(tfp); - _name = fun==null ? ((ExternNode)call.fptr())._extern : fun._name; // Can be null for extern calls - } - - @Override public String op() { return "call"; } - @Override public String label() { return op(); } - @Override public String name() { return _name; } - @Override public TypeFunPtr tfp() { return _tfp; } - @Override public RegMask regmap(int i) { return riscv.callInMask(_tfp,i,fun()._maxArgSlot); } - @Override public RegMask outregmap() { return riscv.RPC_MASK; } - @Override public int nargs() { return nIns()-2; } // Minus control, memory, fptr - - @Override public void encoding( Encoding enc ) { - // Short form +/-4K: beq r0,r0,imm12 - // Long form: auipc rX,imm20/32; jal r0,[rX+imm12/32] - FunNode fun = CodeGen.CODE.link(_tfp); - if( fun==null ) enc.external(this,_name); - else enc.relo(this); - short rpc = enc.reg(this); - enc.add4(riscv.j_type(riscv.OP_JAL, rpc, 0)); - } - - // Delta is from opcode start - @Override public byte encSize(int delta) { - if( -(1L<<20) <= delta && delta < (1L<<20) ) return 4; - // 2 word encoding needs a tmp register, must teach RA - throw Utils.TODO(); - } - - // Delta is from opcode start - @Override public void patch( Encoding enc, int opStart, int opLen, int delta ) { - short rpc = enc.reg(this); - if( opLen==4 ) { - enc.patch4(opStart,riscv.j_type(riscv.OP_JAL, rpc, delta)); - } else { - throw Utils.TODO(); - } - } - - @Override public void asm(CodeGen code, SB sb) { - sb.p(_name).p(" "); - for( int i=0; i>12)); - // Load dst,[dst+#low12_constant_pool] - enc.patch4(opStart+4, riscv.i_type(riscv.OP_LOADFP, dst, 0b11, tmp, delta & 0xFFF)); - } - - @Override public void asm(CodeGen code, SB sb) { - _con.print(sb.p(code.reg(this)).p(" = #")); - } - -} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/FunRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/FunRISC.java deleted file mode 100644 index 6fce6244..00000000 --- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/FunRISC.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.seaofnodes.simple.node.cpus.riscv; - -import com.seaofnodes.simple.codegen.*; -import com.seaofnodes.simple.node.FunNode; -import com.seaofnodes.simple.node.MachNode; -import com.seaofnodes.simple.util.SB; -import com.seaofnodes.simple.util.Utils; - -public class FunRISC extends FunNode implements MachNode { - FunRISC(FunNode fun) {super(fun);} - @Override public void computeFrameAdjust(CodeGen code, int maxReg) { - super.computeFrameAdjust(code,maxReg); - if( _hasCalls ) // If non-leaf, pad to 16b - _frameAdjust = ((_frameAdjust+8) & -16); - } - @Override public void encoding( Encoding enc ) { - int sz = _frameAdjust; - if( sz == 0 ) return; // Skip if no frame adjust - if( sz >= 1L<<12 ) throw Utils.TODO(); - enc.add4(riscv.i_type(riscv.OP_IMM, riscv.RSP, 0, riscv.RSP, -sz & 0xFFF)); - } -} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/I2F8RISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/I2F8RISC.java deleted file mode 100644 index db0547fe..00000000 --- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/I2F8RISC.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.seaofnodes.simple.node.cpus.riscv; - -import com.seaofnodes.simple.codegen.*; -import com.seaofnodes.simple.node.*; -import com.seaofnodes.simple.util.SB; - -// fcvt.d.w -// Converts a 32-bit signed integer, in integer register rs1 into a double-precision floating-point number in floating-point register rd. -public class I2F8RISC extends MachConcreteNode implements MachNode { - I2F8RISC(Node i2f8) {super(i2f8);} - @Override public String op() { return "cvtf"; } - @Override public RegMask regmap(int i) { assert i==1; return riscv.RMASK; } - @Override public RegMask outregmap() { return riscv.FMASK; } - @Override public void encoding( Encoding enc ) { - short dst = (short)(enc.reg(this )-riscv.F_OFFSET); - short src1 = enc.reg(in(1)); - int body = riscv.r_type(riscv.OP_FP,dst,riscv.RM.RNE.ordinal(),src1,0,0x69); - enc.add4(body); - } - @Override public void asm(CodeGen code, SB sb) { - sb.p(code.reg(this)).p(" = ").p("(flt)").p(code.reg(in(1))); - } -} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/ImmRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/ImmRISC.java deleted file mode 100644 index f2beb25c..00000000 --- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/ImmRISC.java +++ /dev/null @@ -1,53 +0,0 @@ -package com.seaofnodes.simple.node.cpus.riscv; - -import com.seaofnodes.simple.codegen.*; -import com.seaofnodes.simple.node.*; -import com.seaofnodes.simple.type.TypeInteger; -import com.seaofnodes.simple.util.SB; -import java.util.BitSet; - -abstract public class ImmRISC extends MachConcreteNode implements MachNode { - public final int _imm12; - ImmRISC( Node add, int imm12, boolean pop ) { - super(add); - _imm12 = imm12; - // If pop, we are moving from an ideal Node to MachNode; the ideal Node - // inputs got copied here, but we are absorbing the immediate into the - // MachNode and so do not want the constant input. - if( pop ) _inputs.pop(); - // if NOT pop, we are the 2nd-half of a 2-part expansion, reset the - // input edges to no-control - else { _inputs.set(0,null); _inputs.setX(1,add); } - } - ImmRISC( Node add, int imm12 ) { this(add,imm12,true); } - @Override public RegMask regmap(int i) { return riscv.RMASK; } - @Override public RegMask outregmap() { return riscv.WMASK; } - @Override public boolean isClone() { - // Allow cloning of the 2nd half of a 2-part constant; this might - // trigger cloning the first part also. - return in(1) instanceof LUI || in(1) instanceof AUIPC; - } - - @Override public StringBuilder _print1(StringBuilder sb, BitSet visited) { - int imm12 = (_imm12<<20)>>20; // Sign extend 12 bits - in(1)._print0(sb.append("( "), visited); - return sb.append(String.format(" %s #%d )",glabel(),imm12)); - } - - abstract int opcode(); - abstract int func3(); - - @Override public void encoding( Encoding enc ) { - short dst = enc.reg(this ); - short src = enc.reg(in(1)); - enc.add4(riscv.i_type(opcode(), dst, func3(), src, _imm12 & 0xFFF)); - } - - // General form: "addi rd = rs1 + imm" - @Override public void asm(CodeGen code, SB sb) { - int imm12 = (_imm12<<20)>>20; // Sign extend 12 bits - sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" ").p(glabel()).p(" #").p(imm12); - if( in(1) instanceof LUI lui ) - sb.p(" // #").hex4((int)(((TypeInteger)lui._con).value()) + imm12); - } -} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/Int8RISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/Int8RISC.java deleted file mode 100644 index edd1850f..00000000 --- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/Int8RISC.java +++ /dev/null @@ -1,46 +0,0 @@ -package com.seaofnodes.simple.node.cpus.riscv; - -import com.seaofnodes.simple.codegen.*; -import com.seaofnodes.simple.node.ConstantNode; -import com.seaofnodes.simple.node.MachNode; -import com.seaofnodes.simple.util.SB; - -// Special instruction for loading 8 byte constants from the constant pool. - -public class Int8RISC extends ConstantNode implements MachNode, RIPRelSize { - public Int8RISC(ConstantNode con) { super(con); } - - @Override public String op() { return "ld8"; } - @Override public RegMask regmap(int i) { return null; } - @Override public RegMask outregmap() { return riscv.WMASK; } - @Override public boolean isClone() { return true; } - @Override public Int8RISC copy() { return new Int8RISC(this); } - - @Override public byte encSize(int delta) { return 8; } - - @Override public void encoding( Encoding enc ) { - short dst = enc.reg(this); - short tmp = (short)riscv.T6; - enc.largeConstant(this,_con, 0, -1); - // AUIPC dst,#hi20_constant_pool - enc.add4(riscv.u_type(riscv.OP_AUIPC, tmp, 0)); - // Load dst,[dst+#low12_constant_pool] - enc.add4(riscv.i_type(riscv.OP_LOAD, dst, 0b11, tmp, 0)); - } - @Override public RegMask killmap() { return new RegMask(riscv.T6); } - - // Delta is from opcode start - @Override public void patch( Encoding enc, int opStart, int opLen, int delta ) { - short dst = enc.reg(this); - short tmp = (short)riscv.T6; - // AUIPC dst,#hi20_constant_pool - enc.patch4(opStart , riscv.u_type(riscv.OP_AUIPC, tmp, delta>>12)); - // Load dst,[dst+#low12_constant_pool] - enc.patch4(opStart+4, riscv.i_type(riscv.OP_LOAD, dst, 0b11, tmp, delta & 0xFFF)); - } - - @Override public void asm(CodeGen code, SB sb) { - _con.print(sb.p(code.reg(this)).p(" = #")); - } - -} 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 deleted file mode 100644 index f8af913a..00000000 --- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/IntRISC.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.seaofnodes.simple.node.cpus.riscv; - -import com.seaofnodes.simple.codegen.*; -import com.seaofnodes.simple.node.ConstantNode; -import com.seaofnodes.simple.node.MachNode; -import com.seaofnodes.simple.type.Type; -import com.seaofnodes.simple.type.TypeInteger; -import com.seaofnodes.simple.util.SB; - -// 12-bit integer constant. Larger constants are made up in the instruction -// selection by adding with a LUI. -public class IntRISC extends ConstantNode implements MachNode { - public IntRISC(ConstantNode con) { super(con); } - @Override public String op() { return "ldi"; } - @Override public RegMask regmap(int i) { return null; } - @Override public RegMask outregmap() { return riscv.WMASK; } - @Override public boolean isClone() { return true; } - @Override public IntRISC copy() { return new IntRISC(this); } - @Override public void encoding( Encoding enc ) { - short dst = enc.reg(this); - int val = _con==Type.NIL ? 0 : (int)(((TypeInteger)_con).value() & 0xFFF); - // Explicit truncation of larger immediates; this will sign-extend on - // load and this is handled during instruction selection. - enc.add4(riscv.i_type(riscv.OP_IMM, dst, 0, riscv.ZERO, val)); - } - @Override public void asm(CodeGen code, SB sb) { - String reg = code.reg(this); - _con.print(sb.p(reg).p(" = #")); - } -} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/LUI.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/LUI.java deleted file mode 100644 index 7333f2b7..00000000 --- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/LUI.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.seaofnodes.simple.node.cpus.riscv; - -import com.seaofnodes.simple.codegen.*; -import com.seaofnodes.simple.node.ConstantNode; -import com.seaofnodes.simple.node.MachNode; -import com.seaofnodes.simple.type.TypeInteger; -import com.seaofnodes.simple.util.SB; - -// Load upper 20bits. -public class LUI extends ConstantNode implements MachNode { - public LUI( int imm20 ) { - super(TypeInteger.constant(imm20)); - assert (imm20 & 0xFFF)==0; - } - @Override public RegMask regmap(int i) { return null; } - @Override public RegMask outregmap() { return riscv.WMASK; } - @Override public boolean isClone() { return true; } - @Override public LUI copy() { return new LUI((int)((TypeInteger)_con).value()); } - @Override public String op() { return "lui"; } - @Override public void encoding( Encoding enc ) { - long x = ((TypeInteger)_con).value(); - int imm20 = (int)(x>>12) & 0xFFFFF; - short dst = enc.reg(this); - int lui = riscv.u_type(riscv.OP_LUI, dst, imm20); - enc.add4(lui); - } - - @Override public void asm(CodeGen code, SB sb) { - String reg = code.reg(this); - sb.p(reg).p(" = #").hex4((int)(((TypeInteger)_con).value())); - } -} 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 deleted file mode 100644 index 7e08a01d..00000000 --- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/LoadRISC.java +++ /dev/null @@ -1,29 +0,0 @@ -package com.seaofnodes.simple.node.cpus.riscv; - -import com.seaofnodes.simple.codegen.*; -import com.seaofnodes.simple.node.*; -import com.seaofnodes.simple.util.SB; - -// Load memory addressing on RISC -// Support imm, reg(direct), or reg+off(indirect) addressing -// Base = base - base pointer, offset is added to base -// idx = null -// off = off - imm12 added to base -public class LoadRISC extends MemOpRISC { - public LoadRISC(LoadNode ld, Node base, int off) { super(ld, base, off, null); } - @Override public String op() { return "ld" +_sz; } - @Override public RegMask regmap(int i) { return riscv.RMASK; } - @Override public RegMask outregmap() { return riscv.MEM_MASK; } - @Override public void encoding( Encoding enc ) { - short dst = enc.reg(this ); - short ptr = enc.reg(ptr()); - int op = dst >= riscv.F_OFFSET ? riscv.OP_LOADFP : riscv.OP_LOAD; - if( dst >= riscv.F_OFFSET ) dst -= riscv.F_OFFSET; - enc.add4(riscv.i_type(op, dst, func3(), ptr, _off)); - } - - @Override public void asm(CodeGen code, SB sb) { - sb.p(code.reg(this)).p(","); - asm_address(code,sb); - } -} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/MemOpRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/MemOpRISC.java deleted file mode 100644 index 93c90052..00000000 --- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/MemOpRISC.java +++ /dev/null @@ -1,76 +0,0 @@ -package com.seaofnodes.simple.node.cpus.riscv; - -import com.seaofnodes.simple.util.SB; -import com.seaofnodes.simple.util.Utils; -import com.seaofnodes.simple.codegen.*; -import com.seaofnodes.simple.node.*; -import com.seaofnodes.simple.type.*; -import java.util.BitSet; - -public abstract class MemOpRISC extends MemOpNode implements MachNode { - final int _off; // Limit 12 bits - final char _sz; - MemOpRISC(MemOpNode mop, Node base, int off, Node val) { - super(mop,mop); - assert base._type instanceof TypeMemPtr; - _inputs.setX(2, base ); // Base can be an Add, is no longer raw object base - _inputs.setX(3, null); // Never an index - _inputs.setX(4, val ); - _off = off; - _sz = (char)('0'+(1<<_declaredType.log_size())); - } - - @Override public String label() { return op(); } - Node val() { return in(4); } // Only for stores - - @Override public StringBuilder _printMach(StringBuilder sb, BitSet visited) { return sb.append(".").append(_name); } - - @Override public Type compute() { throw Utils.TODO(); } - @Override public Node idealize() { throw Utils.TODO(); } - - // func3 is based on load/store size and extend - int func3() { - int func3 = -1; - // no unsigned flavour for store, so both signed and unsigned trigger the same - if(this instanceof StoreRISC) { - if( _declaredType == TypeInteger. I8 || _declaredType == TypeInteger.U8 || _declaredType == TypeInteger.BOOL) func3=0; // SB - if( _declaredType == TypeInteger.I16 || _declaredType == TypeInteger.U16 ) func3=1; // SH - if( _declaredType == TypeInteger.I32 || _declaredType == TypeInteger.U32 ) func3=2; // SW - if( _declaredType instanceof TypeMemPtr) func3=3; // SD - if( _declaredType == TypeInteger.BOT ) func3=3; // SD - if( func3 == -1 ) throw Utils.TODO(); - return func3; - } - if( _declaredType == TypeInteger. I8 ) func3=0; // LB - if( _declaredType == TypeInteger.I16 ) func3=1; // LH - if( _declaredType == TypeInteger.I32 ) func3=2; // LW - if( _declaredType == TypeInteger.BOT ) func3=3; // LD - if( _declaredType == TypeInteger. U8 ) func3=4; // LBU - if( _declaredType == TypeInteger.BOOL) func3=4; // LBU - if( _declaredType == TypeInteger.U16 ) func3=5; // LHU - if( _declaredType == TypeInteger.U32 ) func3=6; // LWU - - // float - if( _declaredType == TypeFloat.F32) func3 = 2; // fLW fSW - if( _declaredType == TypeFloat.F64) func3 = 3; // fLD fSD - - if( _declaredType instanceof TypeMemPtr) func3 = 3; // 8 byte pointers (pick ld) - if( func3 == -1 ) throw Utils.TODO(); - return func3; - } - - // Register mask allowed on input i. - @Override public RegMask regmap(int i) { - // 0 - ctrl - // 1 - memory - if( i==2 ) return riscv.RMASK; // base - // 2 - index - if( i==4 ) return riscv.MEM_MASK; // value - throw Utils.TODO(); - } - - SB asm_address(CodeGen code, SB sb) { - sb.p("[").p(code.reg(ptr())).p("+"); - return sb.p(_off).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 deleted file mode 100644 index 5cc40079..00000000 --- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/MulFRISC.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.seaofnodes.simple.node.cpus.riscv; - -import com.seaofnodes.simple.codegen.*; -import com.seaofnodes.simple.node.*; -import com.seaofnodes.simple.util.SB; - -// fmul.d -public class MulFRISC extends MachConcreteNode implements MachNode { - MulFRISC(Node mulf) {super(mulf);} - @Override public String op() { return "mulf"; } - @Override public RegMask regmap(int i) { assert i==1 || i==2; return riscv.FMASK; } - @Override public RegMask outregmap() { return riscv.FMASK; } - @Override public void encoding( Encoding enc ) { riscv.rf_type(enc,this,riscv.RM.RNE,9); } - @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/riscv/MulRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/MulRISC.java deleted file mode 100644 index 9cb9ace7..00000000 --- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/MulRISC.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.seaofnodes.simple.node.cpus.riscv; - -import com.seaofnodes.simple.codegen.*; -import com.seaofnodes.simple.node.*; -import com.seaofnodes.simple.util.SB; - -// mulh signed multiply instruction(no-imm form) -public class MulRISC extends MachConcreteNode implements MachNode{ - public MulRISC(Node mul) {super(mul);} - @Override public String op() { return "mul"; } - @Override public RegMask regmap(int i) { assert i==1 || i==2; return riscv.RMASK; } - @Override public RegMask outregmap() { return riscv.WMASK; } - @Override public void encoding( Encoding enc ) { riscv.r_type(enc,this,0,1); } - @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/riscv/NegRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/NegRISC.java deleted file mode 100644 index d8183d09..00000000 --- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/NegRISC.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.seaofnodes.simple.node.cpus.riscv; - -import com.seaofnodes.simple.codegen.*; -import com.seaofnodes.simple.node.*; -import com.seaofnodes.simple.util.SB; - -public class NegRISC extends MachConcreteNode implements MachNode { - public NegRISC( Node neg ) { super(neg); } - @Override public String op() { return "neg"; } - @Override public RegMask regmap(int i) { assert i==1; return riscv.RMASK; } - @Override public RegMask outregmap() { return riscv.WMASK; } - @Override public void encoding( Encoding enc ) { - short dst = enc.reg(this ); - short src = enc.reg(in(1)); - riscv.r_type(riscv.OP,dst,0,0,src,0x20); - } - @Override public void asm(CodeGen code, SB sb) { - sb.p(code.reg(this)).p(" = -").p(code.reg(in(1))); - } -} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/NewRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/NewRISC.java deleted file mode 100644 index 6c03bda6..00000000 --- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/NewRISC.java +++ /dev/null @@ -1,47 +0,0 @@ -package com.seaofnodes.simple.node.cpus.riscv; - -import com.seaofnodes.simple.codegen.*; -import com.seaofnodes.simple.node.*; -import com.seaofnodes.simple.print.ASMPrinter; -import com.seaofnodes.simple.util.SB; -import com.seaofnodes.simple.util.Utils; - -public class NewRISC extends NewNode implements MachNode, RIPRelSize { - // A pre-zeroed chunk of memory. - NewRISC( NewNode nnn ) { super(nnn); } - @Override public void encoding( Encoding enc ) { - // Generic external encoding; 2 ops. - enc.external(this,"calloc"); - // A1 is a caller-save, allowed to crush building external address - // auipc - enc.add4(riscv.i_type(riscv.OP_IMM , _arg2Reg , 0, riscv.ZERO, 1)); - enc.add4(riscv.u_type(riscv.OP_AUIPC, riscv.A2 , 0)); - enc.add4(riscv.i_type(riscv.OP_JALR , riscv.RPC, 0, riscv.A2, 0)); - } - - // Patch is for running "new" in a JIT. - // Delta is from opcode start - @Override public byte encSize(int delta) { return 4*3; } - - // Patch is for running "new" in a JIT. - // Delta is from opcode start - @Override public void patch( Encoding enc, int opStart, int opLen, int delta ) { - // Negative patches are JIT emulator targets. - if( opStart+delta < 0 ) { - delta -= 4; // Offset from the AUIPC start - int imm20 = delta>>12; - if( ((delta>>11)&1)==1 ) imm20++; // Correct accidental sign extension - int imm12 = delta&0xFFF; - enc.patch4(opStart+4,riscv.u_type(riscv.OP_AUIPC, riscv.A2 , imm20)); - enc.patch4(opStart+8,riscv.i_type(riscv.OP_JALR , riscv.RPC, 0, riscv.A2, imm12)); - } else - throw Utils.TODO(); - } - - // General form: "alloc #bytes PC" - @Override public void asm(CodeGen code, SB sb) { - sb.p("ldi a1=#1\n"); - sb.p("auipc a2=#calloc\n"); - sb.p("call a2+#calloc, a0"); - } -} 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 deleted file mode 100644 index 1369ee76..00000000 --- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/NotRISC.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.seaofnodes.simple.node.cpus.riscv; - -import com.seaofnodes.simple.codegen.*; -import com.seaofnodes.simple.node.MachConcreteNode; -import com.seaofnodes.simple.node.MachNode; -import com.seaofnodes.simple.node.NotNode; -import com.seaofnodes.simple.util.SB; - -public class NotRISC extends MachConcreteNode implements MachNode { - public NotRISC(NotNode not) { super(not); } - @Override public String op() { return "not"; } - @Override public RegMask regmap(int i) { return riscv.RMASK; } - @Override public RegMask outregmap() { return riscv.WMASK; } - @Override public void encoding( Encoding enc ) { - short dst = enc.reg(this ); - short src = enc.reg(in(1)); - enc.add4(riscv.i_type(riscv.OP_IMM, dst, 2, src, 1)); - } - @Override public void asm(CodeGen code, SB sb) { sb.p(code.reg(this)); } -} 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 deleted file mode 100644 index 963b9d50..00000000 --- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/OrIRISC.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.seaofnodes.simple.node.cpus.riscv; - -import com.seaofnodes.simple.node.Node; -import com.seaofnodes.simple.util.Utils; - -public class OrIRISC extends ImmRISC { - public OrIRISC( Node and, int imm) { super(and,imm); } - @Override int opcode() { return riscv.OP_IMM; } - @Override int func3() { return 6; } - @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 deleted file mode 100644 index a310a1f4..00000000 --- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/OrRISC.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.seaofnodes.simple.node.cpus.riscv; - -import com.seaofnodes.simple.codegen.*; -import com.seaofnodes.simple.node.*; -import com.seaofnodes.simple.util.SB; - -public class OrRISC extends MachConcreteNode implements MachNode { - public OrRISC(Node or) { super(or); } - @Override public String op() { return "or"; } - @Override public RegMask regmap(int i) { return riscv.RMASK; } - @Override public RegMask outregmap() { return riscv.WMASK; } - @Override public void encoding( Encoding enc ) { riscv.r_type(enc,this,6,0); } - @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/riscv/ParmRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/ParmRISC.java deleted file mode 100644 index 0f8db4e5..00000000 --- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/ParmRISC.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.seaofnodes.simple.node.cpus.riscv; - -import com.seaofnodes.simple.codegen.Encoding; -import com.seaofnodes.simple.codegen.RegMask; -import com.seaofnodes.simple.node.MachNode; -import com.seaofnodes.simple.node.ParmNode; - -public class ParmRISC extends ParmNode implements MachNode { - final RegMask _rmask; - ParmRISC(ParmNode parm) { - super(parm); - _rmask = riscv.callInMask(fun().sig(),_idx,0); - } - @Override public RegMask regmap(int i) { return null; } - @Override public RegMask outregmap() { return _rmask; } - @Override public void encoding( Encoding enc ) { } -} 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 deleted file mode 100644 index 2f9ac469..00000000 --- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/ProjRISC.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.seaofnodes.simple.node.cpus.riscv; - -import com.seaofnodes.simple.codegen.Encoding; -import com.seaofnodes.simple.codegen.RegMask; -import com.seaofnodes.simple.node.MachNode; -import com.seaofnodes.simple.node.ProjNode; - -public class ProjRISC extends ProjNode implements MachNode { - ProjRISC(ProjNode p) { super(p); } - @Override public RegMask regmap(int i) { return null; } - @Override public RegMask outregmap() { - return ((MachNode)in(0)).outregmap(_idx); - } - @Override public void encoding( Encoding enc ) { } -} 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 deleted file mode 100644 index 7e4631d3..00000000 --- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/RetRISC.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.seaofnodes.simple.node.cpus.riscv; - -import com.seaofnodes.simple.codegen.CodeGen; -import com.seaofnodes.simple.codegen.Encoding; -import com.seaofnodes.simple.codegen.RegMask; -import com.seaofnodes.simple.node.FunNode; -import com.seaofnodes.simple.node.MachNode; -import com.seaofnodes.simple.node.ReturnNode; -import com.seaofnodes.simple.util.Utils; - -public class RetRISC extends ReturnNode implements MachNode { - public RetRISC(ReturnNode ret, FunNode fun) { super(ret, fun); fun.setRet(this); } - @Override public void encoding( Encoding enc ) { - int sz = fun()._frameAdjust; - if( sz >= 1L<<12 ) throw Utils.TODO(); - if( sz != 0 ) - enc.add4(riscv.i_type(riscv.OP_IMM, riscv.RSP, 0, riscv.RSP, sz & 0xFFF)); - short rpc = enc.reg(rpc()); - enc.add4(riscv.i_type(riscv.OP_JALR, riscv.ZERO, 0, rpc, 0)); - } -} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SetFRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SetFRISC.java deleted file mode 100644 index 3424cd01..00000000 --- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SetFRISC.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.seaofnodes.simple.node.cpus.riscv; - -import com.seaofnodes.simple.codegen.*; -import com.seaofnodes.simple.node.BoolNode; -import com.seaofnodes.simple.node.MachConcreteNode; -import com.seaofnodes.simple.node.MachNode; -import com.seaofnodes.simple.util.SB; - -// corresponds to feq.d = equal -// fle.d = less than or equal -// flt.d = less than -public class SetFRISC extends MachConcreteNode implements MachNode { - final String _bop; // One of <,<=,== - SetFRISC( BoolNode bool ) { - super(bool); - assert bool.isFloat(); - _bop = bool.op(); - } - @Override public String op() { return "set"+_bop; } - @Override public RegMask regmap(int i) { return riscv.FMASK; } - @Override public RegMask outregmap() { return riscv.WMASK; } - @Override public void encoding( Encoding enc ) { - short dst = enc.reg(this ); - short src1 = (short)(enc.reg(in(1))-riscv.F_OFFSET); - short src2 = (short)(enc.reg(in(2))-riscv.F_OFFSET); - int body = riscv.r_type(riscv.OP_FP,dst,riscv.fsetop(_bop),src1,src2,0b1010001/*FCMP*/); - enc.add4(body); - } - - @Override public void asm(CodeGen code, SB sb) { - sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" ").p(_bop).p(" ").p(code.reg(in(2))); - } -} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SetIRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SetIRISC.java deleted file mode 100644 index 690e2102..00000000 --- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SetIRISC.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.seaofnodes.simple.node.cpus.riscv; - -import com.seaofnodes.simple.util.Utils; -import com.seaofnodes.simple.node.Node; - -// corresponds to slti,sltiu -public class SetIRISC extends ImmRISC { - final boolean _unsigned; // slti vs sltiu - public SetIRISC( Node src, int imm12, boolean unsigned ) { - super(src,imm12); - _unsigned = unsigned; - } - @Override public String op() { return "slt" + (_unsigned ? "u":"") + "i"; } - @Override public String glabel() { return "<"+ (_unsigned ? "u":""); } - @Override int opcode() { return 0b0010011; } - @Override int func3() { return _unsigned ? 0b011 : 0b010; } -} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SetRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SetRISC.java deleted file mode 100644 index 848cc59f..00000000 --- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SetRISC.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.seaofnodes.simple.node.cpus.riscv; - -import com.seaofnodes.simple.codegen.*; -import com.seaofnodes.simple.node.*; -import com.seaofnodes.simple.util.SB; - -// corresponds to slt sltu -public class SetRISC extends MachConcreteNode implements MachNode { - final boolean _unsigned; - public SetRISC( Node cmp, boolean unsigned ) { super(cmp); _unsigned=unsigned; } - @Override public String op() { return "slt" + (_unsigned ? "u":""); } - @Override public RegMask regmap(int i) { return riscv.RMASK; } - @Override public RegMask outregmap() { return riscv.WMASK; } - @Override public void encoding( Encoding enc ) { - riscv.r_type(enc,this,_unsigned ? 0b011 : 0b010,0); - } - @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))); - } -} \ No newline at end of file diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SllIRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SllIRISC.java deleted file mode 100644 index 12c0ddec..00000000 --- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SllIRISC.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.seaofnodes.simple.node.cpus.riscv; - -import com.seaofnodes.simple.node.Node; -import com.seaofnodes.simple.util.Utils; - -public class SllIRISC extends ImmRISC { - public SllIRISC( Node and, int imm) { super(and,imm); } - @Override int opcode() { return riscv.OP_IMM; } - @Override int func3() { return 1; } - @Override public String glabel() { return "<<"; } - @Override public String op() { return "slli"; } -} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SllRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SllRISC.java deleted file mode 100644 index 5a4d4cc6..00000000 --- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SllRISC.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.seaofnodes.simple.node.cpus.riscv; - -import com.seaofnodes.simple.codegen.*; -import com.seaofnodes.simple.node.*; -import com.seaofnodes.simple.util.SB; - -// Shift left logical -public class SllRISC extends MachConcreteNode implements MachNode { - public SllRISC(Node sll) { super(sll); } - @Override public String op() { return "shl"; } - @Override public RegMask regmap(int i) { return riscv.RMASK; } - @Override public RegMask outregmap() { return riscv.WMASK; } - @Override public void encoding( Encoding enc ) { riscv.r_type(enc,this,1,0); } - @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/riscv/SplitRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SplitRISC.java deleted file mode 100644 index 6082cbbf..00000000 --- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SplitRISC.java +++ /dev/null @@ -1,66 +0,0 @@ -package com.seaofnodes.simple.node.cpus.riscv; - -import com.seaofnodes.simple.codegen.*; -import com.seaofnodes.simple.node.FunNode; -import com.seaofnodes.simple.node.Node; -import com.seaofnodes.simple.node.SplitNode; -import com.seaofnodes.simple.util.SB; -import com.seaofnodes.simple.util.Utils; - -public class SplitRISC extends SplitNode { - SplitRISC( String kind, byte round ) { super(kind,round, new Node[2]); } - @Override public RegMask regmap(int i) { return riscv.SPLIT_MASK; } - @Override public RegMask outregmap() { return riscv.SPLIT_MASK; } - @Override public void encoding( Encoding enc ) { - short dst = enc.reg(this ); - short src = enc.reg(in(1)); - - // Flag for being either XMM or stack - boolean dstX = dst >= riscv.F_OFFSET; - boolean srcX = src >= riscv.F_OFFSET; - - if( dst >= riscv.MAX_REG ) { - // Store to SP - if( src >= riscv.MAX_REG ) { - throw Utils.TODO(); // Very rare stack-stack move - } - int off = enc._fun.computeStackOffset(enc._code,dst); - int op = srcX ? riscv.OP_STOREFP : riscv.OP_STORE; - if( srcX ) src -= riscv.F_OFFSET; - enc.add4(riscv.s_type(op, 0b011, riscv.RSP, src, off)); - return; - } - if( src >= riscv.MAX_REG ) { - // Load from SP - int off = enc._fun.computeStackOffset(enc._code,src); - int op = dstX ? riscv.OP_LOADFP : riscv.OP_LOAD; - if( dstX ) dst -= riscv.F_OFFSET; - enc.add4(riscv.i_type(op, dst, 0b011, riscv.RSP, off)); - return; - } - // pick opcode based on regs - if( !dstX && !srcX ) { - // GPR->GPR - enc.add4(riscv.r_type(riscv.OP,dst,0,src,riscv.ZERO,0)); - } else if( dstX && srcX ) { - // FPR->FPR - src -= riscv.F_OFFSET; - dst -= riscv.F_OFFSET; - enc.add4(riscv.r_type(riscv.OP_FP, dst, 0b000, src, src, 0b0010101)); // dst = FMIN(src,src) - } else if(!srcX && dstX) { - //GPR->FPR - // fmv.d.x - enc.add4(riscv.r_type(riscv.OP_FP, dst - riscv.F_OFFSET, 0, src, 0, 0b1111001)); - } else if(srcX && !dstX) { - //FPR->GPR - //fmv.x.d - enc.add4(riscv.r_type(riscv.OP_FP, dst, 0, src - riscv.F_OFFSET, 0, 0b1110001)); - } - } - - // General form: "mov dst = src" - @Override public void asm(CodeGen code, SB sb) { - FunNode fun = code._encoding==null ? null : code._encoding._fun; - sb.p(code.reg(this,fun)).p(" = ").p(code.reg(in(1),fun)); - } -} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SraIRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SraIRISC.java deleted file mode 100644 index fcfa0774..00000000 --- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SraIRISC.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.seaofnodes.simple.node.cpus.riscv; - -import com.seaofnodes.simple.node.Node; -import com.seaofnodes.simple.util.Utils; - -public class SraIRISC extends ImmRISC { - public SraIRISC( Node and, int imm) { - super(and,imm | (0x20 << 5)); - } - @Override int opcode() { return riscv.OP_IMM; } - @Override int func3() { return 5;} - @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 deleted file mode 100644 index e2e34415..00000000 --- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SraRISC.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.seaofnodes.simple.node.cpus.riscv; - -import com.seaofnodes.simple.codegen.*; -import com.seaofnodes.simple.node.*; -import com.seaofnodes.simple.util.SB; - -// Right Shift Arithmetic -public class SraRISC extends MachConcreteNode implements MachNode { - public SraRISC(Node sra) { super(sra); } - @Override public String op() { return "sar"; } - @Override public RegMask regmap(int i) { return riscv.RMASK; } - @Override public RegMask outregmap() { return riscv.WMASK; } - @Override public void encoding( Encoding enc ) { riscv.r_type(enc,this,0b101,0b00100000); } - @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/riscv/SrlIRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SrlIRISC.java deleted file mode 100644 index 2f73e139..00000000 --- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SrlIRISC.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.seaofnodes.simple.node.cpus.riscv; - -import com.seaofnodes.simple.node.Node; -import com.seaofnodes.simple.util.Utils; - -public class SrlIRISC extends ImmRISC { - public SrlIRISC( Node and, int imm, boolean pop) { super(and,imm,pop); } - @Override int opcode() { return riscv.OP_IMM; } - @Override int func3() { return 5; } - @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 deleted file mode 100644 index 1eccc1c8..00000000 --- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SrlRISC.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.seaofnodes.simple.node.cpus.riscv; - -import com.seaofnodes.simple.codegen.*; -import com.seaofnodes.simple.node.*; -import com.seaofnodes.simple.util.SB; - -// Right Shift Logical -public class SrlRISC extends MachConcreteNode implements MachNode { - public SrlRISC(Node srli) { super(srli); } - @Override public String op() { return "shr"; } - @Override public RegMask regmap(int i) { return riscv.RMASK; } - @Override public RegMask outregmap() { return riscv.WMASK; } - @Override public void encoding( Encoding enc ) { riscv.r_type(enc,this,7,0); } - @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/riscv/StoreRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/StoreRISC.java deleted file mode 100644 index d06c0c35..00000000 --- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/StoreRISC.java +++ /dev/null @@ -1,47 +0,0 @@ -package com.seaofnodes.simple.node.cpus.riscv; - -import com.seaofnodes.simple.codegen.*; -import com.seaofnodes.simple.codegen.RegMask; -import com.seaofnodes.simple.node.*; -import com.seaofnodes.simple.util.SB; -import com.seaofnodes.simple.util.Utils; - -// Store memory addressing on ARM -// Support imm, reg(direct), or reg+off(indirect) addressing -// 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, Node base, int off, Node val ) { super(st, base, off, val); } - @Override public String op() { return "st"+_sz; } - @Override public RegMask regmap(int i) { - // 0 - ctrl - if( i==1 ) return null; // mem - if( i==2 ) return riscv.RMASK; // ptr - // 2 - index - if( i==4 ) return riscv.MEM_MASK; // Wide mask to store GPR and FPR - return null; // Anti-dependence - } - @Override public RegMask outregmap() { return null; } - - @Override public void encoding( Encoding enc ) { - short val = enc.reg(val()); - short ptr = enc.reg(ptr()); - int op = val >= riscv.F_OFFSET ? riscv.OP_STOREFP : riscv.OP_STORE; - if( val >= riscv.F_OFFSET ) val -= riscv.F_OFFSET; - enc.add4(riscv.s_type(op, func3()&7, ptr, val == -1 ? riscv.ZERO : val, _off)); - } - - @Override public void asm(CodeGen code, SB sb) { - asm_address(code,sb).p(","); - if( val()==null ) sb.p("#").p("0"); - else sb.p(code.reg(val())); - } -} 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 deleted file mode 100644 index da3e92f4..00000000 --- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SubFRISC.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.seaofnodes.simple.node.cpus.riscv; - -import com.seaofnodes.simple.codegen.*; -import com.seaofnodes.simple.node.*; -import com.seaofnodes.simple.util.SB; - -// fsub.d -public class SubFRISC extends MachConcreteNode implements MachNode { - SubFRISC(Node subf) {super(subf);} - @Override public String op() { return "subf"; } - @Override public RegMask regmap(int i) { assert i==1 || i==2; return riscv.FMASK; } - @Override public RegMask outregmap() { return riscv.FMASK; } - @Override public void encoding( Encoding enc ) { riscv.rf_type(enc,this,riscv.RM.RNE,5); } - @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/riscv/SubRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SubRISC.java deleted file mode 100644 index 2adfce23..00000000 --- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SubRISC.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.seaofnodes.simple.node.cpus.riscv; - -import com.seaofnodes.simple.codegen.*; -import com.seaofnodes.simple.node.*; -import com.seaofnodes.simple.util.SB; - -public class SubRISC extends MachConcreteNode implements MachNode { - public SubRISC( Node sub ) { super(sub); } - @Override public String op() { return "sub"; } - @Override public RegMask regmap(int i) { assert i==1 || i==2; return riscv.RMASK; } - @Override public RegMask outregmap() { return riscv.WMASK; } - @Override public void encoding( Encoding enc ) { riscv.r_type(enc,this,0,0x20); } - @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/riscv/TFPRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/TFPRISC.java deleted file mode 100644 index 7e057507..00000000 --- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/TFPRISC.java +++ /dev/null @@ -1,55 +0,0 @@ -package com.seaofnodes.simple.node.cpus.riscv; - -import com.seaofnodes.simple.codegen.*; -import com.seaofnodes.simple.node.ConstantNode; -import com.seaofnodes.simple.node.MachNode; -import com.seaofnodes.simple.node.Node; -import com.seaofnodes.simple.type.TypeFunPtr; -import com.seaofnodes.simple.util.SB; -import com.seaofnodes.simple.util.Utils; - -public class TFPRISC extends ConstantNode implements MachNode, RIPRelSize { - TFPRISC(ConstantNode con) { super(con); } - @Override public String op() { return "ldx"; } - @Override public RegMask regmap(int i) { return null; } - @Override public RegMask outregmap() { return riscv.WMASK; } - @Override public boolean isClone() { return true; } - @Override public TFPRISC copy() { return new TFPRISC(this); } - @Override public void encoding( Encoding enc ) { - enc.relo(this); - // TODO: 1 op encoding, plus a TODO if it does not fit - short dst = enc.reg(this); - TypeFunPtr tfp = (TypeFunPtr)_con; - // auipc t0,0 - enc.add4(riscv.u_type(riscv.OP_AUIPC, dst, 0)); - // addi t1,t0 + #0 - enc.add4(riscv.i_type(riscv.OP_IMM, dst, 0, dst, 0)); - } - - @Override public byte encSize(int delta) { - if( -(1L<<11) <= delta && delta < (1L<<11) ) return 4; - throw Utils.TODO(); - } - - // Delta is from opcode start - @Override public void patch( Encoding enc, int opStart, int opLen, int delta ) { - short rpc = enc.reg(this); - if( opLen==8 ) { - // AUIPC (upper 20 bits) - // opstart of add - int next = opStart + opLen; - enc.patch4(opStart,riscv.u_type(riscv.OP_AUIPC, rpc, delta>>12)); - // addi(low 12 bits) - enc.patch4(next,riscv.i_type(riscv.OP_IMM, rpc, 0, rpc, delta & 0xFFF)); - // addi - } else { - // should not happen as one instruction is 4 byte, and TFP arm encodes 2. - throw Utils.TODO(); - } - } - - @Override public void asm(CodeGen code, SB sb) { - _con.print(sb.p(code.reg(this)).p(" #")); - } - @Override public boolean eq(Node n) { return this==n; } -} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/TMPRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/TMPRISC.java deleted file mode 100644 index 44e73d1d..00000000 --- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/TMPRISC.java +++ /dev/null @@ -1,41 +0,0 @@ -package com.seaofnodes.simple.node.cpus.riscv; - -import com.seaofnodes.simple.codegen.*; -import com.seaofnodes.simple.node.ConstantNode; -import com.seaofnodes.simple.node.MachNode; -import com.seaofnodes.simple.node.Node; -import com.seaofnodes.simple.type.TypeMemPtr; -import com.seaofnodes.simple.util.SB; - -public class TMPRISC extends ConstantNode implements MachNode, RIPRelSize { - TMPRISC(ConstantNode con) { super(con); } - @Override public String op() { return "ldp"; } - @Override public RegMask regmap(int i) { return null; } - @Override public RegMask outregmap() { return riscv.WMASK; } - @Override public boolean isClone() { return true; } - @Override public TMPRISC copy() { return new TMPRISC(this); } - @Override public void encoding( Encoding enc ) { - enc.largeConstant(this,((TypeMemPtr)_con)._obj,0,-1); - short dst = enc.reg(this); - // AUIPC dst,#hi20_constant_pool - enc.add4(riscv.u_type(riscv.OP_AUIPC, dst, 0)); - // addi dst,[dst+#low12_constant_pool] - enc.add4(riscv.i_type(riscv.OP_IMM, dst, 0, dst, 0)); - } - - @Override public byte encSize(int delta) { return 8; } - - // Delta is from opcode start - @Override public void patch( Encoding enc, int opStart, int opLen, int delta ) { - short dst = enc.reg(this); - // AUIPC dst,#hi20_constant_pool - enc.patch4(opStart , riscv.u_type(riscv.OP_AUIPC, dst, delta>>12)); - // Load dst,[dst+#low12_constant_pool] - enc.patch4(opStart+4, riscv.i_type(riscv.OP_IMM, dst, 0, dst, delta & 0xFFF)); - } - - @Override public void asm(CodeGen code, SB sb) { - _con.print(sb.p(code.reg(this)).p(" #")); - } - @Override public boolean eq(Node n) { return this==n; } -} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/UJmpRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/UJmpRISC.java deleted file mode 100644 index c4d5d8af..00000000 --- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/UJmpRISC.java +++ /dev/null @@ -1,47 +0,0 @@ -package com.seaofnodes.simple.node.cpus.riscv; - -import com.seaofnodes.simple.codegen.*; -import com.seaofnodes.simple.node.*; -import com.seaofnodes.simple.type.Type; -import com.seaofnodes.simple.util.SB; -import com.seaofnodes.simple.util.Utils; -import java.util.BitSet; - -// unconditional jump -public class UJmpRISC extends CFGNode implements MachNode, RIPRelSize { - @Override public String op() { return "jmp"; } - @Override public String label() { return op(); } - @Override public StringBuilder _print1( StringBuilder sb, BitSet visited ) { return sb.append("jmp "); } - @Override public RegMask regmap(int i) {return null; } - @Override public RegMask outregmap() { return null; } - @Override public Type compute() { throw Utils.TODO(); } - @Override public Node idealize() { throw Utils.TODO(); } - @Override public void encoding( Encoding enc ) { - // Short form +/-4K: beq r0,r0,imm12 - // Long form: auipc rX,imm20/32; jal r0,[rX+imm12/32] - enc.jump(this,uctrl()); - enc.add4(riscv.j_type(riscv.OP_JAL, 0, 0)); - } - - // Delta is from opcode start - @Override public byte encSize(int delta) { - if( -(1L<<20) <= delta && delta < (1L<<20) ) return 4; - // 2 word encoding needs a tmp register, must teach RA - throw Utils.TODO(); - } - - // Delta is from opcode start - @Override public void patch( Encoding enc, int opStart, int opLen, int delta ) { - if( opLen==4 ) { - enc.patch4(opStart,riscv.j_type(riscv.OP_JAL, 0, delta)); - } else { - throw Utils.TODO(); - } - } - - @Override public void asm(CodeGen code, SB sb) { - CFGNode target = uctrl(); - //assert target.nOuts() > 1; // Should optimize jmp to empty targets - sb.p(label(target)); - } -} 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 deleted file mode 100644 index 19ea6bc7..00000000 --- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/XorIRISC.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.seaofnodes.simple.node.cpus.riscv; - -import com.seaofnodes.simple.node.Node; -import com.seaofnodes.simple.util.Utils; - -public class XorIRISC extends ImmRISC { - public XorIRISC( Node and, int imm) { super(and,imm); } - public XorIRISC( Node and, int imm, boolean pop) { super(and,imm,pop); } - @Override int opcode() { return riscv.OP_IMM; } - @Override int func3() { return 4; } - @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 deleted file mode 100644 index 413a7c3b..00000000 --- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/XorRISC.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.seaofnodes.simple.node.cpus.riscv; - -import com.seaofnodes.simple.codegen.*; -import com.seaofnodes.simple.node.*; -import com.seaofnodes.simple.util.SB; - -public class XorRISC extends MachConcreteNode implements MachNode { - public XorRISC(Node or) { super(or); } - @Override public String op() { return "xor"; } - @Override public RegMask regmap(int i) { return riscv.RMASK; } - @Override public RegMask outregmap() { return riscv.WMASK; } - @Override public void encoding( Encoding enc ) { riscv.r_type(enc,this,4,0); } - @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/riscv/riscv.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/riscv.java deleted file mode 100644 index 7a6361bc..00000000 --- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/riscv.java +++ /dev/null @@ -1,543 +0,0 @@ -package com.seaofnodes.simple.node.cpus.riscv; - -import com.seaofnodes.simple.codegen.*; -import com.seaofnodes.simple.node.*; -import com.seaofnodes.simple.type.*; -import com.seaofnodes.simple.util.Utils; - -public class riscv extends Machine { - - public riscv(CodeGen code) { - if( !"SystemV".equals(code._callingConv) ) - throw new IllegalArgumentException("Unknown calling convention "+code._callingConv); - } - - @Override public String name() {return "riscv";} - //@Override public int maxReg() { return MAX_REG; } - - // Using ABI names instead of register names - public static int ZERO = 0, RPC= 1, RSP= 2, S12= 3, S13= 4, T0 = 5, T1 = 6, T2 = 7; - public static int S0 = 8, S1 = 9, A0 = 10, A1 = 11, A2 = 12, A3 = 13, A4 = 14, A5 = 15; - public static int A6 = 16, A7 = 17, S2 = 18, S3 = 19, S4 = 20, S5 = 21, S6 = 22, S7 = 23; - public static int S8 = 24, S9 = 25, S10= 26, S11= 27, T3 = 28, T4 = 29, T5 = 30, T6 = 31; - - // FP registers - public static int F0 = 32, F1 = 33, F2 = 34, F3 = 35, F4 = 36, F5 = 37, F6 = 38, F7 = 39; - public static int FS0 = 40, FS1 = 41, FA0 = 42, FA1 = 43, FA2 = 44, FA3 = 45, FA4 = 46, FA5 = 47; - public static int FA6 = 48, FA7 = 49, FS2 = 50, FS3 = 51, FS4 = 52, FS5 = 53, FS6 = 54, FS7 = 55; - public static int FS8 = 56, FS9 = 57, FS10 = 58, FS11 = 59, FT8 = 60, FT9 = 61, FT10 = 62, FT11 = 63; - - static final int MAX_REG = 64; - - public static final int F_OFFSET = 32; - - static final String[] REGS = new String[] { - "zero","rpc" , "rsp" , "s12" , "s13" , "t0" , "t1" , "t2" , - "s0" , "s1" , "a0" , "a1" , "a2" , "a3" , "a4" , "a5" , - "a6" , "a7" , "s2" , "s3" , "s4" , "s5" , "s6" , "s7" , - "s8" , "s9" , "s10" , "s11" , "t3" , "t4" , "t5" , "t6" , - "f0" , "f1" , "f2" , "f3" , "f4" , "f5" , "f6" , "f7" , - "fs0" , "fs1" , "fa0" , "fa1" , "fa2" , "fa3" , "fa4" , "fa5" , - "fa6" , "fa7" , "fs2" , "fs3" , "fs4" , "fs5" , "fs6" , "fs7" , - "fs8" , "fs9" , "fs10", "fs11", "ft8" , "ft9" , "ft10", "ft11" - // Register-based naming hella easier to debug than "official" register - // names when single-stepping in debugger. - //"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[] regs() { return REGS; } - - // General purpose register mask: pointers and ints, not floats - 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 RSP - 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<> 1) & 0x3FF; - int imm11 = (delta>>11) & 1; - int imm12_19 = (delta>>12) & 0xFF; - int imm20 = (delta>>19) & 1; - int bits = imm20<<19 | imm10_01 << 9 | imm11 << 8 | imm12_19; - return bits << 12 | rd << 7 | opcode; - } - - - public static int i_type(int opcode, int rd, int func3, int rs1, int imm12) { - assert 0 <= rd && rd < 32; - assert 0 <= rs1 && rs1 < 32; - assert opcode >= 0 && func3 >=0 && imm12 >= 0; // Zero-extend by caller - return (imm12 << 20) | (rs1 << 15) | (func3 << 12) | (rd << 7) | opcode; - } - - // S-type instructions(store) - public static int s_type(int opcode, int func3, int rs1, int rs2, int imm12) { - assert 0 <= rs1 && rs1 < 32; - assert 0 <= rs2 && rs2 < 32; - assert 0 <= func3; - - assert imm12 >= 0; // Masked to high zero bits by caller - int imm_lo = imm12 & 0x1F; - int imm_hi = imm12 >> 5; - return (imm_hi << 25) | (rs2 << 20) | (rs1 << 15) | (func3 << 12) | (imm_lo << 7) | opcode; - } - - // BRANCH - public static int b_type(int opcode, int func3, short rs1, short rs2, int delta) { - assert 0 <= rs1 && rs1 < 32; - assert 0 <= rs2 && rs2 < 32; - assert -4*1024 <= delta && delta < 4*1024; - assert (delta&1)==0; // Low bit is always zero, not encoded - // Messy branch offset encoding - // 31 30 29 28 27 26 25 24-20 19-15 14-12 11 10 9 8 7 6-0 - // 12 10 9 8 7 6 5 SRC2 SRC1 FUNC3 4 3 2 1 11 OP - int imm4_1 = (delta>> 1) & 0xF; - int imm10_5= (delta>> 5) &0x3F; - int imm11 = (delta>>11) & 1; - int imm12 = (delta>>12) & 1; - int imm5 = imm4_1<<1 | imm11; - int imm7 = imm12<<6 | imm10_5; - return (imm7 << 25 ) | (rs2 << 20) | (rs1 << 15) | (func3 << 12) | (imm5 << 7) | opcode; - } - - public enum RM { - RNE, // Round to Nearest, ties to Even - RTZ, // Round towards Zero - RDN, // Round Down - RUP, // Round Up - DIRECT, // Round to Nearest, ties to Max Magnitude - RESERVED1, // Reserved for futue use - RESERVED2, // Reserved for future use - DYN, // In instruction’s rm field, selects dynamic rounding mode; In Rounding Mode register, reserved - } - - // Since opcode is the same just return back func3 - static public int jumpop(String op) { - return switch(op) { - case "==" -> 0x0; - case "!=" -> 0x1; - case "<" -> 0x4; - case ">=" -> 0x5; - case "u<" -> 0x6; - case "u<=" -> 0x7; - default -> throw Utils.TODO(); - }; - } - - // Since opcode is the same just return back func3 - static public int fsetop(String op) { - return switch(op) { - case "<" -> 1; - case "<=" -> 0; - case "==" -> 2; - default -> throw Utils.TODO(); - }; - } - - @Override public RegMask callArgMask( TypeFunPtr tfp, int idx, int maxArgSlot ) { return callInMask(tfp,idx,maxArgSlot); } - static RegMask callInMask( TypeFunPtr tfp, int idx, int maxArgSlot ) { - if( idx==0 ) return RPC_MASK; - if( idx==1 ) return null; - if( idx-2 >= tfp.nargs() ) return null; // Anti-dependence - // Count floats in signature up to index - int fcnt=0; - for( int i=2; i0 ) throw Utils.TODO(); - return new RegMask(MAX_REG + 1 + (idx - 2)); - } - - @Override public short maxArgSlot( TypeFunPtr tfp ) { - int icnt=0, fcnt=0; // Count of ints, floats - for( int i=0; i>52) == ti.value(); - } - // True if HIGH 20-bit signed immediate, with all zeros low. - public static boolean imm20Exact(TypeInteger ti) { - // shift left 32 to clear out the upper 32 bits. - // shift right SIGNED to sign-extend upper 32 bits; then shift 12 more to clear out lower 12 bits. - // shift left 12 to re-center the bits. - return ti.isConstant() && (((ti.value()<<32)>>44)<<12) == 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 CallNode call -> call(call); - case CastNode cast -> new CastMach(cast); - case CallEndNode cend -> new CallEndMach(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 MinusNode neg -> new NegRISC(neg); - case MulFNode mulf -> new MulFRISC(mulf); - case MulNode mul -> new MulRISC(mul); - case NewNode nnn -> nnn(nnn); - case NotNode not -> new NotRISC(not); - case OrNode or -> or(or); - case ParmNode parm -> new ParmRISC(parm); - case PhiNode phi -> new PhiNode(phi); - case ProjNode prj -> prj(prj); - case ReadOnlyNode read-> new ReadOnlyMach(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 XorNode xor -> xor(xor); - - case LoopNode loop -> new LoopNode(loop); - case RegionNode region -> new RegionNode(region); - default -> throw Utils.TODO(); - }; - } - - private Node addf(AddFNode addf) { - return new AddFRISC(addf); - } - - private Node add(AddNode add) { - if( add.in(2) instanceof ConstantNode off2 && off2._con instanceof TypeInteger ti && imm12(ti) ) - return new AddIRISC(add, (int)ti.value(),true); - return new AddRISC(add); - } - - private Node and(AndNode and) { - if( and.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti ) { - if( imm12(ti) ) - return new AndIRISC(and, (int)ti.value()); - // Could be any size low bit mask - if( ti.value() == 0xFFFFFFFFL ) - return new SrlIRISC(new SllIRISC(and,32),32,false); - } - return new AndRISC(and); - } - - private Node call(CallNode call) { - return call.fptr() instanceof ConstantNode con && con._con instanceof TypeFunPtr tfp - ? new CallRISC(call, tfp) - : new CallRRISC(call); - } - private Node nnn(NewNode nnn) { - // TODO: pass in the TFP for alloc - return new NewRISC(nnn); - } - - private Node cmp(BoolNode bool) { - // Float variant directly implemented in hardware - if( bool.isFloat() ) - return new SetFRISC(bool); - - // Only < and y - swap; y < x - // x >= y - swap and negate; !(x < y); `slt tmp=y,x;` then NOT. - // x != y - sub and vs0 == `sub tmp=x-y; sltu dst=tmp,#1` then NOT. - - // The ">", ">=" and "!=" in Simple include a NotNode, which can be - // implemented with a XOR. If one of the above is followed by a NOT - // we can remove the double XOR in the encodings. - - return switch( bool.op() ) { - case "<" -> bool.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti && imm12(ti) - ? new SetIRISC(bool, (int)ti.value(),false) - : new SetRISC(bool, false); - // x <= y - flip and negate; !(y < x); `slt tmp=y,x; xori dst=tmp,#1` - case "<=" -> new XorIRISC(new SetRISC(bool.swap12(), false),1,false); - // x == y - sub and vs0 == `sub tmp=x-y; sltu dst=tmp,#1` - case "==" -> new SetIRISC(new SubRISC(bool),1,true); - default -> throw Utils.TODO(); - }; - } - - private Node con( ConstantNode con ) { - if( !con._con.isConstant() ) return new ConstantNode( con ); // Default unknown caller inputs - return switch( con._con ) { - case TypeInteger ti -> { - if( imm12(ti) ) yield new IntRISC(con); - long x = ti.value(); - if( imm20Exact(ti) ) yield new LUI((int)x); - if( (x<<32)>>32 == x ) { // Signed lower 32-bit immediate - // Here, the low 12 bits get sign-extended, which means if - // bit11 is set, the value is negative and lowers the LUI - // value. Add a bit 12 to compensate - if( ((x>>11)&1)==1 ) x += 0x1000; - yield new AddIRISC(new LUI((int)(x & ~0xFFF)), (int)(x & 0xFFF),false); - } - // Need more complex sequence for larger constants... or a load - // from a constant pool, which does not need an extra register - yield new Int8RISC(con); - } - // Load from constant pool - case TypeFloat tf -> new FltRISC(con); - case TypeFunPtr tfp -> new TFPRISC(con); - case TypeMemPtr tmp -> new TMPRISC(con); - 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( iff.in(1) instanceof BoolNode bool && !bool.isFloat() ) { - // if less than or equal switch inputs - String bop = bool.op(); - if( bop.equals("<=") || bop.equals(">") ) - return new BranchRISC(iff, IfNode.swap(bop), bool.in(2), bool.in(1)); - return new BranchRISC(iff, bop, bool.in(1), bool.in(2)); - } - // Vs zero - return new BranchRISC(iff, iff.in(1)==null ? "==" : "!=", iff.in(1), null); - } - - private Node or(OrNode or) { - 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 && 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 && 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 && imm12(ti)) - return new SrlIRISC(shr, (int)ti.value(),true); - return new SrlRISC(shr); - } - - private Node sll(ShlNode sll) { - 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 && imm12(ti) - ? new AddIRISC(sub, (int)(-ti.value()),true) - : new SubRISC(sub); - } - - private Node i2f8(ToFloatNode tfn) { - assert tfn.in(1)._type instanceof TypeInteger ti; - return new I2F8RISC(tfn); - } - - private Node prj(ProjNode prj) { - return new ProjRISC(prj); - } - - private Node ld(LoadNode ld) { - return new LoadRISC(ld,address(ld),off); - } - - private Node st(StoreNode st) { - Node xval = st.val() instanceof ConstantNode con && con._con == TypeInteger.ZERO ? null : st.val(); - return new StoreRISC(st,address(st), off, 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 static int off; - private Node address( MemOpNode mop ) { - off = 0; // Reset - 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 { - base = new AddRISC(base,mop.off()); - base._type = mop.ptr()._type; - } - return base; - } - -} 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 deleted file mode 100644 index 2513de37..00000000 --- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddFMemX86.java +++ /dev/null @@ -1,44 +0,0 @@ -package com.seaofnodes.simple.node.cpus.x86_64_v2; - -import com.seaofnodes.simple.codegen.*; -import com.seaofnodes.simple.node.*; -import com.seaofnodes.simple.util.SB; -import com.seaofnodes.simple.util.Utils; - -public class AddFMemX86 extends MemOpX86 { - AddFMemX86( AddFNode add, LoadNode ld , Node base, Node idx, int off, int scale, Node val ) { - super(add,ld, base, idx, off, scale, 0, val ); - } - @Override public String op() { return "addf"+_sz; } - @Override public RegMask regmap(int i) { - if( i==1 ) return null; // Memory - if( i==2 ) return x86_64_v2.RMASK; // base - if( i==3 ) return x86_64_v2.RMASK; // index - if( i==4 ) return x86_64_v2.XMASK; // value - throw Utils.TODO(); - } - @Override public RegMask outregmap() { return x86_64_v2.XMASK; } - @Override public int twoAddress() { return 4; } - @Override public void encoding( Encoding enc ) { - // addsd xmm0, DWORD PTR [rdi+0xc] - short dst = enc.reg(this ); - short ptr = enc.reg(ptr()); - short idx = enc.reg(idx()); - // F opcode - enc.add1(0xF2); - // rex prefix must come next (REX.W is not set) - x86_64_v2.rexF(dst, ptr, idx, false, enc); - - // FP ADD - enc.add1(0x0F).add1(0x58); - - x86_64_v2.indirectAdr(_scale, idx, ptr, _off, dst, enc); - } - - // General form: "add dst = src + [base + idx<<2 + 12]" - @Override public void asm(CodeGen code, SB sb) { - sb.p(code.reg(this)).p(" = "); - sb.p(val()==null ? "#"+_imm : code.reg(val())).p(" + "); - asm_address(code,sb); - } -} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddFX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddFX86.java deleted file mode 100644 index 02e0c7ea..00000000 --- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddFX86.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.seaofnodes.simple.node.cpus.x86_64_v2; - -import com.seaofnodes.simple.codegen.*; -import com.seaofnodes.simple.node.*; -import com.seaofnodes.simple.util.SB; - -public class AddFX86 extends MachConcreteNode implements MachNode { - AddFX86( Node addf ) { super(addf); } - @Override public String op() { return "addf"; } - @Override public RegMask regmap(int i) { assert i==1 || i==2; return x86_64_v2.XMASK; } - @Override public RegMask outregmap() { return x86_64_v2.XMASK; } - @Override public int twoAddress() { return 1; } - @Override public boolean commutes() { return true; } - - @Override public void encoding( Encoding enc ) { - // F2 0F 58 /r ADDSD xmm1, xmm2/m64 - short dst = (short)(enc.reg(this ) - x86_64_v2.XMM_OFFSET); - short src = (short)(enc.reg(in(2)) - x86_64_v2.XMM_OFFSET); - // Fopcode - enc.add1(0xF2); - // rex prefix must come next (REX.W is not set) - x86_64_v2.rexF(dst, src, 0, false, enc); - - enc.add1(0x0F).add1(0x58); - - enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, dst, src)); - } - - // General form: "addf dst += src" - @Override public void asm(CodeGen code, SB sb) { - sb.p(code.reg(this)).p(" += ").p(code.reg(in(2))); - } -} 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 deleted file mode 100644 index 0ba390c6..00000000 --- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddIX86.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.seaofnodes.simple.node.cpus.x86_64_v2; - -import com.seaofnodes.simple.codegen.Encoding; -import com.seaofnodes.simple.node.Node; -import com.seaofnodes.simple.util.SB; -import com.seaofnodes.simple.util.Utils; - -public class AddIX86 extends ImmX86 { - AddIX86( Node add, int imm ) { super(add,imm); } - @Override public String op() { - return _imm == 1 ? "inc" : (_imm == -1 ? "dec" : "addi"); - } - @Override public String glabel() { return "+"; } - @Override int opcode() { return 0x81; } - @Override int mod() { return 0; } - @Override public final void encoding( Encoding enc ) { - if( _imm== 1 || _imm== -1 ) { - short dst = enc.reg(this); // Also src1 - enc.add1( x86_64_v2.rex(0,dst,0)); - enc.add1(0xFF); - enc.add1( x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, _imm==1 ? 0 : 1, dst) ); - } else super.encoding(enc); - } -} 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 deleted file mode 100644 index 3ee721e3..00000000 --- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddMemX86.java +++ /dev/null @@ -1,34 +0,0 @@ -package com.seaofnodes.simple.node.cpus.x86_64_v2; - -import com.seaofnodes.simple.codegen.*; -import com.seaofnodes.simple.node.*; -import com.seaofnodes.simple.util.SB; - -public class AddMemX86 extends MemOpX86 { - AddMemX86( AddNode add, LoadNode ld , Node base, Node idx, int off, int scale, int imm, Node val ) { - super(add,ld, base, idx, off, scale, imm, val ); - } - @Override public String op() { return "add"+_sz; } - @Override public RegMask outregmap() { return x86_64_v2.WMASK; } - @Override public int twoAddress() { return 4; } - @Override public void encoding( Encoding enc ) { - // add something to register from memory - // add eax,DWORD PTR [rdi+0xc] - // REX.W + 03 /r ADD r64, r/m64 - short dst = enc.reg(this ); - short ptr = enc.reg(ptr()); - short idx = enc.reg(idx()); - - enc.add1(x86_64_v2.rex(dst, ptr, idx)); - // opcode - enc.add1(0x03); - - x86_64_v2.indirectAdr(_scale, idx, ptr, _off, dst, enc); - } - - // General form: "add dst += [base + idx<<2 + 12]" - @Override public void asm(CodeGen code, SB sb) { - sb.p(code.reg(this)).p(" += "); - asm_address(code,sb); - } -} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddX86.java deleted file mode 100644 index 84543e8f..00000000 --- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddX86.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.seaofnodes.simple.node.cpus.x86_64_v2; - -import com.seaofnodes.simple.node.Node; - -public class AddX86 extends RegX86 { - AddX86( Node add ) { super(add); } - @Override public String op() { return "add"; } - @Override public String glabel() { return "+"; } - @Override int opcode() { return 0x03; } - @Override public boolean commutes() { return true; } -} 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 deleted file mode 100644 index 3c1b567c..00000000 --- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AndIX86.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.seaofnodes.simple.node.cpus.x86_64_v2; - -import com.seaofnodes.simple.node.Node; - -public class AndIX86 extends ImmX86 { - AndIX86( Node add, int imm ) { super(add,imm); } - @Override public String op() { return "andi"; } - @Override public String glabel() { return "&"; } - @Override int opcode() { return 0x81; } - @Override int mod() { return 4; } -} 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 deleted file mode 100644 index dedbce45..00000000 --- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AndX86.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.seaofnodes.simple.node.cpus.x86_64_v2; - -import com.seaofnodes.simple.node.Node; - -public class AndX86 extends RegX86 { - AndX86( Node add ) { super(add); } - @Override public String op() { return "and"; } - @Override public String glabel() { return "&"; } - @Override int opcode() { return 0x23; } - @Override public boolean commutes() { return true; } -} 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 deleted file mode 100644 index 460f568b..00000000 --- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CallRX86.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.seaofnodes.simple.node.cpus.x86_64_v2; - -import com.seaofnodes.simple.codegen.*; -import com.seaofnodes.simple.node.CallNode; -import com.seaofnodes.simple.node.MachNode; -import com.seaofnodes.simple.util.SB; - -public class CallRX86 extends CallNode implements MachNode { - CallRX86( CallNode call ) { super(call); } - @Override public String op() { return "callr"; } - @Override public String label() { return op(); } - @Override public RegMask regmap(int i) { - return i==_inputs._len - ? x86_64_v2.WMASK // Function call target - : x86_64_v2.callInMask(tfp(),i,fun()._maxArgSlot); // Normal argument - } - @Override public RegMask outregmap() { return null; } - @Override public void encoding( Encoding enc ) { - // FF /2 CALL r/m64 - // calls the function in the register - short src = enc.reg(fptr()); - enc.add1(0xFF); - enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.INDIRECT,2,src)); - } - - @Override public void asm(CodeGen code, SB sb) { - sb.p(code.reg(fptr())).p(" "); - for( int i=0; i= 8 ) enc.add1(x86_64_v2.rex(dst, dst, 0)); - enc.add1(0x33); // opcode - enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, dst, dst)); - return; - } - - // Simply move the constant into a GPR - // Conditional encoding based on 64 or 32 bits - //REX.W + C7 /0 id MOV r/m64, imm32 - - long imm = ((TypeInteger)_con).value(); - if (Integer.MIN_VALUE <= imm && imm < 0) { - // We need sign extension, so use imm32 into 64 bit register - // REX.W + C7 /0 id MOV r/m64, imm32 - enc.add1(x86_64_v2.rex(0, dst, 0)); - enc.add1(0xC7); // 32 bits encoding - enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, 0x00, dst)); - enc.add4((int)imm); - } else if (0 <= imm && imm <= 0xFFFFFFFFL) { - // We want zero extension into 64 bit register - // so move 32 bit into 32 bit register which zeros - // the upper bits in the 64 bit register. - // B8+ rd id MOV r32, imm32 - if (dst >= 8) enc.add1(0x41); - enc.add1(0xB8 + (dst & 0x07)); - enc.add4((int)imm); - } else { - // Just write the full 64 bit constant - // REX.W + B8+ rd io MOV r64, imm64 - enc.add1(x86_64_v2.rex(0, dst, 0)); - enc.add1(0xB8 + (dst & 0x07)); - enc.add8(imm); // 64 bits encoding - } - } - - // Human-readable form appended to the SB. Things like the encoding, - // indentation, leading address or block labels not printed here. - // Just something like "ld4\tR17=[R18+12] // Load array base". - // General form: "op\tdst=src+src" - @Override public void asm(CodeGen code, SB sb) { - String reg = code.reg(this); - if( _con == Type.NIL || _con == TypeInteger.ZERO ) - sb.p(reg).p(",").p(reg); - else - _con.print(sb.p(reg).p(" = #")); - } -} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/JmpX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/JmpX86.java deleted file mode 100644 index de27aa5c..00000000 --- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/JmpX86.java +++ /dev/null @@ -1,81 +0,0 @@ -package com.seaofnodes.simple.node.cpus.x86_64_v2; - -import com.seaofnodes.simple.codegen.*; -import com.seaofnodes.simple.node.*; -import com.seaofnodes.simple.util.SB; -import com.seaofnodes.simple.util.Utils; - -// Jump on flags, uses flags -public class JmpX86 extends IfNode implements MachNode, RIPRelSize { - String _bop; - JmpX86( IfNode iff, String bop ) { - super(iff); - _bop = bop; - } - @Override public String op() { return "j"+_bop; } - @Override public String label() { return op(); } - @Override public void postSelect(CodeGen code) { - Node set = in(1); - if( set==null ) return; // Never-node cutout - Node cmp = set.in(1); - // Bypass an expected Set and just reference the cmp directly - if( set instanceof SetX86 setx && (cmp instanceof CmpX86 || cmp instanceof CmpIX86 || cmp instanceof CmpMemX86 || cmp instanceof CmpFX86) ) { - _inputs.set( 1, cmp ); - _bop = setx._bop; - } else - throw Utils.TODO(); - } - @Override public RegMask regmap(int i) { assert i==1; return x86_64_v2.FLAGS_MASK; } - @Override public RegMask outregmap() { return null; } - @Override public void negate() { _bop = negate(_bop); } - - @Override public void encoding( Encoding enc ) { - if( in(1)!=null ) { - int op = x86_64_v2.jumpop(_bop); - enc.add1(op-16).add1(0); // Short form jump - } else { - if( _bop=="!=" ) return; // Inverted, no code - enc.add1(0xEB).add1(0); // Never-node - } - enc.jump(this,cproj(0)); - } - - // Delta is from opcode start, but X86 measures from the end of the 2-byte encoding - @Override public byte encSize(int delta) { - if( in(1)==null && _bop=="!=" ) return 0; // Inverted never-node, no code - return (byte)(x86_64_v2.imm8(delta-2) ? 2 : 6); - } - - // Delta is from opcode start - @Override public void patch( Encoding enc, int opStart, int opLen, int delta ) { - assert !( in(1)==null && _bop=="!=" ); // Inverted never-node, no code no patch - byte[] bits = enc.bits(); - if( opLen==2 ) { - assert in(1)==null || bits[opStart] == x86_64_v2.jumpop(_bop)-16; - delta -= 2; // Offset from opcode END - assert (byte)delta==delta; - bits[opStart+1] = (byte)delta; - } else { - assert in(1)==null || bits[opStart] == x86_64_v2.jumpop(_bop)-16; - delta -= 6; // Offset from opcode END - bits[opStart] = 0x0F; - bits[opStart+1] = (byte)(in(1)==null ? 0xE9 : x86_64_v2.jumpop(_bop)); - enc.patch4(opStart+2,delta); - } - } - - @Override public void asm(CodeGen code, SB sb) { - if( in(1)!=null ) { // Never-node - String src = code.reg(in(1)); - if( src!="flags" ) sb.p(src).p(" "); - } else if( _bop=="!=" ) { - sb.p("never"); - return; - } - CFGNode prj = cproj(0).uctrlSkipEmpty(); // 0 is True is jump target - if( !prj.blockHead() ) prj = prj.cfg0(); - sb.p(label(prj)); - } - - @Override public String comment() { return "L"+cproj(1)._nid; } -} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/LeaX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/LeaX86.java deleted file mode 100644 index 2cd6018f..00000000 --- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/LeaX86.java +++ /dev/null @@ -1,55 +0,0 @@ -package com.seaofnodes.simple.node.cpus.x86_64_v2; - -import com.seaofnodes.simple.codegen.*; -import com.seaofnodes.simple.node.*; -import com.seaofnodes.simple.util.SB; - -public class LeaX86 extends MachConcreteNode implements MachNode { - final int _scale; - final int _offset; - LeaX86( Node add, Node base, Node idx, int scale, int offset ) { - super(add); - assert scale==0 || scale==1 || scale==2 || scale==3; - _inputs.pop(); - _inputs.pop(); - _inputs.push(base); - _inputs.push(idx); - _scale = scale; - _offset = offset; - } - - @Override public String op() { return "lea"; } - @Override public RegMask regmap(int i) { assert i==1 || i==2; return x86_64_v2.RMASK; } - @Override public RegMask outregmap() { return x86_64_v2.WMASK; } - - @Override public void encoding( Encoding enc ) { - // REX.W + 8D /r LEA r64,m - short dst = enc.reg(this); - short ptr = enc.reg(in(1)); - short idx = enc.reg(in(2)); - // ptr is null - // just do: [(index * s) + disp32] - if( ptr == -1 ) { - enc.add1(x86_64_v2.rex(dst, 0, idx)); - enc.add1(0x8D); // opcode - enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.INDIRECT, dst, 0x04)); - enc.add1(x86_64_v2.sib(_scale, idx, x86_64_v2.RBP)); - enc.add4(_offset); - return; - } - - enc.add1(x86_64_v2.rex(dst, ptr, idx)); - enc.add1(0x8D); // opcode - // rsp is hard-coded here(0x04) - x86_64_v2.indirectAdr(_scale, idx, ptr, _offset, dst, enc); - } - - // General form: "lea dst = base + 4*idx + 12" - @Override public void asm(CodeGen code, SB sb) { - sb.p(code.reg(this)).p(" = "); - if( in(1) != null ) - sb.p(code.reg(in(1))).p(" + "); - sb.p(code.reg(in(2))).p("<<").p(_scale); - if( _offset!=0 ) sb.p(" + #").p(_offset); - } -} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/LoadX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/LoadX86.java deleted file mode 100644 index 441cf693..00000000 --- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/LoadX86.java +++ /dev/null @@ -1,57 +0,0 @@ -package com.seaofnodes.simple.node.cpus.x86_64_v2; - -import com.seaofnodes.simple.codegen.*; -import com.seaofnodes.simple.node.LoadNode; -import com.seaofnodes.simple.node.Node; -import com.seaofnodes.simple.type.*; -import com.seaofnodes.simple.util.SB; -import com.seaofnodes.simple.util.Utils; - -public class LoadX86 extends MemOpX86 { - LoadX86( LoadNode ld, Node base, Node idx, int off, int scale ) { - super(ld,ld, base, idx, off, scale, 0); - } - @Override public String op() { return "ld"+_sz; } - @Override public RegMask outregmap() { return x86_64_v2.MEM_MASK; } - @Override public void encoding( Encoding enc ) { - // REX.W + 8B /r MOV r64, r/m64 - // Zero extension for u8, u16 and u32 but sign extension i8, i16, i32 - // Use movsx and movzx - short dst = enc.reg(this ); - short ptr = enc.reg(ptr()); - short idx = enc.reg(idx()); - enc(enc, _declaredType, dst, ptr, idx, _off, _scale); - } - - static void enc( Encoding enc, Type decl, short dst, short ptr, short idx, int off, int scale ) { - if( decl == TypeFloat.F32) enc.add1(0xF3); - if( decl == TypeFloat.F64) enc.add1(0xF2); - - if( decl.isa(TypeFloat.F64) ) - dst -= (short)x86_64_v2.XMM_OFFSET; - - x86_64_v2.rexF(dst, ptr, idx, decl != TypeInteger.U32 && decl != TypeFloat.F32 && decl != TypeFloat.F64, enc); - - if( false ) ; - else if( decl == TypeFloat.F32 ) enc.add1(0x0F).add1(0x10); // F3 0F 10 /r MOVSS xmm1, m32 - else if( decl == TypeFloat.F64 ) enc.add1(0x0F).add1(0x10); // F2 0F 10 /r MOVSD xmm1, m64 - else if( decl.isa(TypeInteger.I8)) enc.add1(0x0F).add1(0xBE); // sign extend: REX.W + 0F BE /r MOVSX r64, r/m8 - else if( decl == TypeInteger.I16 ) enc.add1(0x0F).add1(0xBF); // sign extend: REX.W + 0F BF /r MOVSX r64, r/m16 - else if( decl == TypeInteger.I32 ) enc.add1(0x63); // sign extend: REX.W + 63 /r MOVSXD r64, r/m32 - else if( decl == TypeInteger.U8 ) enc.add1(0x0F).add1(0xB6); // zero extend: REX.W + 0F B6 /r MOVZX r64, r/m8 - else if( decl == TypeInteger.U16 ) enc.add1(0x0F).add1(0xB7); // zero extend: REX.W + 0F B7 /r MOVZX r64, r/m16 - // Covers U32, I64/BOT, TMP - else if( decl.log_size()>=2 ) enc.add1(0x8B); // zero extend: 8B /r MOV r32, r/m32 - else throw Utils.TODO(); - - // includes modrm internally - x86_64_v2.indirectAdr(scale, idx, ptr, off, dst, enc); - } - - - // General form: "ldN dst,[base + idx<<2 + 12]" - @Override public void asm(CodeGen code, SB sb) { - sb.p(code.reg(this)).p(","); - asm_address(code,sb); - } -} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MemAddX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MemAddX86.java deleted file mode 100644 index bd56d022..00000000 --- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MemAddX86.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.seaofnodes.simple.node.cpus.x86_64_v2; - -import com.seaofnodes.simple.codegen.*; -import com.seaofnodes.simple.node.*; -import com.seaofnodes.simple.util.SB; - -public class MemAddX86 extends MemOpX86 { - MemAddX86( StoreNode st, Node base, Node idx, int off, int scale, int imm, Node val ) { - super(st, st, base, idx, off, scale, imm, val ); - } - @Override public String op() { - return (_imm == 1 ? "inc" : (_imm == -1 ? "dec" : "add")) + _sz; - } - // Register mask allowed as a result. 0 for no register. - @Override public RegMask outregmap() { return null; } - @Override public RegMask killmap() { return x86_64_v2.FLAGS_MASK; } - @Override public void encoding( Encoding enc ) { - // add something to memory - // REX.W + 01 /r | REX.W + 81 /0 id - // ADD [mem], imm32/reg - short ptr = enc.reg(ptr()); - short idx = enc.reg(idx()); - short src = enc.reg(val()); - - enc.add1(x86_64_v2.rex(src, ptr, idx)); - // opcode - enc.add1( src == -1 ? (_imm==1 ? 0xFF : 0x81): 0x01); - - // includes modrm - x86_64_v2.indirectAdr(_scale, idx, ptr, _off, src == -1 ? 0 : src, enc); - if( src == -1 && _imm!=1 ) enc.add4(_imm); - } - // General form: "add [base + idx<<2 + 12] += src" - @Override public void asm(CodeGen code, SB sb) { - asm_address(code,sb); - if( val()==null ) { - if( _imm != 1 && _imm != -1 ) sb.p(" += #").p(_imm); - } else sb.p(" += ").p(code.reg(val())); - } -} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MemOpX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MemOpX86.java deleted file mode 100644 index 5f090717..00000000 --- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MemOpX86.java +++ /dev/null @@ -1,88 +0,0 @@ -package com.seaofnodes.simple.node.cpus.x86_64_v2; - -import com.seaofnodes.simple.codegen.*; -import com.seaofnodes.simple.node.*; -import com.seaofnodes.simple.type.*; -import com.seaofnodes.simple.util.SB; -import com.seaofnodes.simple.util.Utils; -import java.lang.StringBuilder; -import java.util.BitSet; - -// Generic X86 memory operand base. -// inputs: -// ctrl - will appear for possibly RCE (i.e. array ops) -// mem - memory dependence edge -// base - Basic object pointer -// idx/null - Scaled index offset, or null if none -// val/null - Value to store, as part of an op-to-mem or op-from-mem. Null for loads, or if an immediate is being used -// Constants: -// offset - offset added to base. Can be zero. -// scale - scale on index; only 0,1,2,4,8 allowed, 0 is only when index is null -// imm - immediate value to store or op-to-mem, and only when val is null -public abstract class MemOpX86 extends MemOpNode implements MachNode { - final int _off; // Limit 32 bits - final int _scale; // Limit 0,1,2,3 - final int _imm; // Limit 32 bits - final char _sz; // Handy print name - MemOpX86( Node op, MemOpNode mop, Node base, Node idx, int off, int scale, int imm ) { - super(op,mop); - assert base._type instanceof TypeMemPtr && !(base instanceof AddNode); - assert (idx==null && scale==0) || (idx!=null && 0<= scale && scale<=3); - - // Copy memory parts from e.g. the LoadNode over the opcode, e.g. an Add - if( op != mop ) { - _inputs.set(0,mop.in(0)); // Control from mem op - _inputs.set(1,mop.in(1)); // Memory from mem op - _inputs.set(2,base); // Base handed in - } - - assert ptr() == base; - _inputs.setX(3,idx); - _off = off; - _scale = scale; - _imm = imm; - _sz = (char)('0'+(1<<_declaredType.log_size())); - } - - // Store-based flavors have a value edge - MemOpX86( Node op, MemOpNode mop, Node base, Node idx, int off, int scale, int imm, Node val ) { - this(op,mop,base,idx,off,scale,imm); - _inputs.setX(4,val); - } - - Node idx() { return in(3); } - Node val() { return in(4); } // Only for stores, including op-to-memory - - @Override public StringBuilder _printMach(StringBuilder sb, BitSet visited) { - return sb.append(".").append(_name); - } - - @Override public String label() { return op(); } - @Override public Type compute() { throw Utils.TODO(); } - @Override public Node idealize() { throw Utils.TODO(); } - - // Register mask allowed on input i. - @Override public RegMask regmap(int i) { - if( i==1 ) return null; // Memory - if( i==2 ) return x86_64_v2.RMASK; // base in GPR - if( i==3 ) return x86_64_v2.RMASK; // index in GPR - if( i==4 ) return _sz >= 2 - ? x86_64_v2.MEM_MASK // value in GPR or XMM - : x86_64_v2.RMASK; // Bytes and shorts in GPR only - return null; // Anti-dependence - } - - // "[base + idx<<2 + 12]" - SB asm_address(CodeGen code, SB sb) { - sb.p("[").p(code.reg(ptr())); - if( idx() != null ) { - sb.p("+").p(code.reg(idx())); - if( _scale != 0 ) - sb.p("*").p((1<<_scale)); - } - if( _off != 0 ) - sb.p("+").p(_off); - return sb.p("]"); - } - -} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MulFX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MulFX86.java deleted file mode 100644 index 7932c893..00000000 --- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MulFX86.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.seaofnodes.simple.node.cpus.x86_64_v2; - -import com.seaofnodes.simple.codegen.*; -import com.seaofnodes.simple.node.*; -import com.seaofnodes.simple.util.SB; - -public class MulFX86 extends MachConcreteNode implements MachNode { - MulFX86( Node mulf) { super(mulf); } - @Override public String op() { return "mulf"; } - @Override public RegMask regmap(int i) { assert i==1 || i==2; return x86_64_v2.XMASK; } - @Override public RegMask outregmap() { return x86_64_v2.XMASK; } - @Override public int twoAddress() { return 1; } - @Override public boolean commutes() { return true; } - - @Override public void encoding( Encoding enc ) { - // F2 0F 59 /r MULSD xmm1,xmm2/m64 - short dst = (short)(enc.reg(this ) - x86_64_v2.XMM_OFFSET); - short src = (short)(enc.reg(in(2)) - x86_64_v2.XMM_OFFSET); - - // Fopcode - enc.add1(0xF2); - // rex prefix must come next (REX.W is not set) - x86_64_v2.rexF(dst, src, 0, false, enc); - - enc.add1(0x0F).add1(0x59).add1(x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, dst, src)); - } - - // General form: "mulf dst *= src" - @Override public void asm(CodeGen code, SB sb) { - sb.p(code.reg(this)).p(" *= ").p(code.reg(in(2))); - } -} 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 deleted file mode 100644 index 280b8185..00000000 --- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MulIX86.java +++ /dev/null @@ -1,34 +0,0 @@ -package com.seaofnodes.simple.node.cpus.x86_64_v2; - -import com.seaofnodes.simple.codegen.*; -import com.seaofnodes.simple.node.*; -import com.seaofnodes.simple.util.SB; - -public class MulIX86 extends MachConcreteNode implements MachNode { - final int _imm; - MulIX86( Node add, int imm ) { - super(add); - _inputs.pop(); // Pop the constant input - _imm = imm; // Record the constant - } - @Override public String op() { return "muli"; } - @Override public String glabel() { return "*"; } - @Override public RegMask regmap(int i) { return x86_64_v2.RMASK; } - @Override public RegMask outregmap() { return x86_64_v2.WMASK; } - // Rare 3-address form - @Override public final void encoding( Encoding enc ) { - short dst = enc.reg(this ); // Also src1 - short src = enc.reg(in(1)); - enc.add1(x86_64_v2.rex(src, dst, 0)); - // opcode; 0x69 or 0x6B - enc.add1( 0x69 + (x86_64_v2.imm8(_imm) ? 2 : 0) ); - enc.add1( x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, dst, src) ); - // immediate(4 bytes) 32 bits or (1 byte)8 bits - if( x86_64_v2.imm8(_imm) ) enc.add1(_imm); - else enc.add4(_imm); - } - // General form: "addi dst += #imm" - @Override public void asm(CodeGen code, SB sb) { - sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" * #").p(_imm); - } -} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MulX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MulX86.java deleted file mode 100644 index 4d2a11ed..00000000 --- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MulX86.java +++ /dev/null @@ -1,29 +0,0 @@ -package com.seaofnodes.simple.node.cpus.x86_64_v2; - -import com.seaofnodes.simple.codegen.*; -import com.seaofnodes.simple.node.*; -import com.seaofnodes.simple.util.SB; - -public class MulX86 extends MachConcreteNode implements MachNode { - MulX86( Node mul ) { super(mul); } - @Override public String op() { return "mul"; } - @Override public RegMask regmap(int i) { assert i==1 || i==2; return x86_64_v2.RMASK; } - @Override public RegMask outregmap() { return x86_64_v2.WMASK; } - @Override public int twoAddress() { return 1; } - @Override public boolean commutes() { return true; } - - @Override public void encoding( Encoding enc ) { - // REX.W + 0F AF /r IMUL r64, r/m64 - short dst = enc.reg(this ); // src1 - short src = enc.reg(in(2)); // src2 - enc.add1(x86_64_v2.rex(dst, src, 0)); - enc.add1(0x0F); // opcode - enc.add1(0xAF); // opcode - enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, dst, src)); - } - - // General form: "mul dst *= src" - @Override public void asm(CodeGen code, SB sb) { - sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" * ").p(code.reg(in(2))); - } -} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/NegX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/NegX86.java deleted file mode 100644 index 0c2baced..00000000 --- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/NegX86.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.seaofnodes.simple.node.cpus.x86_64_v2; - -import com.seaofnodes.simple.codegen.*; -import com.seaofnodes.simple.node.*; -import com.seaofnodes.simple.util.SB; - -public class NegX86 extends MachConcreteNode implements MachNode { - NegX86(MinusNode not) { super(not); } - @Override public String op() { return "neg"; } - @Override public RegMask regmap(int i) { return x86_64_v2.RMASK; } - @Override public RegMask outregmap() { return x86_64_v2.RMASK; } - @Override public RegMask killmap() { return x86_64_v2.FLAGS_MASK; } - @Override public int twoAddress() { return 1; } - - @Override public void encoding( Encoding enc ) { - short dst = enc.reg(this ); - enc.add1(x86_64_v2.rex(0, dst, 0)); - enc.add1(0xF7); // opcode - enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, 3, dst)); - } - @Override public void asm(CodeGen code, SB sb) { sb.p(code.reg(this)); } -} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/NewX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/NewX86.java deleted file mode 100644 index fc896dce..00000000 --- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/NewX86.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.seaofnodes.simple.node.cpus.x86_64_v2; - -import com.seaofnodes.simple.codegen.*; -import com.seaofnodes.simple.node.*; -import com.seaofnodes.simple.type.TypeFunPtr; -import com.seaofnodes.simple.util.SB; - -public class NewX86 extends NewNode implements MachNode { - // A pre-zeroed chunk of memory. - NewX86( NewNode nnn ) { super(nnn); } - @Override public void encoding( Encoding enc ) { - enc.external(this,"calloc"); - // ldi rcx,#1 // number of elements to calloc - enc.add1(0xB8 + _arg2Reg).add4(1); - // E8 cd CALL rel32; - enc.add1(0xE8); - enc.add4(0); // offset - } - // General form: "alloc #bytes PC" - @Override public void asm(CodeGen code, SB sb) { - sb.p("ldi rcx = #1\n"); - sb.p("call #calloc"); - } - -} 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 deleted file mode 100644 index d9056be9..00000000 --- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/NotX86.java +++ /dev/null @@ -1,39 +0,0 @@ -package com.seaofnodes.simple.node.cpus.x86_64_v2; - -import com.seaofnodes.simple.codegen.*; -import com.seaofnodes.simple.node.*; -import com.seaofnodes.simple.util.SB; - -public class NotX86 extends MachConcreteNode implements MachNode { - NotX86(NotNode not) { super(not); } - @Override public String op() { return "not"; } - @Override public RegMask regmap(int i) { return x86_64_v2.RMASK; } - @Override public RegMask outregmap() { return x86_64_v2.RMASK; } - @Override public RegMask killmap() { return x86_64_v2.FLAGS_MASK; } - - @Override public void encoding( Encoding enc ) { - assert !(in(1) instanceof NotNode); // Cleared out by peeps - assert !(in(1) instanceof BoolNode); // Cleared out by peeps - short dst = enc.reg(this ); - short src = enc.reg(in(1)); - - // Pre-zero using XOR dst,dst; since zero'd will not have a - // byte-dependency from the setz. Can skip REX is dst is low 8, makes - // this a 32b xor, which will also zero the high bits. - if( dst >= 8 ) enc.add1(x86_64_v2.rex(dst, dst, 0)); - enc.add1(0x33); // opcode - enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, dst, dst)); - - // test rdi,rdi - enc.add1(x86_64_v2.rex(src, src, 0)); - enc.add1(0x85); - enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, src, src)); - - // setz (sete dil) - enc.add1(x86_64_v2.rex(dst, 0, 0)); - enc.add1(0x0F); - enc.add1(0x94); - enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, dst, 0)); - } - @Override public void asm(CodeGen code, SB sb) { sb.p(code.reg(this)); } -} 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 deleted file mode 100644 index 15f4824d..00000000 --- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/OrIX86.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.seaofnodes.simple.node.cpus.x86_64_v2; - -import com.seaofnodes.simple.node.Node; - -public class OrIX86 extends ImmX86 { - OrIX86( Node add, int imm ) { super(add,imm); } - @Override public String op() { return "ori"; } - @Override public String glabel() { return "|"; } - @Override int opcode() { return 0x81; } - @Override int mod() { return 1; } -} 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 deleted file mode 100644 index bc8f0ea8..00000000 --- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/OrX86.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.seaofnodes.simple.node.cpus.x86_64_v2; - -import com.seaofnodes.simple.node.Node; - -public class OrX86 extends RegX86 { - OrX86( Node add ) { super(add); } - @Override public String op() { return "or"; } - @Override public String glabel() { return "|"; } - @Override int opcode() { return 0x0B; } - @Override public boolean commutes() { return true; } -} 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 deleted file mode 100644 index 5cfdd804..00000000 --- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ParmX86.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.seaofnodes.simple.node.cpus.x86_64_v2; - -import com.seaofnodes.simple.codegen.Encoding; -import com.seaofnodes.simple.codegen.RegMask; -import com.seaofnodes.simple.node.MachNode; -import com.seaofnodes.simple.node.ParmNode; - -public class ParmX86 extends ParmNode implements MachNode { - final RegMask _rmask; - ParmX86( ParmNode parm ) { - super(parm); - _rmask = x86_64_v2.callInMask(fun().sig(),_idx,1/*RPC*/); - } - @Override public RegMask regmap(int i) { return null; } - @Override public RegMask outregmap() { return _rmask; } - @Override public void encoding( Encoding enc ) { } -} 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 deleted file mode 100644 index 046c7b4c..00000000 --- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ProjX86.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.seaofnodes.simple.node.cpus.x86_64_v2; - -import com.seaofnodes.simple.codegen.Encoding; -import com.seaofnodes.simple.codegen.RegMask; -import com.seaofnodes.simple.node.MachNode; -import com.seaofnodes.simple.node.ProjNode; - -public class ProjX86 extends ProjNode implements MachNode { - ProjX86( ProjNode p ) { super(p); } - @Override public RegMask regmap(int i) { return null; } - @Override public RegMask outregmap() { - return ((MachNode)in(0)).outregmap(_idx); - } - @Override public void encoding( Encoding enc ) {} -} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/RegX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/RegX86.java deleted file mode 100644 index 5a475efe..00000000 --- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/RegX86.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.seaofnodes.simple.node.cpus.x86_64_v2; - -import com.seaofnodes.simple.codegen.*; -import com.seaofnodes.simple.node.*; -import com.seaofnodes.simple.util.SB; - -public abstract class RegX86 extends MachConcreteNode { - RegX86( Node add ) { super(add); } - @Override public RegMask regmap(int i) { return x86_64_v2.RMASK; } - @Override public RegMask outregmap() { return x86_64_v2.WMASK; } - @Override public RegMask killmap() { return x86_64_v2.FLAGS_MASK; } - @Override public int twoAddress() { return 1; } - abstract int opcode(); - @Override public void encoding( Encoding enc ) { - // REX.W + 01 /r - short dst = enc.reg(in(1)); // src1 - short src = enc.reg(in(2)); // src2 - - enc.add1(x86_64_v2.rex(dst, src, 0)); - enc.add1(opcode()); // opcode - enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, dst, src)); - } - // General form: "add dst += src" - @Override public void asm(CodeGen code, SB sb) { - sb.p(code.reg(this)).p(" ").p(glabel()).p("= ").p(code.reg(in(2))); - } -} 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 deleted file mode 100644 index a35d43a2..00000000 --- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/RetX86.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.seaofnodes.simple.node.cpus.x86_64_v2; - -import com.seaofnodes.simple.*; -import com.seaofnodes.simple.codegen.*; -import com.seaofnodes.simple.node.*; - -public class RetX86 extends ReturnNode implements MachNode { - RetX86( ReturnNode ret, FunNode fun ) { super(ret, fun); fun.setRet(this); } - @Override public void encoding( Encoding enc ) { - int sz = fun()._frameAdjust; - if( sz != 0 ) { - enc.add1( x86_64_v2.REX_W ).add1( x86_64_v2.imm8(sz) ? 0x83 : 0x81 ); - enc.add1( x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, 0b000, x86_64_v2.RSP) ); - if( x86_64_v2.imm8(sz) ) enc.add1(sz); - else enc.add4(sz); - } - enc.add1(0xC3); - } -} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SarIX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SarIX86.java deleted file mode 100644 index 95e0c336..00000000 --- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SarIX86.java +++ /dev/null @@ -1,39 +0,0 @@ -package com.seaofnodes.simple.node.cpus.x86_64_v2; - -import com.seaofnodes.simple.codegen.CodeGen; -import com.seaofnodes.simple.codegen.Encoding; -import com.seaofnodes.simple.codegen.RegMask; -import com.seaofnodes.simple.node.*; -import com.seaofnodes.simple.node.MachNode; -import com.seaofnodes.simple.node.Node; -import com.seaofnodes.simple.util.SB; - -// Arithmetic Right Shift -public class SarIX86 extends MachConcreteNode implements MachNode { - final int _imm; - SarIX86( Node sari, int imm ) { super(sari); assert x86_64_v2.imm8(imm); _inputs.pop(); _imm = imm; } - @Override public String op() { return "sari"; } - @Override public String glabel() { return ">>"; } - int opcode() { return 0xC1; } - int mod() { return 7; } - - @Override public RegMask regmap(int i) { return x86_64_v2.RMASK; } - @Override public RegMask outregmap() { return x86_64_v2.WMASK; } - @Override public int twoAddress() { return 1; } - - @Override public void encoding(Encoding enc) { - short dst = enc.reg(this); // Also src1 - enc.add1(x86_64_v2.rex(0, dst, 0)); - enc.add1( opcode()); - - enc.add1( x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, mod(), dst) ); - - // immediate(4 bytes) 32 bits or (1 byte)8 bits - if( x86_64_v2.imm8(_imm) ) enc.add1(_imm); - else enc.add4(_imm); - } - - @Override public void asm(CodeGen code, SB sb) { - sb.p(code.reg(this)).p(" ").p(glabel()).p("= #").p(_imm); - } -} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SarX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SarX86.java deleted file mode 100644 index 4cc8b558..00000000 --- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SarX86.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.seaofnodes.simple.node.cpus.x86_64_v2; - -import com.seaofnodes.simple.codegen.*; -import com.seaofnodes.simple.node.Node; -import com.seaofnodes.simple.util.Utils; - -public class SarX86 extends RegX86 { - SarX86( Node add ) { super(add); } - @Override public String op() { return "sar"; } - @Override public String glabel() { return ">>"; } - @Override public RegMask regmap(int i) { - if (i == 1) return x86_64_v2.WMASK; - if (i == 2) return x86_64_v2.RCX_MASK; - throw Utils.TODO(); - } - @Override int opcode() { return 0xD3; } - @Override public void encoding( Encoding enc ) { - short dst = enc.reg(this ); // src1 - short src = enc.reg(in(2)); // src2 - enc.add1(x86_64_v2.rex(0, dst, 0)); - enc.add1(opcode()); // opcode - enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, 7, dst)); - } -} 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 deleted file mode 100644 index 0eff2afe..00000000 --- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SetX86.java +++ /dev/null @@ -1,54 +0,0 @@ -package com.seaofnodes.simple.node.cpus.x86_64_v2; - -import com.seaofnodes.simple.codegen.*; -import com.seaofnodes.simple.node.*; -import com.seaofnodes.simple.util.SB; -import com.seaofnodes.simple.util.Utils; - -// Corresponds to the x86 instruction "sete && setne". -// Use result of comparison without jump. -public class SetX86 extends MachConcreteNode implements MachNode { - final String _bop; // One of <,<=,== - final boolean _unsigned; - // Constructor expects input is an X86 and not an Ideal node. - SetX86( Node cmp, String bop, boolean unsigned ) { - super(cmp); - _inputs.setLen(1); // Pop the cmp inputs - // Replace with the matched cmp - _inputs.push(cmp); - _bop = bop; - _unsigned = unsigned; - } - @Override public String op() { return "set"+_bop; } - @Override public RegMask regmap(int i) { assert i==1; return x86_64_v2.FLAGS_MASK; } - @Override public RegMask outregmap() { return x86_64_v2.WMASK; } - - @Override public void encoding( Encoding enc ) { - // REX + 0F 94 - short dst = enc.reg(this ); - - // Optional rex, for dst - if( dst >= 4 ) enc.add1(x86_64_v2.rex(0, dst, 0, false)); - enc.add1(0x0F); // opcode - enc.add1(switch (_bop) { - case "==" -> 0x94; // SETE - case "<" -> _unsigned ? 0x92 : 0x9C; // SETB /SETL - case "<=" -> _unsigned ? 0x96 : 0x9E; // SETBE/SETLE - case ">=" -> _unsigned ? 0x93 : 0x9D; // SETAE/SETGE - default -> throw Utils.TODO(); - }); - enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, 0, dst)); - - // low 8 bites are set, now zero extend for next instruction - if( dst >= 4 ) enc.add1(x86_64_v2.rex(dst, dst, 0, false)); - enc.add1(0x0F); // opcode - enc.add1(0xB6); // opcode - enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, dst, dst)); - } - - @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); - } -} 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 deleted file mode 100644 index f7b2f28a..00000000 --- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShlIX86.java +++ /dev/null @@ -1,39 +0,0 @@ -package com.seaofnodes.simple.node.cpus.x86_64_v2; - -import com.seaofnodes.simple.codegen.*; -import com.seaofnodes.simple.node.*; -import com.seaofnodes.simple.node.Node; -import com.seaofnodes.simple.util.SB; - -public class ShlIX86 extends MachConcreteNode implements MachNode { - final int _imm; - ShlIX86( Node shli, int imm ) { - super(shli); - assert x86_64_v2.imm8(imm); - _inputs.pop(); // Pop constant input off - _imm = imm; - } - public int opcode() { return 0xC1; } - public int mod() { return 4; } - - @Override public RegMask regmap(int i) { return x86_64_v2.RMASK; } - @Override public RegMask outregmap() { return x86_64_v2.WMASK; } - @Override public int twoAddress() { return 1; } - - @Override public String op() { return "shli"; } - @Override public String glabel() { return "<<"; } - @Override public void encoding(Encoding enc) { - short dst = enc.reg(this); // Also src1 - enc.add1(x86_64_v2.rex(0, dst, 0)); - enc.add1( opcode()); - - enc.add1( x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, mod(), dst) ); - - // immediate(4 bytes) 32 bits or (1 byte)8 bits - if( x86_64_v2.imm8(_imm) ) enc.add1(_imm); - else enc.add4(_imm); - } - @Override public void asm(CodeGen code, SB sb) { - sb.p(code.reg(this)).p(" ").p(glabel()).p("= #").p(_imm); - } -} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShlX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShlX86.java deleted file mode 100644 index 5fee72d9..00000000 --- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShlX86.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.seaofnodes.simple.node.cpus.x86_64_v2; - -import com.seaofnodes.simple.codegen.*; -import com.seaofnodes.simple.node.Node; -import com.seaofnodes.simple.util.Utils; - -public class ShlX86 extends RegX86 { - ShlX86( Node add ) { super(add); } - @Override public String op() { return "shl"; } - @Override public String glabel() { return "<<"; } - @Override public RegMask regmap(int i) { - if (i == 1) return x86_64_v2.WMASK; - if (i == 2) return x86_64_v2.RCX_MASK; - throw Utils.TODO(); - } - @Override int opcode() { return 0xD3; } - @Override public final void encoding( Encoding enc ) { - short dst = enc.reg(this ); // src1 - short src = enc.reg(in(2)); // src2 - enc.add1(x86_64_v2.rex(0, dst, 0)); - enc.add1(opcode()); // opcode - enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, 4, dst)); - } -} 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 deleted file mode 100644 index 7e0378b1..00000000 --- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShrIX86.java +++ /dev/null @@ -1,34 +0,0 @@ -package com.seaofnodes.simple.node.cpus.x86_64_v2; - -import com.seaofnodes.simple.codegen.*; -import com.seaofnodes.simple.node.*; -import com.seaofnodes.simple.node.Node; -import com.seaofnodes.simple.util.SB; - -public class ShrIX86 extends MachConcreteNode implements MachNode { - final int _imm; - ShrIX86( Node shri, int imm ) { super(shri); assert x86_64_v2.imm8(imm); _inputs.pop(); _imm = imm; } - @Override public String op() { return "shri"; } - @Override public String glabel() { return ">>>"; } - int opcode() { return 0xC1; } - int mod() { return 5; } - - @Override public RegMask regmap(int i) { return x86_64_v2.RMASK; } - @Override public RegMask outregmap() { return x86_64_v2.WMASK; } - @Override public int twoAddress() { return 1; } - - @Override public void encoding(Encoding enc) { - short dst = enc.reg(this); // Also src1 - enc.add1(x86_64_v2.rex(0, dst, 0)); - enc.add1( opcode()); - - enc.add1( x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, mod(), dst) ); - - // immediate(4 bytes) 32 bits or (1 byte)8 bits - if( x86_64_v2.imm8(_imm) ) enc.add1(_imm); - else enc.add4(_imm); - } - @Override public void asm(CodeGen code, SB sb) { - sb.p(code.reg(this)).p(" ").p(glabel()).p("= #").p(_imm); - } -} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShrX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShrX86.java deleted file mode 100644 index 67dcf055..00000000 --- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShrX86.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.seaofnodes.simple.node.cpus.x86_64_v2; - -import com.seaofnodes.simple.codegen.*; -import com.seaofnodes.simple.node.Node; -import com.seaofnodes.simple.util.Utils; - -public class ShrX86 extends RegX86 { - ShrX86( Node add ) { super(add); } - @Override public String op() { return "shr"; } - @Override public String glabel() { return ">>>"; } - @Override public RegMask regmap(int i) { - if (i == 1) return x86_64_v2.WMASK; - if (i == 2) return x86_64_v2.RCX_MASK; - throw Utils.TODO(); - } - @Override int opcode() { return 0xD3; } - @Override public final void encoding( Encoding enc ) { - short dst = enc.reg(this ); // src1 - short src = enc.reg(in(2)); // src2 - enc.add1(x86_64_v2.rex(0, dst, 0)); - enc.add1(opcode()); // opcode - enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, 5, dst)); - } -} 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 deleted file mode 100644 index 4a14240a..00000000 --- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SplitX86.java +++ /dev/null @@ -1,107 +0,0 @@ -package com.seaofnodes.simple.node.cpus.x86_64_v2; - -import com.seaofnodes.simple.codegen.*; -import com.seaofnodes.simple.node.FunNode; -import com.seaofnodes.simple.node.Node; -import com.seaofnodes.simple.node.SplitNode; -import com.seaofnodes.simple.type.Type; -import com.seaofnodes.simple.type.TypeFloat; -import com.seaofnodes.simple.type.TypeInteger; -import com.seaofnodes.simple.util.SB; -import com.seaofnodes.simple.util.Utils; - -public class SplitX86 extends SplitNode { - SplitX86( String kind, byte round ) { super(kind,round, new Node[2]); } - @Override public String op() { return "mov"; } - @Override public RegMask regmap(int i) { return x86_64_v2.SPLIT_MASK; } - @Override public RegMask outregmap() { return x86_64_v2.SPLIT_MASK; } - - // Need to handle 8 cases: . reg->reg, reg->xmm, reg->flags, xmm->reg, xmm->xmm, xmm->flags, flags->reg, flags->xmm, - // flags->flags. - @Override public void encoding( Encoding enc ) { - // REX.W + 8B /r MOV r64, r/m64 - short dst = enc.reg(this ); - short src = enc.reg(in(1)); - if( dst == x86_64_v2.FLAGS ) { - // mov reg, flags - // push rcx - // popf (Pop the top of the stack into the FLAGS register) - // 50+rd PUSH r64 - x86_64_v2.rexF(0,src,0,false,enc); - enc.add1(0x50 + src); - // popf - enc.add1(0x9D); - return; - } - if( src == x86_64_v2.FLAGS ) { - // mov flags, reg - // pushf; pop reg - enc.add1(0x9C); - // 58+ rd POP r64 - x86_64_v2.rexF(0,dst,0,false, enc); - enc.add1(0x58 + dst); - return; - } - - // Flag for being either XMM or stack - boolean dstX = dst >= x86_64_v2.XMM_OFFSET; - boolean srcX = src >= x86_64_v2.XMM_OFFSET; - - // Stack spills - if( dst >= x86_64_v2.MAX_REG ) { - int off = enc._fun.computeStackOffset(enc._code,dst); - if( src >= x86_64_v2.MAX_REG ) { - // Rare stack-stack move. push [RSP+soff]; pop [RSP+doff] - int soff = enc._fun.computeStackOffset(enc._code,src); - enc.add1(0xFF); - x86_64_v2.indirectAdr(0, (short)-1/*index*/, (short)x86_64_v2.RSP, soff, 6, enc); - enc.add1(0x8F); - x86_64_v2.indirectAdr(0, (short)-1/*index*/, (short)x86_64_v2.RSP, off, 0, enc); - return; - } - StoreX86.encVal(enc, srcX ? TypeFloat.F64 : TypeInteger.BOT, (short)x86_64_v2.RSP, (short)-1/*index*/, src, off, 0); - return; - } - if( src >= x86_64_v2.MAX_REG ) { - int off = enc._fun.computeStackOffset(enc._code,src); - LoadX86.enc(enc, dstX ? TypeFloat.F64 : TypeInteger.BOT, dst, (short)x86_64_v2.RSP, (short)-1, off, 0); - return; - } - - // reg-reg move. Adjust numbering for GPR vs FPR reg set - if( dstX ) dst -= (short) x86_64_v2.XMM_OFFSET; - if( srcX ) src -= (short) x86_64_v2.XMM_OFFSET; - - // 0x66 if moving between register classes - if( dstX ^ srcX ) enc.add1(0x66); - if( !dstX && srcX ) - { short tmp=src; src=dst; dst=tmp; } - enc.add1(x86_64_v2.rex(dst, src, 0)); - - // pick opcode based on regs - if( !dstX && !srcX ) { - // reg->reg (MOV r64, r/m64) - enc.add1(0x8B); - } else if( dstX && srcX ) { - // xmm->xmm (NP 0F 28 /r MOVAPS xmm1, xmm2/m128) - enc.add1(0x0F); - enc.add1(0x28); - } else if( dstX && !srcX ) { - // reg->xmm (66 REX.W 0F 6E /r MOVQ xmm, r/m64) - enc.add1(0x0F); - enc.add1(0x6E); - } else if( !dstX && srcX ) { - // xmm->reg(66 0F 7E /r MOVQ r/m64, xmm) - enc.add1(0x0F); - enc.add1(0x7E); - } - - enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, dst, src)); - } - - // General form: "mov dst = src" - @Override public void asm(CodeGen code, SB sb) { - FunNode fun = code._encoding==null ? null : code._encoding._fun; - sb.p(code.reg(this,fun)).p(" = ").p(code.reg(in(1),fun)); - } -} 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 deleted file mode 100644 index 4403b02b..00000000 --- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/StoreX86.java +++ /dev/null @@ -1,85 +0,0 @@ -package com.seaofnodes.simple.node.cpus.x86_64_v2; - -import com.seaofnodes.simple.codegen.*; -import com.seaofnodes.simple.node.ConstantNode; -import com.seaofnodes.simple.node.Node; -import com.seaofnodes.simple.node.StoreNode; -import com.seaofnodes.simple.type.Type; -import com.seaofnodes.simple.type.TypeFloat; -import com.seaofnodes.simple.util.SB; -import java.util.BitSet; - -public class StoreX86 extends MemOpX86 { - StoreX86( StoreNode st, Node base, Node idx, int off, int scale, int imm, Node val ) { - super(st,st, base, idx, off, scale, imm, val); - } - @Override public String op() { return "st"+_sz; } - @Override public StringBuilder _printMach(StringBuilder sb, BitSet visited) { - Node val = val(); - sb.append(".").append(_name).append("="); - if( val==null ) sb.append(_imm); - else val._print0(sb,visited); - return sb.append(";"); - } - // Register mask allowed as a result. 0 for no register. - @Override public RegMask outregmap() { return null; } - @Override public void encoding( Encoding enc ) { - // REX.W + C7 /0 id MOV r/m64, imm32 | - // REX.W + 89 /r MOV r/m64, r64 - short ptr = enc.reg(ptr()); - short idx = enc.reg(idx()); - short src = enc.reg(val()); - - if( src == -1 ) { - int log = _declaredType.log_size(); - x86_64_v2.rexF(-1, ptr, idx, log==3, enc); - switch( log ) { - case 0: enc .add1(0xC6); break; - case 1: enc.add1(0x66).add1(0xC7); break; - case 2: enc .add1(0xC7); break; - case 3: enc .add1(0xC7); break; - } - x86_64_v2.indirectAdr(_scale, idx, ptr, _off, src, enc); - switch( log ) { - case 0: enc.add1(_imm); break; - case 1: enc.add2(_imm); break; - case 2: enc.add4(_imm); break; - case 3: enc.add4(_imm); break; // Limit of a 4 byte immediate - } - } else { - encVal(enc,_declaredType,ptr,idx,src,_off,_scale); - } - } - - // Non-immediate encoding - static void encVal( Encoding enc, Type decl, short ptr, short idx, short src, int off, int scale ) { - int log = decl.log_size(); - // Float reg being stored - if( src >= x86_64_v2.XMM_OFFSET ) { - src -= (short)x86_64_v2.XMM_OFFSET; - assert log == 2 || log == 3; - enc.add1( log==2 ? 0xF3 : 0xF2 ); - x86_64_v2.rexF(src,ptr,idx,false,enc); - enc.add1(0x0F).add1(0x11); - } else { - // byte stores from sil, dil, bpl, spl need a rex - if( log == 0 && src >= x86_64_v2.RSP ) enc.add1(x86_64_v2.rex(src,ptr,idx,false)); - else x86_64_v2.rexF(src,ptr,idx,log==3,enc); - switch( log ) { - case 0: enc .add1(0x88); break; - case 1: enc.add1(0x66).add1(0x89); break; - case 2: enc .add1(0x89); break; - case 3: enc .add1(0x89); break; - } - } - x86_64_v2.indirectAdr(scale, idx, ptr, off, src, enc); - } - - - // General form: "stN [base + idx<<2 + 12],val" - @Override public void asm(CodeGen code, SB sb) { - asm_address(code,sb).p(","); - if( val()==null ) sb.p("#").p(_imm); - else sb.p(code.reg(val())); - } -} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SubFX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SubFX86.java deleted file mode 100644 index b6530a87..00000000 --- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SubFX86.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.seaofnodes.simple.node.cpus.x86_64_v2; - -import com.seaofnodes.simple.codegen.*; -import com.seaofnodes.simple.node.*; -import com.seaofnodes.simple.util.SB; - -public class SubFX86 extends MachConcreteNode implements MachNode { - SubFX86( Node subf) { super(subf); } - @Override public String op() { return "subf"; } - @Override public RegMask regmap(int i) { assert i==1 || i==2; return x86_64_v2.XMASK; } - @Override public RegMask outregmap() { return x86_64_v2.XMASK; } - @Override public int twoAddress() { return 1; } - - @Override public void encoding( Encoding enc ) { - // F2 0F 5C /r SUBSD xmm1, xmm2/m64 - short dst = (short)(enc.reg(this ) - x86_64_v2.XMM_OFFSET); - short src = (short)(enc.reg(in(2)) - x86_64_v2.XMM_OFFSET); - // Fopcode - enc.add1(0xF2); - // rex prefix must come next (REX.W is not set) - x86_64_v2.rexF(dst, src, 0, false, enc); - - enc.add1(0x0F).add1(0x5C); - enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, dst, src)); - } - - // General form: "subf dst -= src" - @Override public void asm(CodeGen code, SB sb) { - sb.p(code.reg(this)).p(" -= ").p(code.reg(in(2))); - } -} 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 deleted file mode 100644 index 8da34bf5..00000000 --- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SubX86.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.seaofnodes.simple.node.cpus.x86_64_v2; - -import com.seaofnodes.simple.node.Node; - -public class SubX86 extends RegX86 { - SubX86( Node add ) { super(add); } - @Override public String op() { return "sub"; } - @Override public String glabel() { return "-"; } - @Override int opcode() { return 0x2B; } -} 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 deleted file mode 100644 index 359b6d83..00000000 --- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/TFPX86.java +++ /dev/null @@ -1,67 +0,0 @@ -package com.seaofnodes.simple.node.cpus.x86_64_v2; - -import com.seaofnodes.simple.codegen.*; -import com.seaofnodes.simple.node.*; -import com.seaofnodes.simple.node.cpus.riscv.riscv; -import com.seaofnodes.simple.type.Type; -import com.seaofnodes.simple.type.TypeFunPtr; -import com.seaofnodes.simple.util.SB; - -// Function constants -public class TFPX86 extends ConstantNode implements MachNode, RIPRelSize { - TFPX86( ConstantNode con ) { super(con); } - @Override public String op() { - return _con == Type.NIL ? "xor" : "ldx"; - } - @Override public boolean isClone() { return true; } - @Override public Node copy() { return new TFPX86(this); } - @Override public RegMask regmap(int i) { return null; } - @Override public RegMask outregmap() { return x86_64_v2.WMASK; } - // Zero-set uses XOR kills flags - @Override public RegMask killmap() { - return _con == Type.NIL ? x86_64_v2.FLAGS_MASK : null; - } - - @Override public void encoding( Encoding enc ) { - short dst = enc.reg(this); - // Short form for zero - if( _con==Type.NIL ) { - // XOR dst,dst. Can skip REX is dst is low 8, makes this a 32b - // xor, which will also zero the high bits. - if( dst >= 8 ) enc.add1(x86_64_v2.rex(dst, dst, 0)); - enc.add1(0x33); // opcode - enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, dst, dst)); - return; - } - - // lea to load function pointer address - // lea rax, [rip+disp32] - enc.relo(this); - enc.add1(x86_64_v2.rex(dst, 0, 0)); - enc.add1(0x8D); // opcode - enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.INDIRECT, dst, 0b101)); - enc.add4(0); - } - - @Override public byte encSize(int delta) { - return 7; - } - - // Delta is from opcode start - @Override public void patch( Encoding enc, int opStart, int opLen, int delta ) { - enc.patch4(opStart + 3, delta - 7); - } - - // Human-readable form appended to the SB. Things like the encoding, - // indentation, leading address or block labels not printed here. - // Just something like "ld4\tR17=[R18+12] // Load array base". - // General form: "op\tdst=src+src" - @Override public void asm(CodeGen code, SB sb) { - String reg = code.reg(this); - if( _con == Type.NIL ) - sb.p(reg).p(",").p(reg); - else - _con.print(sb.p(reg).p(" = #")); - } - @Override public boolean eq(Node n) { return this==n; } -} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/TMPX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/TMPX86.java deleted file mode 100644 index c23f6c5f..00000000 --- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/TMPX86.java +++ /dev/null @@ -1,39 +0,0 @@ -package com.seaofnodes.simple.node.cpus.x86_64_v2; - -import com.seaofnodes.simple.codegen.*; -import com.seaofnodes.simple.node.*; -import com.seaofnodes.simple.type.TypeMemPtr; -import com.seaofnodes.simple.util.SB; - -public class TMPX86 extends ConstantNode implements MachNode, RIPRelSize{ - TMPX86(ConstantNode con ) { super(con); } - @Override public String op() { return "ldp"; } - @Override public RegMask regmap(int i) { return null; } - @Override public RegMask outregmap() { return x86_64_v2.WMASK; } - @Override public boolean isClone() { return true; } - @Override public Node copy() { return new TMPX86(this); } - - @Override public void encoding( Encoding enc ) { - short dst = enc.reg(this); - x86_64_v2.rexF(dst, 0, 0, true, enc); - enc.add1(0x8D).add1(x86_64_v2.modrm(x86_64_v2.MOD.INDIRECT, dst, 0b101)).add4(0); - int opLen = 1/*rexF*/+1/*opcode 0x8D*/+1/*modrm*/+4/*pc rel offset*/; - // Base of constant array - TypeMemPtr tmp = (TypeMemPtr)_con; - int baseOff = tmp._obj.aryBase(); - enc.largeConstant(this,tmp._obj,opLen-baseOff,2/*ELF encoding PC32*/); - } - - // Delta is from opcode start - @Override public byte encSize(int delta) { return (byte)7; } - - // Delta is from opcode start - @Override public void patch( Encoding enc, int opStart, int opLen, int delta ) { - enc.patch4(opStart+opLen-4,delta-opLen); - } - - @Override public void asm(CodeGen code, SB sb) { - _con.print(sb.p(code.reg(this)).p(" = #")); - } - @Override public boolean eq(Node n) { return this==n; } -} diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/UJmpX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/UJmpX86.java deleted file mode 100644 index 92d2a87d..00000000 --- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/UJmpX86.java +++ /dev/null @@ -1,54 +0,0 @@ -package com.seaofnodes.simple.node.cpus.x86_64_v2; - -import com.seaofnodes.simple.codegen.*; -import com.seaofnodes.simple.node.*; -import com.seaofnodes.simple.type.Type; -import com.seaofnodes.simple.util.SB; -import com.seaofnodes.simple.util.Utils; -import java.util.BitSet; - -// unconditional jump -public class UJmpX86 extends CFGNode implements MachNode, RIPRelSize { - UJmpX86() { } - @Override public String op() { return "jmp"; } - @Override public String label() { return op(); } - @Override public StringBuilder _print1( StringBuilder sb, BitSet visited ) { - return sb.append("jmp "); - } - @Override public RegMask regmap(int i) {return null; } - @Override public RegMask outregmap() { return null; } - @Override public Type compute() { throw Utils.TODO(); } - @Override public Node idealize() { throw Utils.TODO(); } - @Override public void encoding( Encoding enc ) { - enc.jump(this,uctrl()); - // Short-form jump - enc.add1(0xEB).add1(0); - //// E9 cd JMP rel32 - //enc.add1(0xE9).add4(0); - } - - // Delta is from opcode start, but X86 measures from the end of the 2-byte encoding - @Override public byte encSize(int delta) { return (byte)(x86_64_v2.imm8(delta-2) ? 2 : 5); } - - // Delta is from opcode start - @Override public void patch( Encoding enc, int opStart, int opLen, int delta ) { - byte[] bits = enc.bits(); - if( opLen==2 ) { - assert bits[opStart] == (byte)0xEB; - delta -= 2; // Offset from opcode END - assert (byte)delta==delta; - bits[opStart+1] = (byte)delta; - } else { - assert bits[opStart] == (byte)0xEB; - bits[opStart] = (byte)0xE9; // Long form - delta -= 5; // Offset from opcode END - enc.patch4(opStart+1,delta); - } - } - - @Override public void asm(CodeGen code, SB sb) { - CFGNode target = uctrl(); - //assert target.nOuts() > 1; // Should optimize jmp to empty targets - sb.p(label(target)); - } -} 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 deleted file mode 100644 index 2d989a50..00000000 --- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/XorIX86.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.seaofnodes.simple.node.cpus.x86_64_v2; - -import com.seaofnodes.simple.node.Node; - -public class XorIX86 extends ImmX86 { - XorIX86( Node add, int imm ) { super(add,imm); } - @Override public String op() { return "xori"; } - @Override public String glabel() { return "^"; } - @Override int opcode() { return 0x81; } - @Override int mod() { return 6; } -} 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 deleted file mode 100644 index 86703903..00000000 --- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/XorX86.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.seaofnodes.simple.node.cpus.x86_64_v2; - -import com.seaofnodes.simple.node.Node; - -public class XorX86 extends RegX86 { - XorX86( Node add ) { super(add); } - @Override public String op() { return "xor"; } - @Override public String glabel() { return "^"; } - @Override int opcode() { return 0x33; } - @Override public boolean commutes() { return true; } -} 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 deleted file mode 100644 index 026e12aa..00000000 --- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/x86_64_v2.java +++ /dev/null @@ -1,649 +0,0 @@ -package com.seaofnodes.simple.node.cpus.x86_64_v2; - -import com.seaofnodes.simple.codegen.*; -import com.seaofnodes.simple.node.*; -import com.seaofnodes.simple.type.*; -import com.seaofnodes.simple.util.Utils; - -public class x86_64_v2 extends Machine { - public x86_64_v2( CodeGen code ) {} - // X86-64 V2. Includes e.g. SSE4.2 and POPCNT. - @Override public String name() { return "x86_64_v2"; } - @Override public int defaultOpSize() { return 5; } - - public static final int RAX = 0, RCX = 1, RDX = 2, RBX = 3, RSP = 4, RBP = 5, RSI = 6, RDI = 7; - public static final int R08 = 8, R09 = 9, R10 = 10, R11 = 11, R12 = 12, R13 = 13, R14 = 14, R15 = 15; - - public static final int XMM0 = 16, XMM1 = 17, XMM2 = 18, XMM3 = 19, XMM4 = 20, XMM5 = 21, XMM6 = 22, XMM7 = 23; - public static final int XMM8 = 24, XMM9 = 25, XMM10 = 26, XMM11 = 27, XMM12 = 28, XMM13 = 29, XMM14 = 30, XMM15 = 31; - public static final int FLAGS = 32; - public static final int MAX_REG = 33; - public static final int RPC = 33; - - // Human-readable name for a register number, e.g. "RAX". - public static final String[] REGS = new String[]{ - "rax", "rcx", "rdx", "rbx", "rsp", "rbp", "rsi", "rdi", - "r8" , "r9" , "r10", "r11", "r12", "r13", "r14", "r15", - "xmm0", "xmm1", "xmm2" , "xmm3" , "xmm4" , "xmm5" , "xmm6" , "xmm7" , - "xmm8", "xmm9", "xmm10", "xmm11", "xmm12", "xmm13", "xmm14", "xmm15", - "flags", - }; - // Hard crash for bad register number, fix yer bugs! - @Override public String[] regs() { return REGS; } - - public static int XMM_OFFSET = 16; - // General purpose register mask: pointers and ints, not floats - static final long RD_BITS = 0b1111111111111111L; // All the GPRs - static RegMask RMASK = new RegMask(RD_BITS); - // No RSP in the *write* general set. - static final long WR_BITS = 0b1111111111101111L; // All the GPRs minus RSP - static RegMask WMASK = new RegMask(WR_BITS); - // Xmm register mask - static final long FP_BITS = 0b1111111111111111L << XMM0; // All the XMMs - static RegMask XMASK = new RegMask(FP_BITS); - static RegMask FLAGS_MASK = new RegMask(FLAGS); - static RegMask RPC_MASK = new RegMask(RPC); - // Divide mask: exclude RDX:RAX - static RegMask DIV_MASK = new RegMask(RD_BITS & ~(1L< 0x84; - case "!=" -> 0x85; - case ">" -> 0x8F; - case "<" -> 0x8C; - case "<=" -> 0x8E; - case ">=" -> 0x8D; - default -> throw new IllegalArgumentException("Too many arguments"); - }; - } - - public static int modrm(MOD mod, int reg, int m_r) { - if( reg == -1 ) reg=0; // Missing reg in this flavor - // combine all the bits - assert 0 <= reg && reg < 16; - assert 0 <= m_r && m_r < 16; - return (mod.ordinal() << 6) | ((reg & 0x07) << 3) | m_r & 0x07; - } - - // 00 000 000 - // same bit-layout as modrm - public static int sib(int scale, int index, int base) { - assert 0 <= base && base < 16; - assert 0 <= index && index < 16; - return (scale << 6) | ((index & 0x07) << 3) | base & 0x07; - } - - // reg1 is reg(R) - // reg2 is r/mem(B) - // reg3 is X(index) - // reg4 is X(base) - - // 0 denotes no direct register - public static int rex(int reg, int ptr, int idx, boolean wide) { - // assuming 64 bit by default so: 0100 1000 - assert -1 <= reg && reg < 16; - assert -1 <= ptr && ptr < 16; - assert -1 <= idx && idx < 16; - - int rex = wide ? REX_W : REX; - if( 8 <= reg ) rex |= 0b00000100; // REX.R - if( 8 <= ptr ) rex |= 0b00000001; // REX.B - if( 8 <= idx ) rex |= 0b00000010; // REX.X - return rex; - } - - public static int rex(int reg, int ptr, int idx) { - return rex(reg, ptr, idx, true); - } - - // rex for floats. Return size (0 or 1) - // don't need to use REX if 0x40. - public static byte rexF(int reg, int ptr, int idx, boolean wide, Encoding enc) { - int rex = rex(reg, ptr, idx, wide); - if (rex == REX) return 0; - enc.add1(rex); - return 1; - } - // Function used for encoding indirect memory addresses - // Does not always generate SIB byte e.g index == -1. - // -1 denotes empty value, not set - note 0 is different from -1 as it can represent rax. - // Looks for best mod locally - public static void indirectAdr( int scale, short index, short base, int offset, int reg, Encoding enc ) { - // Assume indirect - assert 0 <= base && base < 16; - assert -1 <= index && index < 16 && index != RSP; - - MOD mod = MOD.INDIRECT; - // is 1 byte enough or need more? - if( offset != 0 ) - mod = imm8(offset) - ? MOD.INDIRECT_disp8 - : MOD.INDIRECT_disp32; - - if( mod == MOD.INDIRECT && (base == RBP || base == R13) ) { - mod = MOD.INDIRECT_disp8; - } else if( index == -1 && (base == RSP || base == R12) ) { - index = RSP; - } - - if( index == -1 ) { - enc.add1(modrm(mod, reg, base)); - } else { - enc.add1(modrm(mod, reg, x86_64_v2.RSP)); - enc.add1(sib(scale, index, base)); - } - - if( mod == MOD.INDIRECT_disp8 ) { - enc.add1(offset); - } else if( mod == MOD.INDIRECT_disp32 ) { - enc.add4(offset); - } - } - - - // Limit of float args passed in registers - static RegMask[] XMMS8 = new RegMask[]{ - new RegMask(XMM0), new RegMask(XMM1), new RegMask(XMM2), new RegMask(XMM3), - new RegMask(XMM4), new RegMask(XMM5), new RegMask(XMM6), new RegMask(XMM7), - }; - - // Map from function signature and argument index to register. - // Used to set input registers to CallNodes, and ParmNode outputs. - @Override public RegMask callArgMask( TypeFunPtr tfp, int idx, int maxArgSlot ) { return callInMask(tfp,idx,maxArgSlot); } - static RegMask callInMask( TypeFunPtr tfp, int idx, int maxArgSlot ) { - if( idx==0 ) return RPC_MASK; - if( idx==1 ) return null; - if( idx-2 >= tfp.nargs() ) return null; // Anti-dependence - return switch( CodeGen.CODE._callingConv ) { - case "SystemV" -> callSys5 (tfp,idx,maxArgSlot); - case "Win64" -> callWin64(tfp,idx,maxArgSlot); - default -> throw Utils.TODO(); - }; - } - - // Maximum stack args used by this signature - @Override public short maxArgSlot( TypeFunPtr tfp ) { - return switch( CodeGen.CODE._callingConv ) { - case "SystemV" -> maxArgSlotSys5 (tfp); - case "Win64" -> maxArgSlotWin64(tfp); - default -> throw Utils.TODO(); - }; - } - - // Win64 - max *4* args in registers of all kinds; after that on stack. - // Stack args land in increasing memory order with empty/mirror slots for - // every register. - // - // foo( int i0, flt f1, int i2, flt f3, int i4, flt f5, int i6, ... ) - // - // -- Prior Frame -- - // i6 - // f5 - // i4 - // empty mirror, XMM3= f3 - // empty mirror, R08 = i2 - // empty mirror, XMM1= f1 - // empty mirror, RCX = i0 - // -- Caller Frame; 16b align -- - // RPC - // PAD/ALIGN - // -- Callee Frame; 16b align -- - static RegMask[] WIN64_CALL = new RegMask[] { - RCX_MASK, - RDX_MASK, - R08_MASK, - R09_MASK, - }; - - static RegMask callWin64(TypeFunPtr tfp, int idx, int maxArgSlot ) { - // idx 2,3,4,5 passed in registers, with stack slot mirrors. - // idx >= 6 passed on stack, starting at slot#1 (#0 reserved for RPC). - if( idx >= 6 ) - return new RegMask(MAX_REG+maxArgSlot+(idx-2)); - return tfp.arg(idx-2) instanceof TypeFloat - ? XMMS8 [idx-2] - : WIN64_CALL[idx-2]; - } - static short maxArgSlotWin64(TypeFunPtr tfp) { - return (short)tfp.nargs(); - } - - // Sys5: max 6 GPRs and 8 FPRS filled first. Extra args land in increasing - // memory order as needed - no mirror space. - // - // foo( int i0, flt f1, int i2, flt f3, int i4, flt f5, int i6, ... ) - // RDI =i0, RSI =i2, RDX =i4, RCX =i6, R08 =i8, R09 =i10 - // XMM0=f1, XMM1=f3, XMM2=f5, XMM3=f7, XMM4=f9, XMM5=f11, XMM6=f13, XMM7=f15 - // - // -- Prior Frame -- - // f18 - // i18 - // f17 - // i16 - // i14 - // i12 - // -- Caller Frame; 16b align -- - // RPC - // PAD/ALIGN - // -- Callee Frame; 16b align -- - // SystemV(param passing) - static RegMask[] SYS5_CALL = new RegMask[] { - RDI_MASK, - RSI_MASK, - RDX_MASK, - RCX_MASK, - R08_MASK, - R09_MASK, - }; - - static RegMask callSys5(TypeFunPtr tfp, int idx, int maxArgSlot ) { - // First 6 integers passed in registers: rdi,rsi,rdx,rcx,r08,r09 - // First 8 floats passed in registers: xmm0-xmm7 - int icnt=0, fcnt=0; // Count of ints, floats - for( int i=2; i SYSTEM5_CALLER_SAVE; - case "Win64" -> WIN64_CALLER_SAVE; - default -> throw new IllegalArgumentException("Unknown calling convention: " + CodeGen.CODE._callingConv); - }; - } - @Override public long neverSave() { return 1L< addf(addf); - case AddNode add -> add(add); - case AndNode and -> and(and); - case BoolNode bool -> cmp(bool); - case CProjNode c -> new CProjNode(c); - case CallEndNode cend -> new CallEndMach(cend); - case CallNode call -> call(call); - case CastNode cast -> new CastMach(cast); - case ConstantNode con -> con(con); - case DivFNode divf -> new DivFX86(divf); - case DivNode div -> new DivX86(div); - case FunNode fun -> new FunX86(fun); - case IfNode iff -> jmp(iff); - case LoadNode ld -> ld(ld); - case MemMergeNode mem -> new MemMergeNode(mem); - case MinusNode neg -> new NegX86(neg); - case MulFNode mulf -> new MulFX86(mulf); - case MulNode mul -> mul(mul); - case NewNode nnn -> new NewX86(nnn); - case NotNode not -> new NotX86(not); - case OrNode or -> or(or); - case ParmNode parm -> new ParmX86(parm); - case PhiNode phi -> new PhiNode(phi); - case ProjNode prj -> prj(prj); - case ReadOnlyNode read-> new ReadOnlyMach(read); - case ReturnNode ret -> new RetX86(ret, ret.fun()); - case SarNode sar -> sar(sar); - case ShlNode shl -> shl(shl); - case ShrNode shr -> shr(shr); - case StartNode start -> new StartNode(start); - case StopNode stop -> new StopNode(stop); - case StoreNode st -> st(st); - case SubFNode subf -> new SubFX86(subf); - case SubNode sub -> sub(sub); - case ToFloatNode tfn -> i2f8(tfn); - case XCtrlNode x -> new ConstantNode(Type.XCONTROL); - case XorNode xor -> xor(xor); - - case LoopNode loop -> new LoopNode(loop); - case RegionNode region -> new RegionNode(region); - default -> throw Utils.TODO(); - }; - } - - public static boolean imm8( long imm ) { return -128 <= imm && imm <= 127; } - - public static boolean imm32( long imm ) { return (int)imm==imm; } - - // Attempt a full LEA-style break down. - private Node add(AddNode add) { - Node lhs = add.in(1); - Node rhs = add.in(2); - if( lhs instanceof LoadNode ld && ld.nOuts() == 1 && ld._declaredType.log_size() >= 3) - return new AddMemX86(add, address(ld), ld.ptr(), idx, off, scale, 0, rhs); - -// if(rhs instanceof LoadNode ld && ld.nOuts() == 1 && ld._declaredType.log_size() >= 3) { -// throw Utils.TODO(); // Swap load sides -// } - - // Attempt a full LEA-style break down. - // Returns one of AddX86, AddIX86, LeaX86, or LHS - if( rhs instanceof ConstantNode off2 && off2._con instanceof TypeInteger toff ) { - long imm = toff.value(); - assert imm!=0; // Folded in peeps - if( (int)imm != imm ) // Full 64bit immediate - return new AddX86(add); - // Now imm <= 32bits - if( lhs instanceof AddNode ladd ) - // ((base + (idx << scale)) + off) - return _lea(add, ladd.in(1), ladd.in(2), (int)imm); - if( lhs instanceof ShlNode shift ) - // (idx << scale) + off; no base - return _lea(add, null, shift, (int)imm); - - // lhs + rhs1 - return new AddIX86(add, (int)imm); - } - return _lea(add, lhs, rhs, 0); - } - - - private Node addf(AddFNode addf) { - if(addf.in(1) instanceof LoadNode ld && ld.nOuts() == 1) - return new AddFMemX86(addf, address(ld), ld.ptr(), idx, off, scale, addf.in(2)); - -// if(addf.in(2) instanceof LoadNode ld && ld.nOuts() == 1) -// throw Utils.TODO(); // Swap load sides - - return new AddFX86(addf); - } - - - private Node _lea(Node add, Node base, Node idx, int off) { - int scale = 0; - if( base instanceof ShlNode && !(idx instanceof ShlNode) ) - throw Utils.TODO(); // Bug in canonicalization, should on RHS - if( idx instanceof ShlNode shift && shift.in(2) instanceof ConstantNode shfcon && - shfcon._con instanceof TypeInteger tscale && 0 <= tscale.value() && tscale.value() <= 3 ) { - idx = shift.in(1); - scale = ((int) tscale.value()); - } - // (base + idx) + off - return off == 0 && scale == 0 - ? new AddX86(add) - : new LeaX86(add, base, idx, scale, off); - } - - - private Node and(AndNode and) { - if( and.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti && imm32(ti.value()) ) - return new AndIX86(and, (int)ti.value()); - return new AndX86(and); - } - - private Node call(CallNode call) { - return call.fptr() instanceof ConstantNode con && con._con instanceof TypeFunPtr tfp - ? new CallX86(call, tfp) - : new CallRX86(call); - } - - // Because X86 flags, a normal ideal Bool is 2 X86 ops: a "cmp" and at "setz". - // Ideal If reading from a setz will skip it and use the "cmp" instead. - private static boolean swap, unsigned; - private Node cmp( BoolNode bool ) { - swap = unsigned = false; - Node cmp = _cmp(bool); - return new SetX86(cmp, swap ? IfNode.swap(bool.op()) : bool.op(), unsigned); - } - - private Node _cmp(BoolNode bool) { - // Float variant - if( bool.isFloat() ) { - unsigned = true; - return new CmpFX86(bool); - } - Node lhs = bool.in(1); - Node rhs = bool.in(2); - - // Since cmp does not record a BOP, SetX/Jmp need to know if the CMP is swapped. - - // Vs memory - if( lhs instanceof LoadNode ld && ld.nOuts() == 1 && rhs._type.isa(ld._declaredType) ) - return new CmpMemX86(bool, address(ld), ld.ptr(), idx, off, scale, imm(rhs), val, false); - - // Operands swap in the encoding directly, no need for Set/Jmp to swap `bop` - if( rhs instanceof LoadNode ld && ld.nOuts() == 1 && lhs._type.isa(ld._declaredType) && - (val!=null || bool.op()=="==" || bool.op()=="!=") ) - return new CmpMemX86(bool, address(ld), ld.ptr(), idx, off, scale, imm(lhs), val, true ); - - // Vs immediate - if( rhs instanceof ConstantNode con && con._con instanceof TypeInteger ti && imm32(ti.value()) ) - return new CmpIX86(bool, (int)ti.value(), false); - - // Operand swap compare; Set and Jmp need to swap `bop` - if( lhs instanceof ConstantNode con && con._con instanceof TypeInteger ti && imm32(ti.value()) ) - return new CmpIX86(bool, (int)ti.value(), swap=true); - - // 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 TMPX86(con); - case TypeNil tn -> throw Utils.TODO(); - // TOP, BOTTOM, XCtrl, Ctrl, etc. Never any executable code. - case Type t -> t == Type.NIL ? new IntX86(con) : new ConstantNode(con); - }; - } - - private Node i2f8(ToFloatNode tfn) { - assert tfn.in(1)._type instanceof TypeInteger ti; - return new I2f8X86(tfn); - } - - private Node jmp(IfNode iff) { - // If/Bool combos will match to a Cmp/Set which sets flags. - // Most general arith ops will also set flags, which the Jmp needs directly. - // Loads do not set the flags, and will need an explicit TEST - String op = "!="; - if( iff.in(1) instanceof BoolNode bool ) op = swap ? IfNode.swap(bool.op()) : bool.op(); - else if( iff.in(1)==null ) op = "=="; // Never-node cutout - else iff.setDef(1, new BoolNode.NE(iff.in(1), new ConstantNode(TypeInteger.ZERO))); - return new JmpX86(iff, op); - } - - private Node ld(LoadNode ld) { - return new LoadX86(address(ld), ld.ptr(), idx, off, scale); - } - - private Node mul(MulNode mul) { - if( mul.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti && imm32(ti.value()) ) - return new MulIX86(mul, (int)ti.value()); - return new MulX86(mul); - } - - private Node or(OrNode or) { - if( or.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti && imm32(ti.value()) ) - return new OrIX86(or, (int)ti.value()); - return new OrX86(or); - } - - private Node prj( ProjNode prj ) { - return new ProjX86(prj); - } - - private Node sar(SarNode sar) { - if( sar.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti && imm8(ti.value())) - return new SarIX86(sar, (int)(ti.value() & 0xff) ); - return new SarX86(sar); - } - - private Node shl(ShlNode shl) { - if( shl.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti && imm8(ti.value())) - return new ShlIX86(shl, (int)(ti.value() & 0xff) ); - return new ShlX86(shl); - } - - private Node shr( ShrNode shr ) { - if( shr.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti && imm8(ti.value()) ) - return new ShrIX86(shr, (int)(ti.value() & 0xff) ); - return new ShrX86(shr); - } - - private Node st( StoreNode st ) { - // Look for "*ptr op= val" - Node op = st.val(); - if( op instanceof AddNode ) { - if( op.in(1) instanceof LoadNode ld && stld_match(st,ld) ) - return new MemAddX86(address(st), st.ptr(), idx, off, scale, imm(op.in(2)), val); - if( op.in(2) instanceof LoadNode ld && stld_match(st,ld) ) - return new MemAddX86(address(st), st.ptr(), idx, off, scale, imm(op.in(1)), val); - } - return new StoreX86(address(st), st.ptr(), idx, off, scale, imm(st.val()), val); - } - private static boolean stld_match(StoreNode st, LoadNode ld ) { - return - ld.in(0) == st.in(0) && - ld.mem() == st.mem() && - ld.ptr() == st.ptr() && - ld.off() == st.off(); - } - - - private Node sub (SubNode sub ){ - return sub.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti - ? new AddIX86(sub, (int)-ti.value()) - : new SubX86(sub); - } - - private Node xor (XorNode xor){ - if( xor.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti && imm32(ti.value()) ) - return new XorIX86(xor, (int)ti.value()); - return new XorX86(xor); - } - - // Gather X86 addressing mode bits prior to constructing. This is a - // builder pattern, but saving the bits in a *local* *global* here to keep - // mess contained. - private static int off, scale, imm; - private static Node idx, val; - private N address(N mop) { - off = scale = imm = 0; // Reset - idx = val = null; - Node base = mop.ptr(); - // Skip/throw-away a ReadOnly, only used to typecheck - if(base instanceof ReadOnlyNode read) base = read.in(1); - assert !(base instanceof AddNode) && base._type instanceof TypeMemPtr; // Base ptr always, not some derived - if(mop.off() instanceof AddNode add && add.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti) { - off = (int) ti.value(); - assert off == ti.value(); // In 32-bit range - idx = add.in(1); - if(idx instanceof ShlNode shift && shift.in(2) instanceof ConstantNode shfcon && - shfcon._con instanceof TypeInteger tscale && 0 <= tscale.value() && tscale.value() <= 3) { - idx = shift.in(1); - scale = (int) tscale.value(); - } - } else { - if(mop.off() instanceof ConstantNode con && con._con instanceof TypeInteger ti) { - off = (int) ti.value(); - assert off == ti.value(); // In 32-bit range - } else { - idx = mop.off(); - } - } - return mop; - } - - private int imm( Node xval ) { - assert val == null && imm == 0; - if( xval instanceof ConstantNode con && con._con instanceof TypeInteger ti) { - val = null; - imm = (int) ti.value(); - assert imm == ti.value(); // In 32-bit range - } else { - val = xval; - } - return imm; - } - -} diff --git a/src/main/java/com/seaofnodes/simple/print/ASMPrinter.java b/src/main/java/com/seaofnodes/simple/print/ASMPrinter.java deleted file mode 100644 index a9f86ce8..00000000 --- a/src/main/java/com/seaofnodes/simple/print/ASMPrinter.java +++ /dev/null @@ -1,306 +0,0 @@ -package com.seaofnodes.simple.print; - -import com.seaofnodes.simple.codegen.CodeGen; -import com.seaofnodes.simple.codegen.Encoding.Relo; -import com.seaofnodes.simple.codegen.Encoding; -import com.seaofnodes.simple.node.*; -import com.seaofnodes.simple.type.*; -import com.seaofnodes.simple.util.Ary; -import com.seaofnodes.simple.util.SB; -import com.seaofnodes.simple.util.Utils; -import java.util.HashSet; - -public abstract class ASMPrinter { - - public static SB print(SB sb, CodeGen code) { - if( code._cfg==null ) - return sb.p("Need _cfg set, run after GCM"); - - // instruction address - int iadr = 0; - for( int i=0; i[] raligns = new Ary[5]; - for( Node op : enc._bigCons.keySet() ) { - Relo relo = enc._bigCons.get(op); - int align = relo._t.alignment(); - Ary relos = raligns[align]==null ? (raligns[align]=new Ary<>( Relo.class)) : raligns[align]; - relos.add(relo); - } - - HashSet targets = new HashSet<>(); - sb.p("--- Constant Pool ------").nl(); - - // By alignment - for( int align = 4; align >= 0; align-- ) { - Ary relos = raligns[align]; - if( relos == null ) continue; - for( Relo relo : relos ) { - if( targets.contains(relo._t) ) continue; - targets.add(relo._t); - sb.hex2(iadr).p(" "); - switch( relo._t ) { - case TypeTuple tt -> { - for( Type tx : tt._types ) { - pN(enc,sb,iadr,align).p(" "); - iadr += (1< { - int sz = ts.size(); - int log = 1< { - pN(enc,sb,iadr,align).fix(9-(1< CodeGen.Phase.RegAlloc.ordinal(); - final boolean postEncode= code._phase.ordinal() >=CodeGen.Phase.Encoding.ordinal(); - - boolean once=false; - for( Node n : bb.outs() ) { - if( !(n instanceof PhiNode phi) ) continue; - if( phi._type instanceof TypeMem || phi._type instanceof TypeRPC ) continue; // Nothing for the hidden ones - // Post-RegAlloc phi prints all on one line - if( postAlloc ) { - if( !once ) { once=true; sb.fix(4," ").p(" ").fix(encWidth,"").p(" "); } - sb.p(phi._label).p(':').p(code.reg(phi,fun)).p(','); - } else { - // Pre-RegAlloc phi prints one line per - sb.fix(4," ").p(" ").fix(encWidth,"").p(" ").fix(opWidth,phi._label).p(" ").p(code.reg(phi,fun)); - if( phi.getClass() == PhiNode.class ) { - sb.p(" = phi( "); - for( int i=1; i1 ) - break; // Has code in the block, need to jump around - // No code in the block, can fall through it - } - sb.hex2(iadr++).p(" ").fix(encWidth,"??").p(" ").fix(opWidth,"JMP").p(" ").fix(argWidth,label(cfg)).nl(); - return iadr; - } - - // ProjNodes following a multi (e.g. Call or New results), - // get indent slightly and just print their index & node# - if( n instanceof ProjNode proj ) { - if( proj._type instanceof TypeMem ) return iadr; // Nothing for the hidden ones - sb.fix(4," ").p(" ").fix(encWidth,"").p(" ").fix(opWidth,proj._label==null ? "---" : proj._label).p(" ").p(code.reg(n,fun)).nl(); - return iadr; - } - - // ADDR ENCODING Op--- dst = src op src // Comment - // 1234 abcdefgh ld4 RAX = [Rbase + off] // Comment - sb.hex2(iadr).p(" "); - - // Encoding - int fatEncoding = 0; - if( code._encoding != null && code._encoding._opLen!=null ) { - int size = code._encoding._opLen[n._nid]; - if( code._asmLittle ) - for( int i=0; i>1); // Not-printed parts of encoding - } else - sb.fix(encWidth,""); - sb.p(" "); - - // Op; generally "ld4" or "call" - sb.fix(opWidth, n instanceof MachNode mach ? mach.op() : n.label()).p(" "); - - // General asm args - String isMultiOp = null; - if( n instanceof MachNode mach ) { - int old = sb.len(); - mach.asm(code,sb); - int len = sb.len(); - isMultiOp = isMultiOp(sb,old,len); - sb.fix(argWidth-(len-old),""); // Pad out - - } else if( !(n._type instanceof TypeMem) ) { - // Room for some inputs - sb.fix(5, n._nid+":" ); - int off = 0; - for( int i=0; i= argWidth ) break; - } - if( off < argWidth ) sb.fix(argWidth-off,""); - } - sb.p(" "); - - // Comment - String comment = n.comment(); - if( comment!=null ) sb.p("// ").p(comment); - - sb.nl(); - - // Printing more op bits than fit - if( isMultiOp != null && code._encoding != null && code._encoding._opLen!=null ) { - // Multiple ops, template style, no RA, no scheduling. Print out - // one-line-per-newline, with encoding bits up front. - int size = code._encoding._opLen[n._nid]; - int off = Math.min(size,dopz); - while( isMultiOp!=null ) { - sb.hex2(iadr).p(" "); - int len = Math.min(size-off,dopz); - for( int i=0; i 0 ) { - // Extra bytes past the default encoding width, all put on a line by - // themselves. X86 special for super long encodings - sb.hex2(iadr).p(" "); - for( int i=0; i xScopes) { - - // Since the graph has cycles, we need to create a flat list of all the - // nodes in the graph. - Collection all = findAll(xScopes, stop, scope); - StringBuilder sb = new StringBuilder(); - sb.append("digraph chapter18 {\n"); - sb.append("/*\n"); - sb.append(stop._src); - sb.append("\n*/\n"); - - // To keep the Scopes below the graph and pointing up into the graph we - // need to group the Nodes in a subgraph cluster, and the scopes into a - // different subgraph cluster. THEN we can draw edges between the - // scopes and nodes. If we try to cross subgraph cluster borders while - // still making the subgraphs DOT gets confused. - sb.append("\trankdir=BT;\n"); // Force Nodes before Scopes - - // CNC - turned off Apr/8/2024, gives more flex in the layout and - // removes some of the more ludicrous layout choices. - // Preserve node input order - //sb.append("\tordering=\"in\";\n"); - - // Merge multiple edges hitting the same node. Makes common shared - // nodes much prettier to look at. - sb.append("\tconcentrate=\"true\";\n"); - - // Force nested scopes to order - sb.append("\tcompound=\"true\";\n"); - - // Just the Nodes first, in a cluster no edges - nodes(sb, all); - - // Now the scopes, in a cluster no edges - if( xScopes != null ) - for( ScopeNode xscope : xScopes ) - if( !xscope.isDead() ) - scope( sb, xscope ); - - // Walk the Node edges - nodeEdges(sb, all); - - // Walk the active Scope edges - if( xScopes != null ) - for( ScopeNode xscope : xScopes ) - if( !xscope.isDead() ) - scopeEdges( sb, xscope ); - - sb.append("}\n"); - return sb.toString(); - } - - private void nodesByCluster(StringBuilder sb, boolean doCtrl, Collection all) { - if (!_separateControlCluster && doCtrl) // all nodes in 1 cluster - return; - // Just the Nodes first, in a cluster no edges - sb.append(doCtrl ? "\tsubgraph cluster_Controls {\n" : "\tsubgraph cluster_Nodes {\n"); // Magic "cluster_" in the subgraph name - for (Node n : all) { - if (n instanceof ProjNode || n instanceof CProjNode || n instanceof MemMergeNode || n==Parser.XCTRL) - continue; // Do not emit, rolled into MultiNode or Scope cluster already - if( _separateControlCluster && doCtrl && !(n instanceof CFGNode) ) continue; - if( _separateControlCluster && !doCtrl && (n instanceof CFGNode) ) continue; - sb.append("\t\t").append(n.uniqueName()).append(" [ "); - String lab = n.glabel(); - if (n instanceof MultiNode) { - // Make a box with the MultiNode on top, and all the projections on the bottom - sb.append("shape=plaintext label=<\n"); - sb.append("\t\t\t\n"); - sb.append("\t\t\t\n"); - sb.append("\t\t\t"); - boolean doProjTable = false; - n._outputs.sort((x,y) -> x instanceof ProjNode xp && y instanceof ProjNode yp ? (xp._idx - yp._idx) : (x._nid - y._nid)); - for (Node use : n._outputs) { - if (use instanceof ProjNode proj) { - if (!doProjTable) { - doProjTable = true; - sb.append(""); - } - private static SB node(SB sb, String text, Node n) { - String color = color(n); - sb.p("style=filled fillcolor=").p(color).p(" "); - String shape = switch( n ) { - case FunNode fun -> "shape=box peripheries=2"; - case CFGNode cfg -> "shape=box"; - default -> ""; - }; - sb.p(shape).p(" "); - return colorcell(sb.p("label=<"),text,n,dark(color)).p(">"); - } - - private static String color(Node n) { - return switch( n ) { - case CFGNode cfg -> "yellow"; - case NewNode nnn -> "lightgreen"; - case StructNode s -> "lightgreen"; - default -> typeColor(n._type); - }; - } - private static String typeColor(Type t) { - return t==null ? "white" : switch( t ) { - case TypeMem mem -> "blue"; - case TypeMemPtr tmp -> "green"; - case TypeFunPtr tfp -> "orange"; - case TypeRPC rpc -> "antiquewhite"; - case TypeInteger ti -> "lightblue"; - case TypeFloat tf -> "aqua"; - case Type tn -> tn==Type.NIL ? "lightgray" : "red"; - }; - } - private static final String[] DARKS = new String[]{"blue","green","black"}; - private static boolean dark(String color) { return Utils.find(DARKS,color) != -1; } - - - - // Called with an open div - private static SB colorcell(SB sb, String text, Node n, boolean dark) { - if( dark ) sb.p(""); - sb.p(text); - if( n._type!=null ) { - sb.p("
"); - n._type.gprint(sb); - sb.p(""); - } - if( dark ) sb.p("
"); - return sb; - } - - - private static String makeScopeName(ScopeNode sn, int level) { return sn.uniqueName() + "_" + level; } - private static String makePortName(String scopeName, String varName) { return scopeName + "_" + varName; } - - // Walk the node edges - private static void nodeEdges(SB sb, ScopeNode scope, Collection all) { - // All them edge labels - String scopeName = scope!=null ? makeScopeName(scope, 0) : null; - sb.i().p("edge [ fontname=Helvetica, fontsize=8 ];\n"); - for( Node n : all ) { - // Do not display the Constant->Start edge; - if( n instanceof ConstantNode || - n instanceof XCtrlNode || - // ProjNodes handled by Multi; - n instanceof ProjNode || - n instanceof CProjNode || - // ScopeNodes are done separately - n instanceof ScopeNode || - n instanceof MemMergeNode - ) - continue; - if( n.isDead() ) - throw Utils.TODO(); - // Normal edges - for( int i=0; i "); - if( n instanceof PhiNode && def instanceof RegionNode ) { - // Draw a dotted use->def edge from Phi to Region. - sb.p(def.uniqueName()); - sb.p(" [style=dotted taillabel=").p(i).p("];\n"); - } else { - // Most edges land here use->def - if( def instanceof CProjNode proj ) { - String mname = proj.ctrl().uniqueName(); - sb.p(mname).p(":p").p(proj._idx); - } else if( def instanceof ProjNode proj ) { - String mname = proj.in(0).uniqueName(); - sb.p(mname).p(":p").p(proj._idx); - } else sb.p(def.uniqueName()); - // Number edges, so we can see how they track - sb.p("[taillabel=").p(i); - // Color the edge - nodeEdgeColor(sb,def._type); - // Backedges do not add a ranking constraint - if( i==2 && (n instanceof PhiNode || n instanceof LoopNode) ) - sb.p(" constraint=false"); - sb.p("];\n"); - } - } - - // Bonus edge if hooked by parser - if( (n.iskeep() || n.isUnused()) && scopeName != null ) { - sb.i().p(scopeName).p(" -> ").p(n.uniqueName()).p(" [ style=dashed color=grey];\n"); - } - // Force functions above return - if( n instanceof ReturnNode ret && ret.inProgress() && !ret.fun().isDead() ) - sb.i().p(ret.ctrl().uniqueName()).p(" -> ").p(ret.fun().uniqueName()).p("[style=invis]\n"); - } - } - - private static void nodeEdgeColor(SB sb, Type t) { - sb.p(" color=").p(typeColor(t)); - } - - // Walk the scope edges - private static void scopeEdges( SB sb, ScopeNode scope ) { - sb.i().p("edge [style=dashed color=cornflowerblue];\n"); - int level=0; - for( var v : scope._vars ) { - if( v._idx==1 ) continue; // Memory done separately - Node def = scope.in(v._idx); - while( def instanceof ScopeNode lazy ) - def = lazy.in(v._idx); - if( def==null ) continue; - while( level < scope.depth() && v._idx >= scope._kinds.at(level)._lexSize ) - level++; - String scopeName = makeScopeName(scope, level); - sb.i() - .p(scopeName).p(":") - .p('"').p(makePortName(scopeName, v._name)).p('"') // wrap port name with quotes because $ctrl is not valid unquoted - .p(" -> "); - defPort(sb,def).p(";\n"); - } - - // Memory - if( scope.nIns()>1 ) { - MemMergeNode n = scope.mem(); - for( int i=2; i "); - defPort(sb,def).p(";\n"); - } - } - - } - - // (C)Projection index or -1 - private static int idx(Node n) { - return n instanceof ProjNode p ? p._idx : (n instanceof CProjNode cp ? cp._idx : -1); - } - - private static SB defPort(SB sb, Node def) { - int idx = idx(def); - return idx != -1 - ? sb.p(def.in(0).uniqueName()).p(":p").p(idx) - : sb.p(def.uniqueName()); - } -} diff --git a/src/main/java/com/seaofnodes/simple/print/SimpleWebSocket.java b/src/main/java/com/seaofnodes/simple/print/SimpleWebSocket.java deleted file mode 100644 index 4077533d..00000000 --- a/src/main/java/com/seaofnodes/simple/print/SimpleWebSocket.java +++ /dev/null @@ -1,149 +0,0 @@ -package com.seaofnodes.simple.print; - -import com.seaofnodes.simple.util.Utils; -import java.io.*; -import java.net.ServerSocket; -import java.net.Socket; -import java.net.URI; -import java.nio.charset.StandardCharsets; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.util.Base64; - -// Why-Oh-Why is this needed? - -// Pass in a URI and a port. -// Opens the client; client has to open a websocket at the given port. -// Connects, handshakes. -// Thereafter you can call get/put to swap frames with client. - -class SimpleWebSocket extends ServerSocket { - final InputStream _in; - final OutputStream _out; - - SimpleWebSocket(URI uri, int port) throws IOException, NoSuchAlgorithmException { - super(port); - // Launch client - java.awt.Desktop.getDesktop().browse(uri); - // Look for client; get in/out streams - Socket sock = accept(); - _in = sock. getInputStream(); - _out = sock.getOutputStream(); - - - // Establish web socket with handshake; easier with a buffered reader - BufferedReader br = new BufferedReader(new InputStreamReader(_in)); - String key = null; - while( br.ready() ) { - String line = br.readLine(); - if( line.startsWith("Sec-WebSocket-Key: ") ) - key = line.substring(19); - } - assert key!=null; - // Magic handshake - byte[] response = ("HTTP/1.1 101 Switching Protocols\r\n" - + "Connection: Upgrade\r\n" - + "Upgrade: websocket\r\n" - + "Sec-WebSocket-Accept: " - + Base64.getEncoder().encodeToString(MessageDigest.getInstance("SHA-1").digest((key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11").getBytes( StandardCharsets.UTF_8 ))) - + "\r\n\r\n").getBytes( StandardCharsets.UTF_8 ); - _out.write(response,0,response.length); - } - - final byte[] _ins = new byte[1024]; - final byte[] _str = new byte[1024]; - final byte[] _key = new byte[4]; - int _x, _len; - private int rawget() { assert _x<_len; return _ins[_x++]&0xFF; } - - // TODO: Flip to Direct Byte Buffer, this is just a poor-mans version - private void flipRead() throws IOException { - // Move _x to _len to offset 0 - System.arraycopy(_ins,_x,_ins,0,(_len-_x)); - _len = _len-_x; - _x=0; - // Read more - int len = _in.read(_ins,_len,_ins.length-_len); - if( len == -1 ) - throw Utils.TODO(); // EOF - _len += len; - } - // Block until you read at least len - private void atLeast(int len) throws IOException { - if( _x+len <= _len ) return; - flipRead(); - } - - - // Classic read from browser client. Encoded, so decoded here - public String get() throws IOException { - atLeast(1); // Need 1 byte for opcode - int op = rawget(); - if( (op&0x80)!=0x80 ) // FIN bit not set? - throw Utils.TODO( "Multi-part from client: " + op ); // Multipart message - op &= 0x7F; // Strip FIN bit - switch( op&15) { - case 1: { - atLeast(5); // Min message length - // Message length - int len = rawget()-128; - if( len > 125 ) { - if( len > 126 ) - throw Utils.TODO(); // Multi-byte length - len = (rawget()<<8) | rawget(); - } - atLeast(len+4); // Rest of message - // Encoding key - _key[0] = (byte)rawget(); - _key[1] = (byte)rawget(); - _key[2] = (byte)rawget(); - _key[3] = (byte)rawget(); - - // Decode bytes - for( int i=0; i 65535 ) - throw Utils.TODO("MOAR LENGTH"); - _out.write(129); // FIN, opcode 1 - whole text message - _out.write(126); // Length is 16b - _out.write(len>>>8); - _out.write(len&255); - for( int i=0; i} for C or {@code import java.lang.*} for Java. - */ -public abstract class sys { - - public static final String SYS; - - static { - try { - SYS = Files.readString(Path.of("src/main/smp/sys.smp")); - } - catch( Exception e ) { - throw new RuntimeException(e); - } - } - -} diff --git a/src/main/java/com/seaofnodes/simple/type/Field.java b/src/main/java/com/seaofnodes/simple/type/Field.java deleted file mode 100644 index b2b2deca..00000000 --- a/src/main/java/com/seaofnodes/simple/type/Field.java +++ /dev/null @@ -1,136 +0,0 @@ -package com.seaofnodes.simple.type; - -import com.seaofnodes.simple.util.Ary; -import com.seaofnodes.simple.util.SB; -import java.util.*; - -/** - * Represents a field in a struct. This is not a Type in the type system. - */ -public class Field extends Type { - - // The pair {fieldName,type} uniquely identifies a field. - - // Field name - public String _fname; - // Type of the field - public Type _t; - // Unique memory alias, not sensibly part of a "type" but very convenient here. - public int _alias; - // Field must be written to exactly once, no more, no less - public boolean _final; - // Field is final in the declaration; value is the same for all instances - // and will be moved to the class object - public boolean _one; - - private static final Ary FREE = new Ary<>(Field.class); - private Field(String fname, Type type, int alias, boolean xfinal, boolean one ) { super(TFLD); init(fname,type,alias,xfinal,one); } - private Field init(String fname, Type type, int alias, boolean xfinal, boolean one ) { - _fname = fname; - _t = type; - _alias = alias; - _final = xfinal; - _one = one; - return this; - } - - // Return a filled-in Field; either from free list or alloc new. - private static Field malloc(String fname, Type type, int alias, boolean xfinal, boolean one ) { - return FREE.isEmpty() ? new Field(fname,type,alias,xfinal,one) : FREE.pop().init(fname,type,alias,xfinal,one); - } - // Malloc-from - public Field malloc( ) { return malloc(_fname,null,_alias,_final,_one); } - @Override Field free(Type t) { - Field f = (Field)t; - assert !f.isFree() && !f._terned; - f._t = null; - f._dual = null; - f._hash = 0; - FREE.push(f); - return this; - } - @Override boolean isFree() { return _t ==null; } - - // Make and intern with listed fields - public static Field make( String fname, Type type, int alias, boolean xfinal, boolean one ) { - Field f = malloc(fname,type,alias,xfinal,one); - Field f2 = f.intern(); - if( f2==f ) return f; - return VISIT.isEmpty() ? f2.free(f) : f2.delayFree(f); - } - public Field makeFrom( Type type ) { - return type == _t ? this : make(_fname,type,_alias,_final,_one); - } - public Field makeFrom( boolean xfinal ) { - return xfinal == _final ? this : make(_fname,_t,_alias,xfinal,_one); - } - - // Cycle-making-breaking - void setType(Type t) { assert _t ==null; _t = t; } - - public static final Field BOT = make(" ",Type.BOTTOM,-999,true,false); - public static final Field TEST = make("test",Type.NIL,-2,false,false); - public static final Field TEST2 = make("test",Type.NIL,-2,true, false); - public static final Field FLT = make("flt ",TypeFloat.F32,-2,false,false); - public static void gather(ArrayList ts) { ts.add(TEST); ts.add(TEST2); ts.add(FLT); } - - @Override Field xmeet( Type that ) { - Field fld = (Field)that; // Invariant - if( this==BOT || fld==BOT ) return BOT; - if( this==BOT.dual() ) return fld ; - if( fld ==BOT.dual() ) return this; - if( _fname!=fld._fname ) { assert !_fname.equals(fld._fname); return BOT; } - assert _alias==fld._alias; - assert _one ==fld._one ; - return make(_fname, _t.meet(fld._t ),_alias,_final | fld._final, _one); - } - - @Override - Field xdual() { return malloc(_fname, _t.dual(),_alias,!_final,_one); } - - @Override Field rdual() { - if( _dual!=null ) return dual(); - assert !_terned; - Field d = malloc(_fname,null,_alias,!_final,_one); - (_dual = d)._dual = this; // Cross link duals - d._t = _t._terned ? _t.dual() : _t.rdual(); - return d; - } - - @Override boolean _isConstant() { return _t._isConstant(); } - @Override boolean _isFinal() { return _final && _t._isFinal(); } - @Override Field _makeRO() { return _final ? this : make(_fname, _t._makeRO(),_alias,true,_one); } - boolean isGLB2() { return _final && _t._isGLB(true); } - Field glb2() { - Type glb = _t._glb(true); - return (glb== _t && _final ) ? this : make(_fname,glb,_alias,true,_one); - } - @Override Field _close() { return makeFrom( _t._close()); } - - // Override in subclasses - int hash() { return _fname.hashCode() ^ _t.hashCode() ^ _alias ^ (_final ? 1024 : 0) ^ (_one ? 2048 : 0); } - - private boolean static_eq( Field f ) { - return _fname.equals(f._fname) && _alias==f._alias && _final==f._final && _one==f._one && _t !=null && f._t !=null; - } - @Override boolean eq( Type t ) { - Field f = (Field)t; - return static_eq(f) && _t ==f._t; - } - @Override boolean cycle_eq(Type t) { - Field f = (Field)t; - return static_eq(f) && _t.cycle_eq(f._t ); - } - - @Override int nkids() { return 1; } - @Override Type at( int idx ) { return _t; } - @Override void set( int idx, Type t ) { _t = t; } - - @Override - public SB _print( SB sb, BitSet visit, boolean html ) { - sb.p(_final?"":"!").p(_one?"$":"").p(_fname).p(":").p(_alias).p(" : "); - return _t ==null ? sb.p("---") : _t.print(sb,visit,html); - } - - @Override public String str() { return (_final?"":"!")+(_one?"$":"")+_fname; } -} diff --git a/src/main/java/com/seaofnodes/simple/type/Type.java b/src/main/java/com/seaofnodes/simple/type/Type.java deleted file mode 100644 index b7a8408e..00000000 --- a/src/main/java/com/seaofnodes/simple/type/Type.java +++ /dev/null @@ -1,514 +0,0 @@ -package com.seaofnodes.simple.type; - -import com.seaofnodes.simple.util.*; -import java.util.*; - -/** - * These types are part of a Monotone Analysis Framework, - * @see see for example this set of slides. - *

- * The types form a lattice; @see a symmetric complete bounded (ranked) lattice. - *

- * This wild lattice theory will be needed later to allow us to easily beef up - * the analysis and optimization of the Simple compiler... but we don't need it - * now, just know that it is coming along in a later Chapter. - *

g - * One of the fun things here is that while the theory is deep and subtle, the - * actual implementation is darn near trivial and is generally really obvious - * what we're doing with it. Right now, it's just simple integer math to do - * simple constant folding e.g. 1+2 == 3 stuff. - */ - -public class Type /*implements Cloneable*/ { - // Interning tables - - // Main intern table; a collection of all unique types. - static final HashMap INTERN = new HashMap<>(); - // Starting intern table; used to rapidly reset the main table between tests - private static final HashMap INTERN0= new HashMap<>(); - // Lifetime management during cyclic installs is complex; this is used to - // track created-but-already-interned objects - private static final Ary FREES = new Ary<>(Type.class); - // "visit" bit management, based solely on _uid - static final BitSet BITS = new BitSet(); - - // "visit" bit management. Key varies by kind of visit: - - // - "close" an open cyclic type. Used by the Parser, it will hit on - // same-named structs, even if the structs have different states of - // completion/fields. - // - "uid/name" - Either the UID or name works (unlike "close" which must - // use the name). Used to make cyclic copies with changes, like make - // read-only where there should be only 1 copy of a name in a cycle. - // - "pair of uids" - For recursive MEET, where we need to key off two types - static final HashMap VISIT = new HashMap<>(); - - // Every Type has a UID to allow visit bits on cyclic visits - private static int UID=1; - public char _uid; // Unique ID for every type - - // ---------------------------------------------------------- - // Simple types are implemented fully here. "Simple" means: the code and - // type hierarchy are simple, not that the Type is conceptually simple. - static final byte TBOT = 0; // Bottom (ALL) - static final byte TTOP = 1; // Top (ANY) - static final byte TCTRL = 2; // Ctrl flow bottom - static final byte TXCTRL = 3; // Ctrl flow top (mini-lattice: any-xctrl-ctrl-all) - static final byte TNIL = 4; // low null of all flavors - static final byte TXNIL = 5; // high or choice null - static final byte TSIMPLE = 6; // End of the Simple Types - - static final byte TPTR = 7; // All nil-able scalar values - static final byte TINT = 8; // All Integers; see TypeInteger - static final byte TFLT = 9; // All Floats ; see TypeFloat - static final byte TCONARY =10; // Constant array - static final byte TRPC =11; // Return Program Control (Return PC or RPC) - static final byte TTUPLE =12; // Tuples; finite collections of unrelated Types, kept in parallel - - static final byte TCYCLIC =13; // Has internal pointers, needs recursive treatment - static final byte TMEMPTR =13; // Memory pointer to a struct type - static final byte TFUNPTR =14; // Function pointer; unique signature and code address (just a bit) - static final byte TMEM =15; // All memory (alias 0) or A slice of memory - with specific alias - static final byte TSTRUCT =16; // Structs; tuples with named fields - static final byte TFLD =17; // Named fields in structs - - // Basic RTTI, useful for a lot of fast tests. - public final byte _type; - public boolean _terned; - - public boolean is_simple() { return _type < TSIMPLE; } - private static final String[] STRS = new String[]{"Bot","Top","Ctrl","~Ctrl","null","~nil"}; - static final int[] CNTS = new int[TFLD+1]; - protected Type(byte type) { - _type = type; // RTTI - _uid = (char)UID++; // A unique ID for every type - assert _uid!=0; // Overflow - CNTS[type]++; - } - - public static final Type BOTTOM = new Type( TBOT ).intern(); // ALL - public static final Type TOP = BOTTOM.dual(); - public static final Type CONTROL = new Type( TCTRL ).intern(); // Ctrl - public static final Type XCONTROL = CONTROL.dual(); - public static final Type NIL = new Type( TNIL ).intern(); // low null of all flavors - public static final Type XNIL = NIL.dual(); - public static Type[] gather() { - ArrayList ts = new ArrayList<>(); - ts.add(BOTTOM); - ts.add(CONTROL); - ts.add(NIL); - TypeNil.gather(ts); - TypePtr.gather(ts); - TypeInteger.gather(ts); - TypeFloat.gather(ts); - TypeMemPtr.gather(ts); - TypeFunPtr.gather(ts); - TypeMem.gather(ts); - Field.gather(ts); - TypeStruct.gather(ts); - TypeTuple.gather(ts); - TypeRPC.gather(ts); - TypeConAry.gather(ts); - - int sz = ts.size(); - for( int i = 0; i < sz; i++ ) - ts.add(ts.get(i).dual()); - return ts.toArray(new Type[ts.size()]); - } - - // Is high or on the lattice centerline. - public boolean isHigh () { return _type==TTOP || _type==TXCTRL || _type==TXNIL; } - public boolean isHighOrConst() { return isHigh() || isConstant(); } - - // ---------------------------------------------------------- - - // Notes on Type interning: At the moment it is not easy to reset the - // interned types because we hold static references to several types and - // these are scattered around. This means the INTERN cache will retain all - // types from every run of the Parser. For this to work correctly types - // must be rigorous about defining when they are the same. Also types need - // to be immutable once defined. The rationale for interning is - // *correctness* with cyclic type definitions. Simple structural recursive - // checks go exponential with merely sharing, but with cycles they will - // stack overflow and crash. Interning means we do not need to have sharing - // checks with every type compare, only during interning. - - // Factory method which interns "this" - @SuppressWarnings("unchecked") - T intern() { - //assert check(); - assert !_terned; // Do not ask for already-interned - T t2 = (T)INTERN.get(this); - if( t2!=null ) { - assert t2._dual != null; // Prior is complete with dual - assert this != t2; // Do not hashcons twice, should not get self back - return t2; // Return prior - } - // Mid recursive type construction - if( !VISIT.isEmpty() && _type >= TCYCLIC ) { - // No intern attempt at all - return (T)this; - } - // Not in type table - _dual = null; // No dual yet - INTERN.put(this,this); // Put in table without dual - _terned = true; - //Util.hash_quality_check_per(INTERN,"INTERN"); - T d = xdual(); // Compute dual without requiring table lookup, and not setting name - _dual = d; - if( this==d ) return d; // Self-symmetric? Dual is self - assert !equals(d); // Self-symmetric is handled by caller - assert d._dual==null; // Else dual-dual not computed yet - assert INTERN.get(d)==null; - d._dual = (T)this; - INTERN.put(d,d); // Install dual also - d._terned = true; - //assert check(); - return (T)this; - } - - // Recursive visit elsewhere, interns with dual already - T _intern() { - //assert check(); - assert !_terned; - Type t = INTERN.get(this); - if( t!=null ) return (T)t.delayFree(this).delayFree(_dual); - assert !INTERN.containsKey( _dual ); - INTERN.put(this,this); - INTERN.put(_dual,_dual); - _terned = _dual._terned = true; - //assert check(); - return (T)this; - } - - private static boolean check() { - for( Type t : INTERN.keySet() ) { - Type t2 = INTERN.get(t); - assert t==t2; - } - return true; - } - - public static Type find(int uid) { - for( Type t : INTERN.keySet() ) - if( t._uid==uid ) return t; - return null; - } - - - int _hash; // Hash cache; not-zero when set. - @Override - public final int hashCode() { - if( _hash!=0 ) return _hash; - _hash = hash(); - if( _hash==0 ) _hash = 0xDEADBEEF; // Bad hash from subclass; use some junk thing - return _hash; - } - // Override in subclasses - int hash() { return _type; } - - @Override - public final boolean equals( Object o ) { - if( o==this ) return true; - if( !(o instanceof Type t)) return false; - if( _type != t._type ) return false; - return eq(t); - } - // Overridden in subclasses; subclass can assume "this!=t" and java classes are same - boolean eq(Type t) { return this==t; } - boolean cycle_eq(Type t) { assert _type < TCYCLIC; return eq(t); } - // A pair of uids - int pid( Type that ) { - return _uid < that._uid ? (_uid<<16 | that._uid) : (that._uid<<16 | _uid); - } - - // At/Set child at 'idx' to t - Type at ( int idx ) { throw Utils.TODO(); } - void set( int idx, Type t ) { throw Utils.TODO(); } - int nkids() { assert _type < TTUPLE; return 0; } // Number of kids - - - // Clear and re-insert the basic Type INTERN table - public static void reset() { - VISIT.clear(); - FREES.clear(); - if( INTERN0.isEmpty() ) { - // First time run all and collect basic types - Type t = TypeRPC .BOT; // Force class load, intern - Type u = TypeTuple.BOT; // Force class load, intern - Type w = TypePtr .PTR; // Force class load, intern - INTERN0.putAll(INTERN); - } else { - // Later times, reset back to first time - VISIT.put(0,CONTROL); // Defeat assert - for( Iterator it = INTERN.keySet().iterator(); it.hasNext(); ) { - Type t = it.next(); - if( !INTERN0.containsKey(t) ) - { t._terned=false; t.free(t); } - it.remove(); - } - VISIT.clear(); - INTERN.putAll(INTERN0); - } - } - - // ---------------------------------------------------------- - public final Type meet(Type t) { - // Shortcut for the self case - if( t == this ) return this; - // Same-type is always safe in the subclasses - if( _type==t._type ) return xmeet(t); - // TypeNil vs TypeNil meet - if( this instanceof TypeNil ptr0 && t instanceof TypeNil ptr1 ) - return ptr0.nmeet(ptr1); - // Reverse; xmeet 2nd arg is never "is_simple" and never equal to "this". - if( is_simple() ) return this.xmeet(t ); - if( t.is_simple() ) return t .xmeet(this); - return Type.BOTTOM; // Mixing 2 unrelated types - } - - // Compute meet right now. Overridden in subclasses. - // Handle cases where 'this.is_simple()' and unequal to 't'. - // Subclassed xmeet calls can assert that '!t.is_simple()'. - Type xmeet(Type t) { - assert is_simple(); // Should be overridden in subclass - // ANY meet anything is thing; thing meet ALL is ALL - if( _type==TBOT || t._type==TTOP ) return this; - if( _type==TTOP || t._type==TBOT ) return t; - - // RHS TypeNil vs NIL/XNIL - if( _type== TNIL ) return t instanceof TypeNil ptr ? ptr.meet0() : (t._type==TXNIL ? TypePtr.PTR : BOTTOM); - if( _type== TXNIL ) return t instanceof TypeNil ptr ? ptr.meetX() : (t._type== TNIL ? TypePtr.PTR : BOTTOM); - - // 'this' is only {TCTRL,TXCTRL} - // Other non-simple RHS things bottom out - if( !t.is_simple() ) return BOTTOM; - // If RHS is NIL/XNIL - if( t._type==TNIL || t._type==TXNIL ) return BOTTOM; - // Both are {TCTRL,TXCTRL} and unequal to each other - return _type==TCTRL || t._type==TCTRL ? CONTROL : XCONTROL; - } - - - // The dual is pre-computed on creation and available "for free" thereafter - Type _dual; - public final T dual() { return (T)_dual; } - - T xdual() { - return (T)new Type(switch( _type ) { - case TBOT -> TTOP; - case TTOP -> TBOT; - case TCTRL -> TXCTRL; - case TXCTRL-> TCTRL; - case TNIL -> TXNIL; - case TXNIL -> TNIL; - default -> throw Utils.TODO(); // Should not reach here - }); - } - - - // ---------------------------------------------------------- - // Our lattice is defined with a MEET and a DUAL. - // JOIN is dual of meet of both duals. - public final Type join(Type t) { - if( this==t ) return this; - return dual().meet(t.dual()).dual(); - } - - // True if this "isa" t; e.g. 17 isa TypeInteger.BOT - public boolean isa( Type t ) { return meet(t)==t; } - - // True if this "isa" t up to named structures - public boolean shallowISA( Type t ) { return isa(t); } - - public Type nonZero() { return TypePtr.NPTR; } - - // Make a zero version of this type, 0 for integers and null for pointers. - public Type makeZero() { return Type.NIL; } - - // Is forward-reference - public boolean isFRef() { return false; } - - // ---------------------------------------------------------- - - // Cyclic types! Flag the start of a cyclic type by putting a sentinel in - // VISIT (and eventually clearing VISIT when done). Then do a normal - // recursive descent visit of all types; this will accumilate types without - // interning them. When done we have to visit the possibly cyclic type and - // intern the whole cycle, possibly hitting the entire cycle on a prior - // interned cycle. Any sub-part might also be interned, including whole - // disjoint cycles. - - // E.g. a new type: "A<->B -> C1<->D1" gets created; the "C1<->D1" cycle - // already exists as "C0<->D0", but the "A<->B" cycle is new. The "A<->B" - // cycle will get interned - except the pointer to C1 needs to get replaced - // with C0. The "C1<->D1" cycle gets freed and "B" will point to the - // "C0<->D0" cycle. - - Type recurOpen() { assert VISIT.isEmpty(); VISIT.put(0L,BOTTOM); return this; } - Type recurClose() { - VISIT.remove(0L); // Just ignore the sentinel - Ary ts = new Ary<>(Type.class); - ts.addAll(VISIT.values()); - if( ts.find(this)== -1 ) ts.add(this); - - // Pass #1: replace already interned fields - assert BITS.isEmpty(); - for( int i=0; i T delayFree(Type free) { - assert !free._terned; - FREES.push(free); - return (T)this; - } - - final Type install() { - if( this instanceof TypeStruct ) { - Type x = _intern(); // Stop the recursion - assert x==this; - } - int nkids = nkids(); - for( int i=0; i false; - case TNIL -> false; - case TCTRL -> true; - case TXCTRL -> false; - case TXNIL -> false; - case TTOP -> false; - default -> throw Utils.TODO(); - }; - } - public final Type glb(boolean mem) { - if( isGLB(mem) ) return this; - Type glb = recurOpen()._glb(mem).recurClose(); - assert this.isa(glb); - return glb; - } - Type _glb(boolean mem) { assert is_simple(); return Type.BOTTOM; } - - Type _close() { return this; } - - // ---------------------------------------------------------- - - // Size in bits to hold an instance of this type. - // Sizes are expected to be between 1 and 64 bits. - // Size 0 means this either takes no space (such as a known-zero field) - // or isn't a scalar to be stored in memory. - public int log_size () { return 3; } // log-size of a type; log_size for a struct is usually undefined - public int size() { return 1< extends Type { - boolean _any; - // One of byte,short,int,long,float,double array - public final A _ary; - - TypeConAry( boolean any, byte type, A ary ) { super(type); _any = any; _ary = ary; } - public static final TypeConAry BOT = new TypeConAry(false, TINT, null).intern(); - public static void gather(ArrayList ts) { - ts.add(BOT); - TypeConAryB.gather(ts); - TypeConAryI.gather(ts); - } - - @Override public String str() { return (_any?"~":"") + "[]"; } - @Override TypeConAry xdual() { - if( _ary==null ) - return new TypeConAry(!_any,_type,null); - return this; - } - - @Override Type xmeet(Type t) { - if( t instanceof TypeInteger ti ) return imeet(ti); - TypeConAry ary = (TypeConAry)t; // Invariant - if( this==BOT ) return BOT; - if( ary ==BOT ) return BOT; - if( this==BOT.dual() ) return ary ; - if( ary ==BOT.dual() ) return this; - assert _ary!=ary._ary; // Already interned and this!=t - return elem().meet(ary.elem()); - } - Type imeet( TypeInteger ti ) { - if( this==BOT.dual() ) return ti; - if( this==BOT ) return BOT; - Type elem = elem(); - if( !(elem instanceof TypeInteger) ) return BOTTOM; - if( ti.isHigh() && elem.isa(ti.dual()) ) - return this; - return elem.meet(ti); - } - - - @Override public boolean isHigh() { return this==TOP; } - @Override boolean _isConstant() { return true; } - - // Meet-over-elements type - public Type elem() { - if( _ary==null ) - return _any ? Type.TOP : BOTTOM; - int len = len(); - long min = Long.MAX_VALUE; - long max = Long.MIN_VALUE; - for( int i=0; i { - private TypeConAryB(byte[] bs) { super(false,TINT,bs); } - public static TypeConAryB make( byte[] bs ) { return new TypeConAryB(bs).intern(); } - public static TypeConAryB make( String s ) { return make(s.getBytes()); } - static final TypeConAryB ABC = make("abc"); - static final TypeConAryB ABCD = make("abcd"); - public static void gather(ArrayList ts) { ts.add(ABC); ts.add(ABCD); } - @Override public long at8(int idx) { return _ary[idx]; } - @Override public int len() { return _ary.length; } - @Override public int log_size() { return 0; } - @Override public String str() { return "[\""+new String(_ary)+"\"]"; } - @Override boolean eq(Type t) { return Arrays.equals(_ary,((TypeConAryB)t)._ary); } - @Override int hash() { return Arrays.hashCode(_ary); } - @Override public void write( ByteArrayOutputStream baos ) { baos.write(_ary,0,_ary.length); } -} diff --git a/src/main/java/com/seaofnodes/simple/type/TypeConAryI.java b/src/main/java/com/seaofnodes/simple/type/TypeConAryI.java deleted file mode 100644 index 924e3d73..00000000 --- a/src/main/java/com/seaofnodes/simple/type/TypeConAryI.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.seaofnodes.simple.type; - -import java.util.ArrayList; -import java.util.Arrays; - -public class TypeConAryI extends TypeConAry { - private TypeConAryI(int[] bs) { super(false,TINT,bs); } - public static TypeConAryI make( int[] bs ) { return new TypeConAryI(bs).intern(); } - private static final TypeConAryI I123 = make(new int[]{1,2,3}); - public static void gather(ArrayList ts) { ts.add(I123); } - @Override public long at8(int idx) { return _ary[idx]; } - @Override public int len() { return _ary.length; } - @Override public int log_size() { return 2; } - @Override public String str() { return "[i32]"; } - @Override boolean eq(Type t) { return Arrays.equals(_ary,((TypeConAryI)t)._ary); } - @Override int hash() { return Arrays.hashCode(_ary); } -} diff --git a/src/main/java/com/seaofnodes/simple/type/TypeFloat.java b/src/main/java/com/seaofnodes/simple/type/TypeFloat.java deleted file mode 100644 index f3de581c..00000000 --- a/src/main/java/com/seaofnodes/simple/type/TypeFloat.java +++ /dev/null @@ -1,110 +0,0 @@ -package com.seaofnodes.simple.type; - -import com.seaofnodes.simple.util.SB; -import com.seaofnodes.simple.util.Utils; -import com.seaofnodes.simple.util.Ary; -import java.util.ArrayList; - -/** - * Float Type - */ -public class TypeFloat extends Type { - - // - high -64, high -32, con 0, low +32, low +64 - public byte _sz; - - /** - * The constant value or 0 - */ - public double _con; - - private static final Ary FREE = new Ary<>(TypeFloat.class); - private TypeFloat(byte sz, double con) { super(TFLT); init(sz,con); } - private TypeFloat init( byte sz, double con ) { _sz = sz; _con = con; return this; } - public static TypeFloat malloc(byte sz, double con) { return FREE.isEmpty() ? new TypeFloat(sz,con) : FREE.pop().init(sz,con); } - private static TypeFloat make(int sz, double con) { - TypeFloat f = malloc((byte)sz,con); - TypeFloat t2 = f.intern(); - return t2==f ? f : t2.free(f); - } - @Override TypeFloat free(Type t) { - TypeFloat f = (TypeFloat)t; - f._hash = 0; - f._dual=null; - FREE.push(f); - return this; - } - - public static TypeFloat constant(double con) { return make(0, con); } - - public final static TypeFloat ONE = constant(1.0); - public final static TypeFloat FZERO = constant(0.0); - public final static TypeFloat F32 = make(32, 0); - public final static TypeFloat F64 = make(64, 0); - - public static void gather(ArrayList ts) { ts.add(F64); ts.add(F32); ts.add(constant(3.141592653589793)); } - - @Override public String str() { - return switch( _sz ) { - case -64 -> "~flt"; - case -32 -> "~f32"; - case 0 -> ""+_con+((float)_con==_con ? "f" : ""); - case 32 -> "f32"; - case 64 -> "flt"; - default -> throw Utils.TODO(); - }; - } - private boolean isF32() { return ((float)_con)==_con; } - - @Override public boolean isHigh () { return _sz< 0; } - @Override public int log_size() { return _sz==32 || _sz==-32 ? 2 : 3; } - - public double value() { return _con; } - - @Override - public TypeFloat xmeet(Type other) { - TypeFloat f = (TypeFloat)other; - // Invariant from caller: 'this' != 'other' and same class (TypeFloat). - TypeFloat i = (TypeFloat)other; // Contract - // Larger size in i1, smaller in i0 - TypeFloat i0 = _sz < i._sz ? this : i; - TypeFloat i1 = _sz < i._sz ? i : this; - - if( i1._sz== 64 ) return F64; - if( i0._sz==-64 ) return i1; - if( i1._sz== 32 ) - return i0._sz==0 && !i0.isF32() ? F64 : F32; - if( i1._sz!= 0 ) return i1; - // i1 is a constant - if( i0._sz==-32 ) - return i1.isF32() ? i1 : F64; - // Since both are constants, and are never equals (contract) unequals - // constants fall to bottom - return i0.isF32() && i1.isF32() ? F32 : F64; - } - - @Override - TypeFloat xdual() { - return _isConstant() ? this : new TypeFloat((byte)-_sz,0); // Constants are a self-dual - } - - @Override boolean _isGLB(boolean mem) { return _glb(mem)==this; } - @Override Type _glb(boolean mem) { - if( !mem ) return F64; - if( _isConstant() ) return isF32() ? F32 : F64; - return isHigh() ? dual() : this; - } - - @Override boolean _isConstant() { return _sz==0; } - - @Override public Type makeZero() { return FZERO; } - @Override - int hash() { return (int)(Double.hashCode(_con) ^ _sz ^ (1<<17)); } - @Override - public boolean eq( Type t ) { - TypeFloat i = (TypeFloat)t; // Contract - // Allow NaN to check for equality - return _sz==i._sz && Double.doubleToLongBits(_con)==Double.doubleToLongBits(i._con); - } - -} diff --git a/src/main/java/com/seaofnodes/simple/type/TypeFunPtr.java b/src/main/java/com/seaofnodes/simple/type/TypeFunPtr.java deleted file mode 100644 index b21dd451..00000000 --- a/src/main/java/com/seaofnodes/simple/type/TypeFunPtr.java +++ /dev/null @@ -1,241 +0,0 @@ -package com.seaofnodes.simple.type; - -import com.seaofnodes.simple.util.SB; -import com.seaofnodes.simple.util.Utils; -import com.seaofnodes.simple.util.Ary; -import java.util.*; - -/** - * Represents a Pointer to a function. - *

- * Functions have argument types and a return type, making a function - * signature. Within a signature there can be many instances of functions, and - * each function is labeled with a small integer constant. A TFP can represent - * a single signature and a set of functions; the set might contain only 1 - * function or the zeroth function (the null ptr). - *

- * Functions with different signatures cannot be mixed, and will result in a - * bottom function type, which can only be null-checked. - */ -public class TypeFunPtr extends TypeNil { - // A TypeFunPtr is Signature and a set of functions. - public Type[] _sig; - public Type _ret; - // TODO: - boolean _open; // Extra args are true:BOTTOM, false:TOP - // Cheesy easy implementation for a small set; 1 bit per unique function - // within the same Type[]. Can be upgraded to a BitSet for larger classes - // of functions. Negative means "these 63 concrete bits plus infinite - // unknown more" - public long _fidxs; // 63 unique functions per signature - - private static final Ary FREE = new Ary<>(TypeFunPtr.class); - private TypeFunPtr(byte nil, boolean open, Type[] sig, Type ret, long fidxs) { super(TFUNPTR,nil); init(nil,open,sig,ret,fidxs); } - private TypeFunPtr init(byte nil, boolean open, Type[] sig, Type ret, long fidxs ) { - assert sig != null; - _nil = nil; - _open = open; - _sig = sig; - _ret = ret; - _fidxs= fidxs; - return this; - } - static TypeFunPtr malloc( byte nil, boolean open, Type[] sig, Type ret, long fidxs ) { - return FREE.isEmpty() ? new TypeFunPtr(nil,open,sig,ret,fidxs) : FREE.pop().init(nil,open,sig,ret,fidxs); - } - @Override TypeFunPtr free(Type t) { - TypeFunPtr fun = (TypeFunPtr)t; - assert !fun.isFree() && !fun._terned; - fun._sig = null; - fun._ret = null; - fun._dual = null; - fun._hash = 0; - FREE.push(fun); - return this; - } - @Override boolean isFree() { return _sig==null; } - - // All fields directly listed - public static TypeFunPtr make( byte nil, boolean open, Type[] sig, Type ret, long fidxs ) { - TypeFunPtr fun = malloc(nil,open,sig,ret,fidxs); - TypeFunPtr f2 = fun.intern(); - if( f2==fun ) return fun; - return VISIT.isEmpty() ? f2.free(fun) : f2.delayFree(fun); - } - public static TypeFunPtr make( boolean nil, boolean open, Type[] sig, Type ret ) { return make((byte)(nil ? 3 : 2),open,sig,ret,-1); } - - - @Override TypeFunPtr makeFrom( byte nil ) { return nil ==_nil ? this : make( nil,_open,_sig,_ret, _fidxs); } - public TypeFunPtr makeFrom( Type ret ) { return ret ==_ret ? this : make( _nil,_open,_sig, ret, _fidxs); } - public TypeFunPtr makeFrom( int fidx ) { return make((byte)2, _open, _sig,_ret,1L< ts) { ts.add(TEST); ts.add(TEST0); ts.add(BOT); ts.add(MAIN);ts.add(CALLOC); } - - @Override - Type xmeet(Type t) { - TypeFunPtr that = (TypeFunPtr) t; - // if equal, no matters. - // if short is BOT, chop ; recurPre on short. - // if short is TOP, copy long; recurPre on long. - TypeFunPtr min = _sig.length < that._sig.length ? this : that; - if( _sig.length != that._sig.length && (min._open ^ min==this) ) - return that.xmeet(this); - // Recurse all common fields - Type[] args = new Type[_sig.length]; - for( int i=0; i "),visit,html).p(" #"); - // Print fidxs - return sb.p(printFIDX()).p("}").p(q()); - } - String printFIDX() { - String tilde = isHigh() ? "~" : ""; - long fidxs = isHigh() ? ~_fidxs : _fidxs; - String fidx = fidxs==0 ? "" - : Long.bitCount(fidxs) == 1 ? ""+Long.numberOfTrailingZeros(fidxs) - : fidxs == -1 ? "ALL" - : "b"+Long.toBinaryString(fidxs); // Just some function bits - return tilde+fidx; - } - - // Usage: for( long fidxs=fidxs(); fidxs!=0; fidxs=nextFIDX(fidxs) { int fidxs = Long.numberOfTrailingZeros(fidxs); ... } - public static long nextFIDX(long fidxs) { return fidxs & (fidxs-1); } - -} diff --git a/src/main/java/com/seaofnodes/simple/type/TypeInteger.java b/src/main/java/com/seaofnodes/simple/type/TypeInteger.java deleted file mode 100644 index 99f237a9..00000000 --- a/src/main/java/com/seaofnodes/simple/type/TypeInteger.java +++ /dev/null @@ -1,153 +0,0 @@ -package com.seaofnodes.simple.type; - -import com.seaofnodes.simple.util.Ary; -import com.seaofnodes.simple.util.SB; -import com.seaofnodes.simple.util.Utils; -import java.util.ArrayList; - -/** - * Integer Type - */ -public class TypeInteger extends Type { - /** - * Describes an integer *range* - everything from min to max; both min and - * max are inclusive. If min==max, this is a constant. - *
- * If min <= max, this is a below center (towards bottom). - * If min > max, this is an above center (towards top). - */ - public long _min, _max; - - private static final Ary FREE = new Ary<>(TypeInteger.class); - private TypeInteger(long min, long max) { super(TINT); init(min,max); } - private TypeInteger init(long min, long max) { _min = min; _max = max; return this; } - public static TypeInteger malloc(long lo, long hi) { return FREE.isEmpty() ? new TypeInteger(lo,hi) : FREE.pop().init(lo,hi); } - public static TypeInteger make(long lo, long hi) { - TypeInteger i = malloc(lo,hi); - TypeInteger t2 = i.intern(); - return t2==i ? i : t2.free(i); - } - @Override TypeInteger free(Type t) { - TypeInteger i = (TypeInteger)t; - i._min = i._max = 0; - i._hash = 0; - i._dual = null; - FREE.push(i); - return this; - } - - public static TypeInteger constant(long con) { return make(con, con); } - - public final static TypeInteger ZERO= make(0,0); - public final static TypeInteger FALSE=ZERO; - public final static TypeInteger TRUE= make(1,1); - public final static TypeInteger TWO = make(2,2); // Shows up in some Simple tests as the starting argument - - public final static TypeInteger I1 = make(-1,0); - public final static TypeInteger I8 = make(-128,127); - public final static TypeInteger I16 = make(-32768,32767); - public final static TypeInteger I32 = make(-1L<<31,(1L<<31)-1); - public final static TypeInteger BOT = make(Long.MIN_VALUE,Long.MAX_VALUE); - public final static TypeInteger TOP = (TypeInteger)BOT.dual(); - - public final static TypeInteger U1 = make(0,1); - public final static TypeInteger BOOL= U1; - public final static TypeInteger U8 = make(0,255); - public final static TypeInteger U16 = make(0,65535); - public final static TypeInteger U32 = make(0,(1L<<32)-1); - - public static void gather(ArrayList ts) { ts.add(I32); ts.add(BOT); ts.add(U1); ts.add(I1); ts.add(U8); } - - @Override public String str() { - if( _isConstant() ) return ""+_min; - long lo = _min, hi = _max; - String x = ""; - if( hi < lo ) { - lo = _max; hi = _min; - x = "~"; - } - return x+_str(lo,hi); - } - private static String _str(long lo, long hi) { - if( lo==Long.MIN_VALUE && hi==Long.MAX_VALUE ) return "i64"; - if( lo== 0 && hi== 1 ) return "bool"; - if( lo== -1 && hi== 0 ) return "i1"; - if( lo== -128 && hi== 127 ) return "i8"; - if( lo== -32768 && hi== 32767 ) return "i16"; - if( lo== -1L<<31 && hi==(1L<<31)-1 ) return "i32"; - if( lo== 0 && hi== 255 ) return "u8"; - if( lo== 0 && hi== 65535 ) return "u16"; - if( lo== 0 && hi==(1L<<32)-1 ) return "u32"; - return "["+lo+"-"+hi+"]"; - } - - @Override public boolean isHigh () { return _min > _max; } - - @Override public int log_size() { - if( isHigh() ) return 0; // High types are dead, and should never hit code emission. - if( this==I8 || this==U8 || this==BOOL ) return 0; // 1<<0 == 1 bytes - if( this==I16 || this==U16 ) return 1; // 1<<1 == 2 bytes - if( this==I32 || this==U32 ) return 2; // 1<<2 == 4 bytes - if( this==BOT ) return 3; // 1<<3 == 8 bytes - if( isConstant() ) { // just const here - if (-0xFF <= _min && _min <= 0xFF) return 0; - else if (-0xFFFF <= _min && _min <= 0xFFFF) return 1; - else if (-0xFFFFFFFFL <= _min && _min <= 0xFFFFFFFFL) return 2; - else return 3; - } - throw Utils.TODO(); - } - - public long value() { assert isConstant(); return _min; } - - // AND-mask of forced zeros. e.g. unsigned types will return their mask; - // u8 will return 0xFF. But also a range of 16-18 (0x10-0x12) will return - // 0x13 - no value in the range {16,17,18} will allow bit 0x04 to be set. - public long mask() { - if( isHigh() ) return 0; - if( isConstant() ) return _min; - // Those bit positions which differ min to max - long x = _min ^ _max; - // Highest '1' bit in the differ set. Since the range is from min to - // max, all values below here are possible. - long ff1 = Long.highestOneBit(x); - // Make a all-1's mask from ff1, and set over the same bits (either min - // or max is ok). - long mask = _min | (ff1-1) | ff1; - return mask; - } - - @Override - public Type xmeet(Type other) { - if( other instanceof TypeConAry ary ) return ary.imeet(this); - // Invariant from caller: 'this' != 'other' and same class (TypeInteger) - TypeInteger i = (TypeInteger)other; // Contract - return make(Math.min(_min,i._min), Math.max(_max,i._max)); - } - - @Override TypeInteger xdual() { - return _min==_max ? this : malloc(_max,_min); - } - - @Override public TypeInteger nonZero() { - if( isHigh() ) return this; - if( this==ZERO ) return null; // No sane answer - if( _min==0 ) return make(1,Math.max(_max,1)); // specifically good on BOOL - if( _max==0 ) return make(_min,-1); - return this; - } - @Override public Type makeZero() { return ZERO; } - @Override boolean _isConstant() { return _min == _max; } - @Override boolean _isGLB(boolean mem) { return _glb(mem)==this; } - @Override Type _glb(boolean mem) { - if( !mem ) return BOT; - // GLB is not well-defined in memory; depends on the size of the memory field - if( _isConstant() ) return BOT; - return isHigh() ? dual() : this; - } - - @Override int hash() { return Utils.fold(_min) * Utils.fold(_max); } - @Override public boolean eq( Type t ) { - return t instanceof TypeInteger i && _min==i._min && _max==i._max; - } -} diff --git a/src/main/java/com/seaofnodes/simple/type/TypeMem.java b/src/main/java/com/seaofnodes/simple/type/TypeMem.java deleted file mode 100644 index 99b19c75..00000000 --- a/src/main/java/com/seaofnodes/simple/type/TypeMem.java +++ /dev/null @@ -1,108 +0,0 @@ -package com.seaofnodes.simple.type; - -import com.seaofnodes.simple.util.Ary; -import com.seaofnodes.simple.util.SB; -import com.seaofnodes.simple.util.Utils; -import java.util.ArrayList; -import java.util.BitSet; - - -/** - * Represents a slice of memory corresponding to a set of aliases - */ -public class TypeMem extends Type { - - // Which slice of memory? - // 1 and TOP means no slice. - // 1 and BOT means all memory. - // N means alias slice#N. - public int _alias; - public Type _t; // Memory contents, some scalar type - - private static final Ary FREE = new Ary<>(TypeMem.class); - private TypeMem(int alias, Type t) { super(TMEM); init(alias,t); } - private TypeMem init(int alias, Type t) { - assert alias>1 || (t==Type.TOP || t==Type.BOTTOM); - _alias = alias; - _t = t; - return this; - } - @Override TypeMem free(Type t) { - TypeMem mem = (TypeMem)t; - mem._alias= -99; - mem._dual = null; - mem._hash = 0; - FREE.push(mem); - return this; - } - @Override boolean isFree() { return _alias == -99; } - - public static TypeMem malloc(int alias, Type t) { - return FREE.isEmpty() ? new TypeMem(alias,t) : FREE.pop().init(alias,t); - } - public static TypeMem make(int alias, Type t) { - TypeMem f = malloc(alias,t); - TypeMem f2 = f.intern(); - if( f2==f ) return f; - return VISIT.isEmpty() ? f2.free(f) : f2.delayFree(f); - } - - public static final TypeMem TOP = make(1, Type.TOP ); - public static final TypeMem BOT = make(1, Type.BOTTOM); - - public static void gather(ArrayList ts) { ts.add(make(2,Type.NIL)); ts.add(make(2,TypeInteger.ZERO)); ts.add(BOT); } - - @Override - TypeMem xmeet(Type t) { - TypeMem that = (TypeMem) t; // Invariant: TypeMem and unequal - if( this==TOP ) return that; - if( that==TOP ) return this; - if( this==BOT ) return BOT; - if( that==BOT ) return BOT; - int alias = _alias==that._alias ? _alias : 1; - Type mt = _t.meet(that._t); - return make(alias,mt); - } - - @Override - Type xdual() { return _t._dual==_t ? this : malloc(_alias,_t.dual()); } - - @Override TypeMem rdual() { - if( _dual!=null ) return dual(); - assert !_terned; - TypeMem d = malloc(_alias,null); - (_dual = d)._dual = this; // Cross link duals - d._t = _t._terned ? _t.dual() : _t.rdual(); - return d; - } - - @Override public boolean isHigh() { return _t.isHigh(); } - @Override public int log_size() { throw Utils.TODO(); } - @Override boolean _isFinal() { return _t._isFinal(); } - @Override boolean _isGLB(boolean mem) { return _t._isGLB(true); } - @Override public Type _glb(boolean mem) { return make(_alias,_t._glb(true)); } - - @Override int hash() { return 9876543 + _alias + _t.hashCode(); } - - @Override boolean eq(Type t) { - TypeMem that = (TypeMem) t; // Invariant - return _alias == that._alias && _t == that._t; - } - - @Override boolean cycle_eq(Type t) { - TypeMem that = (TypeMem) t; // Invariant - return _alias == that._alias && _t.cycle_eq(that._t); - } - - @Override int nkids() { return 1; } - @Override Type at( int idx ) { return _t; } - @Override void set( int idx, Type t ) { _t = t; } - - @Override public SB _print(SB sb, BitSet visit, boolean html) { - sb.p("#"); - if( _alias==0 ) return sb.p(_t._type==TTOP ? "TOP" : "BOT"); - return _t.print(sb.p(_alias).p(":"),visit,html); - } - - @Override public String str() { return toString(); } -} diff --git a/src/main/java/com/seaofnodes/simple/type/TypeMemPtr.java b/src/main/java/com/seaofnodes/simple/type/TypeMemPtr.java deleted file mode 100644 index 6041155d..00000000 --- a/src/main/java/com/seaofnodes/simple/type/TypeMemPtr.java +++ /dev/null @@ -1,147 +0,0 @@ -package com.seaofnodes.simple.type; - -import com.seaofnodes.simple.util.Ary; -import com.seaofnodes.simple.util.SB; -import com.seaofnodes.simple.util.Utils; -import java.util.*; - -/** - * Represents a Pointer to memory. - * - * Null is generic pointer to non-existent memory. - * *void is a non-Null pointer to all possible refs, both structs and arrays. - * Pointers can be to specific struct and array types, or a union with Null. - * The distinguished *$BOT ptr represents union of *void and Null. - * The distinguished *$TOP ptr represents the dual of *$BOT. - */ -public class TypeMemPtr extends TypeNil { - // A TypeMemPtr is pair (obj,nil) - // where obj is a TypeStruct, possibly TypeStruct.BOT/TOP - // where nil is an explicit null is allowed or not - - // Examples: - // (Person,false) - a not-nil Person - // (Person,true ) - a Person or a nil - // (BOT ,false) - a not-nil void* (unspecified struct) - // (TOP ,true ) - a nil - - public TypeStruct _obj; - public boolean _one; // Singleton instance - - private static final Ary FREE = new Ary<>(TypeMemPtr.class); - private TypeMemPtr(byte nil, TypeStruct obj, boolean one) { super(TMEMPTR,nil); init(nil,obj,one); } - private TypeMemPtr init(byte nil, TypeStruct obj, boolean one) { _nil=nil; _obj=obj; _one=one; return this; } - // Return a filled-in TypeMemPtr; either from free list or alloc new. - private static TypeMemPtr malloc(byte nil, TypeStruct obj, boolean one) { - return FREE.isEmpty() ? new TypeMemPtr(nil,obj,one) : FREE.pop().init(nil,obj,one); - } - - // All fields directly listed - public static TypeMemPtr make(byte nil, TypeStruct obj, boolean one) { - TypeMemPtr tmp = malloc(nil, obj, one); - TypeMemPtr t2 = tmp.intern(); - if( t2==tmp ) return tmp; - return VISIT.isEmpty() ? t2.free(tmp) : t2.delayFree(tmp); - } - @Override TypeMemPtr free(Type t) { - TypeMemPtr tmp = (TypeMemPtr)t; - tmp._nil = -99; - tmp._obj = null; - tmp._dual = null; - tmp._hash = 0; - FREE.push(tmp); - return this; - } - @Override boolean isFree() { return _obj==null; } - - static TypeMemPtr make(byte nil, TypeStruct obj) { return make(nil, obj, false); } - public static TypeMemPtr make (TypeStruct obj) { return make((byte)2, obj); } - public static TypeMemPtr makeNullable(TypeStruct obj) { return make((byte)3, obj); } - - public TypeMemPtr makeFrom(TypeStruct obj) { return obj==_obj ? this : make(_nil, obj, _one); } - public TypeMemPtr makeNullable() { return makeFrom((byte)3); } - @Override TypeMemPtr makeFrom(byte nil) { return nil==_nil ? this : make(nil, _obj, _one); } - - // An abstract pointer, pointing to either a Struct or an Array. - // Can also be null or not, so 4 choices {TOP,BOT} x {nil,not} - public static final TypeMemPtr BOT = make((byte)3, TypeStruct.BOT); - public static final TypeMemPtr TOP = BOT.dual(); - public static final TypeMemPtr NOTBOT = make((byte)2,TypeStruct.BOT); - - public static final TypeMemPtr TEST= make((byte)2, TypeStruct.TEST); - public static void gather(ArrayList ts) { ts.add(NOTBOT); ts.add(BOT); ts.add(TEST); } - - @Override - public TypeNil xmeet(Type t) { - TypeMemPtr that = (TypeMemPtr) t; - assert !isFree() && !that.isFree(); - return make(xmeet0(that), (TypeStruct)_obj.meet(that._obj), _one & that._one); - } - - @Override - TypeMemPtr xdual() { return malloc( dual0(), _obj.dual(), _one); } - - @Override TypeMemPtr rdual() { - if( _dual!=null ) return dual(); - assert !_terned; - TypeMemPtr d = malloc(dual0(), null, _one); - (_dual = d)._dual = this; // Cross link duals - d._obj = _obj._terned ? _obj.dual() : _obj.rdual(); - return d; - } - - // RHS is NIL; do not deep-dual when crossing the center line - @Override Type meet0() { return _nil==3 ? this : make((byte)3,_obj); } - - @Override boolean _isConstant() { return _one && _obj._isConstant(); } - @Override boolean _isFinal() { return _obj._isFinal(); } - @Override TypeMemPtr _makeRO() { return makeFrom(_obj._makeRO()); } - @Override boolean _isGLB(boolean mem) { return _obj.isGLB2(); } - @Override TypeMemPtr _glb(boolean mem) { return make((byte)3,_obj.glb2()); } - @Override TypeMemPtr _close() { return makeFrom(_obj._close()); } - - - // True if this "isa" t up to named structures - @Override public boolean shallowISA( Type t ) { - if( !(t instanceof TypeMemPtr that) ) return false; - if( this==that ) return true; - if( xmeet0(that)!=that._nil ) return false; - if( _obj==that._obj ) return true; - if( _obj._name.equals(that._obj._name) ) - return true; // Shallow, do not follow matching names, just assume ok - throw Utils.TODO(); // return _obj.shallowISA(that._obj); - } - - // Is forward-reference - @Override public boolean isFRef() { return _obj.isFRef(); } - - @Override public int log_size() { return 3; } // (1<<3)==8-byte pointers - - @Override int hash() { return _obj.hashCode() ^ super.hash() ^ (_one ? 2048 : 0); } - - @Override boolean eq(Type t) { - TypeMemPtr ptr = (TypeMemPtr)t; // Invariant - return super.eq(ptr) && _one == ptr._one && _obj == ptr._obj; - } - @Override boolean cycle_eq(Type t) { - if( t._type != TMEMPTR ) return false; - TypeMemPtr ptr = (TypeMemPtr)t; // Invariant - return super.eq(ptr) && _one == ptr._one && _obj.cycle_eq(ptr._obj); - } - - @Override int nkids() { return 1; } - @Override Type at( int idx ) { return _obj; } - @Override void set( int idx, Type t ) { _obj = (TypeStruct)t; } - - @Override public String str() { - if( this== NOTBOT) return "*void"; - if( this== BOT) return "*void?"; - return x()+"*"+_obj.str()+q(); - } - - @Override SB _print(SB sb, BitSet visit, boolean html ) { - if( this== NOTBOT) return sb.p("*void"); - if( this== BOT) return sb.p("*void?"); - return _obj.print(sb.p(x()).p("*"),visit,html).p(q()); - } -} diff --git a/src/main/java/com/seaofnodes/simple/type/TypeNil.java b/src/main/java/com/seaofnodes/simple/type/TypeNil.java deleted file mode 100644 index 56d73b00..00000000 --- a/src/main/java/com/seaofnodes/simple/type/TypeNil.java +++ /dev/null @@ -1,62 +0,0 @@ -package com.seaofnodes.simple.type; - -import com.seaofnodes.simple.util.SB; -import com.seaofnodes.simple.util.Utils; -import java.util.ArrayList; - -/** - * Nil-able Scalar types - */ -public abstract class TypeNil extends Type { - // 0 = high-subclass choice nil - // 1 = high-subclass no nil - // 2 = low -subclass no nil - // 3 = low -subclass also nil - byte _nil; - - TypeNil(byte t, byte nil ) { super(t); _nil = nil; } - - public static void gather(ArrayList ts) { } - - abstract TypeNil makeFrom(byte nil); - - byte xmeet0(TypeNil that) { return (byte)Math.max(_nil,that._nil); } - - byte dual0() { return (byte)(3-_nil); } - - // RHS is NIL - abstract Type meet0(); - - // RHS is XNIL - Type meetX() { - return _nil==0 ? XNIL : (_nil<=2 ? TypePtr.NPTR : TypePtr.PTR); - } - - Type nmeet(TypeNil tn) { - // Invariants: both are TypeNil subclasses and unequal classes. - // If this is TypePtr, we went to TypePtr.nmeet and not here. - // If that is TypePtr, this is not (invariant); reverse and go again. - if( tn instanceof TypePtr ts ) return ts.nmeet(this); - - // Two mismatched TypeNil, no Scalar. - if( _nil==0 && tn._nil==0 ) return XNIL; - if( _nil<=2 && tn._nil<=2 ) return TypePtr.NPTR; - return TypePtr.PTR; - } - - @Override public boolean isHigh () { return _nil <= 1; } - @Override public boolean isHighOrConst() { return isHigh() || isConstant(); } - - public boolean notNull() { return _nil==1 || _nil==2; } - public boolean nullable() { return _nil==3; } - - @Override boolean _isConstant() { return false; } - @Override Type _glb(boolean mem) { return Type.NIL; } - - final String q() { return _nil == 1 || _nil == 2 ? "" : "?"; } - final String x() { return isHigh() ? "~" : ""; } - - int hash() { return _nil<<17; } - - @Override boolean eq(Type ptr) { return ptr instanceof TypeNil nil && _nil ==nil._nil; } -} diff --git a/src/main/java/com/seaofnodes/simple/type/TypePtr.java b/src/main/java/com/seaofnodes/simple/type/TypePtr.java deleted file mode 100644 index 75478119..00000000 --- a/src/main/java/com/seaofnodes/simple/type/TypePtr.java +++ /dev/null @@ -1,55 +0,0 @@ -package com.seaofnodes.simple.type; - -import com.seaofnodes.simple.util.SB; -import com.seaofnodes.simple.util.Utils; -import java.util.ArrayList; -import java.util.BitSet; - -/** - * Represents a Scalar; a single register-sized value. - */ -public class TypePtr extends TypeNil { - private static final String[] STRS = new String[]{"~ptr","~nptr","nptr","ptr"}; - - private TypePtr(byte nil) { super(TPTR,nil); } - - // An abstract pointer, pointing to either a Struct or an Array. - // Can also be null or not, so 4 choices {TOP,BOT} x {nil,not} - public static TypePtr XPTR = new TypePtr((byte)0).intern(); - public static TypePtr XNPTR= new TypePtr((byte)1).intern(); - public static TypePtr NPTR = (TypePtr)XNPTR.dual(); - public static TypePtr PTR = (TypePtr)XPTR.dual(); - private static final TypePtr[] PTRS = new TypePtr[]{XPTR,XNPTR,NPTR,PTR}; - - public static void gather(ArrayList ts) { ts.add(PTR); ts.add(NPTR); } - - TypeNil makeFrom(byte nil) { throw Utils.TODO(); } - - @Override public TypeNil xmeet(Type t) { - TypePtr that = (TypePtr) t; - return PTRS[xmeet0(that)]; - } - - @Override TypePtr xdual() { return new TypePtr((byte)(3-_nil)); } - - // High scalar loses, low scalar wins - @Override TypeNil nmeet(TypeNil tn) { - if( _nil==0 ) return tn; // High scalar loses - if( _nil==1 ) return tn.makeFrom(xmeet0(tn)); // High scalar loses - if( _nil==2 ) return tn._nil==3 ? PTR : NPTR; // Low scalar wins - return PTR; // this - } - - - // RHS is NIL - @Override Type meet0() { return isHigh() ? NIL : PTR; } - // RHS is XNIL - // 0->xscalar, 1->nscalar, 2->nscalar, 3->scalar - @Override Type meetX() { return _nil==0 ? XNIL : (_nil==3 ? PTR : NPTR); } - - boolean _isGLB(boolean mem) { return this==PTR; } - @Override TypePtr _glb(boolean mem) { return PTR; } - - @Override public String str() { return STRS[_nil]; } - @Override public SB _print(SB sb, BitSet visit, boolean html) { return sb.p(str()); } -} diff --git a/src/main/java/com/seaofnodes/simple/type/TypeRPC.java b/src/main/java/com/seaofnodes/simple/type/TypeRPC.java deleted file mode 100644 index bd5e2923..00000000 --- a/src/main/java/com/seaofnodes/simple/type/TypeRPC.java +++ /dev/null @@ -1,99 +0,0 @@ -package com.seaofnodes.simple.type; - -import com.seaofnodes.simple.util.SB; -import com.seaofnodes.simple.util.Utils; -import java.util.ArrayList; -import java.util.BitSet; -import java.util.HashSet; - -/** - * Return Program Control or Return PC or RPC - */ -public class TypeRPC extends Type { - - // A set of CallEndNode IDs (or StopNode); commonly just one. - // Basically a sparse bit set - final HashSet _rpcs; - - // If true, invert the meaning of the bits - final boolean _any; - - private TypeRPC(boolean any, HashSet rpcs) { - super(TRPC); - _any = any; - _rpcs = rpcs; - } - private static TypeRPC make(boolean any, HashSet rpcs) { - return new TypeRPC(any,rpcs).intern(); - } - - public static TypeRPC constant(int cend) { - HashSet rpcs = new HashSet<>(); - rpcs.add(cend); - return make(false,rpcs); - } - - public static final TypeRPC BOT = make(true,new HashSet<>()); - private static final TypeRPC TEST2 = constant(2); - private static final TypeRPC TEST3 = constant(2); - - public static void gather(ArrayList ts) { ts.add(BOT); ts.add(TEST2); ts.add(TEST3); } - - @Override public String str() { - if( _rpcs.isEmpty() ) - return _any ? "$[ALL]" : "$[]"; - if( _rpcs.size()==1 ) - for( Integer rpc : _rpcs ) - return _any ? "$[-"+rpc+"]" : "$["+rpc+"]"; - return "$["+(_any ? "-" : "")+_rpcs+"]"; - } - - @Override - public TypeRPC xmeet(Type other) { - TypeRPC rpc = (TypeRPC)other; - // If the two sets are equal, the _any must be unequal (invariant), - // so they cancel and all bits are set. - if( _rpcs.equals(rpc._rpcs) ) - return BOT; - // Classic union of bit sets (which might be infinite). - HashSet lhs = _rpcs, rhs = rpc._rpcs; - // Smaller on left - if( lhs.size() > rhs.size() ) { lhs = rpc._rpcs; rhs = _rpcs; } - - HashSet rpcs = new HashSet<>(); - boolean any = true; - // If both sets are infinite, intersect. - if( _any && rpc._any ) { - for( Integer i : lhs ) if( rhs.contains(i) ) rpcs.add(i); - - } else if( !_any && !rpc._any ) { - // if neither set is infinite, union. - rpcs.addAll( lhs ); - rpcs.addAll( rhs ); - any = false; - } else { - // If one is infinite, subtract the other from it. - HashSet sub = _any ? rpc._rpcs : _rpcs; - HashSet inf = _any ? _rpcs : rpc._rpcs; - for( Integer i : inf ) - if( inf.contains(i) && !sub.contains(i) ) - rpcs.add(i); - } - return make(any,rpcs); - } - - @Override - Type xdual() { return new TypeRPC(!_any,_rpcs); } - - @Override boolean _isConstant() { return !_any && _rpcs.size()==1; } - @Override boolean _isGLB(boolean mem) { return true; } - - @Override - int hash() { return _rpcs.hashCode() ^ (_any ? -1 : 0) ; } - @Override - public boolean eq( Type t ) { - TypeRPC rpc = (TypeRPC)t; // Contract - return _any==rpc._any && _rpcs.equals(rpc._rpcs); - } - -} diff --git a/src/main/java/com/seaofnodes/simple/type/TypeStruct.java b/src/main/java/com/seaofnodes/simple/type/TypeStruct.java deleted file mode 100644 index 73fecdab..00000000 --- a/src/main/java/com/seaofnodes/simple/type/TypeStruct.java +++ /dev/null @@ -1,480 +0,0 @@ -package com.seaofnodes.simple.type; - -import com.seaofnodes.simple.codegen.CodeGen; -import com.seaofnodes.simple.util.*; -import java.util.*; - -/** - * Represents a struct type. - */ -public class TypeStruct extends Type { - - // A Struct has a name and a set of fields; the fields themselves have - // names, types and aliases. The name has no semantic meaning, but is - // useful for debugging. - - // During parsing a mid-declaration struct is flagged as a "open". It is - // treated as having infinite fields with correct name and type BOTTOM. - - public String _name; // Struct name - public boolean _open; // infinite fields are all true:BOTTOM, false:TOP - public Field[] _fields; - - private TypeStruct(String name, boolean open, Field[] fields) { - super(TSTRUCT); - _name = name; - _open = open; - _fields = fields; - } - - private static final Ary FREE = new Ary<>(TypeStruct.class); - // Return a filled-in TypeStruct; either from free list or alloc new. - private static TypeStruct malloc(String name, boolean open, Field[] fields) { - if( FREE.isEmpty() ) return new TypeStruct(name,open,fields); - TypeStruct ts = FREE.pop(); - assert ts.isFree(); - ts._name = name; - ts._open = open; - ts._fields = fields; - return ts; - } - // Free ts; return this. - @Override TypeStruct free(Type t) { - TypeStruct ts = (TypeStruct)t; - assert !ts.isFree() && !ts._terned; - ts._fields = null; - ts._offs = null; - ts._dual = null; - ts._hash = 0; - FREE.push(ts); - return this; - } - @Override boolean isFree() { return _fields==null; } - - - // All fields directly listed - public static TypeStruct make(String name, boolean open, Field... fields) { - TypeStruct ts = malloc(name, open, fields); - TypeStruct t2 = ts.intern(); - if( t2==ts ) return ts; - return VISIT.isEmpty() ? t2.free(ts) : ts.delayFree(ts); - } - // New open struct with no fields - public static TypeStruct open( String name ) { return make(name,true); } - - // Array - public static TypeStruct makeAry(String name, TypeInteger len, int lenAlias, Type body, int bodyAlias, boolean efinal) { - return make(name,false, - Field.make("#" ,len , lenAlias,true ,false), - Field.make("[]",body,bodyAlias,efinal,false)); - } - - public TypeStruct add( Field f ) { - assert _open && find(f._fname)==-1; // No double field names - Field[] flds = Arrays.copyOf(_fields,_fields.length+1); - flds[_fields.length] = f; - return make(_name,true,flds); - } - public TypeStruct replace( Field f ) { - assert !_open; - Field[] flds = Arrays.copyOf(_fields,_fields.length); - flds[find(f._fname)] = f; - return make(_name,false,flds); - } - - - public final TypeStruct close() { - return (TypeStruct)recurOpen()._close().recurClose(); - } - @Override TypeStruct _close() { - TypeStruct ts = (TypeStruct)VISIT.get(_name); - if( ts!=null ) return ts; - ts = recurPre(_name,false); - Field[] flds = ts._fields; - - // Now start the recursion - for( int i=0; i ts) { - ts.add(BOT); - ts.add(TEST); - ts.add(ARY); - ts.add(STR); - ts.add(ABC); - ts.add(SINT0); - ts.add(SFLT0); - ts.add(SINT1); - ts.add(SFLT1); - ts.add(SFLT2); - ts.add(((TypeMemPtr)(SFLT2.field("s1")._t))._obj); - // Break cyclic init: built a struct - Field fcalloc = Field.make("calloc",TypeFunPtr.CALLOC,-2,true,true); - TypeStruct scalloc = make("calloc",false,fcalloc); - ts.add(scalloc); - - } - - // Find field index by name - public int find(String fname) { - for( int i=0; i<_fields.length; i++ ) - if( _fields[i]._fname.equals(fname) ) - return i; - return -1; - } - public Field field(String fname) { - int idx = find(fname); - return idx== -1 ? null : _fields[idx]; - } - - // Find field index by alias - public int findAlias( int alias ) { - for( int i=0; i<_fields.length; i++ ) - if( _fields[i]._alias==alias ) - return i; - return -1; - } - - @Override - TypeStruct xmeet(Type t) { - TypeStruct that = (TypeStruct) t; - assert !isFree() && !that.isFree(); - if( this==TOP ) return that; - if( that==TOP ) return this; - if( this==BOT ) return BOT; - if( that==BOT ) return BOT; - - // Within the same compilation unit, struct names are unique. If the - // names differ, its different structs. Across many compilation units, - // structs with the same name but different field layouts can be - // interned... which begs the question: - // "What is the meet of structs from two different compilation units?" - // And the answer is: "don't ask". - if( !_name.equals(that._name) ) - return BOT; // It's a struct; that's about all we know - - // if equal, no matters. - // if short is BOT, chop ; recurPre on short. - // if short is TOP, copy long; recurPre on long. - TypeStruct min = _fields.length < that._fields.length ? this : that; - if( _fields.length != that._fields.length && (min._open ^ min==this) ) - return that.xmeet(this); - - // Check all other fields are sanely similar; same struct type but - // different field contents (e.g. field "age" is either 'int' or '18'). - int len = Math.min(_fields.length,that._fields.length); - for( int i=0; i=2 && _fields[_fields.length-1]._fname=="[]"; } - - public int aryBase() { - assert isAry(); - if( _offs==null ) _offs = offsets(); - return _offs[1]; - } - public int aryScale() { - assert isAry(); - return _fields[1]._t.log_size(); - } - - - // Field offsets as packed byte offsets - private int[] _offs; // Lazily computed and not part of the type semantics - public int offset(int idx) { - if( _offs==null ) _offs = offsets(); - return _offs[idx]; - } - private int[] offsets() { // Field byte offsets - assert CodeGen.CODE._phase.ordinal() >= CodeGen.Phase.Opto.ordinal(); - // Compute a layout for a collection of fields - assert _fields != null; // No forward refs - - // Compute a layout - _offs = new int[_fields.length+1]; - if( isAry() ) { // Arrays used fixed u32 len, padding to elems, var length - assert _fields[1]._fname=="[]"; - // Pad out to the element alignment - int align = _fields[1]._t.alignment(); - _offs[1] = (4 + ((1<=0; i-- ) { - offs[i] = off; - off += cnts[i]<
").append(lab).append("
").append("\n"); - sb.append("\t\t\t\t").append("\n"); - sb.append("\t\t\t\t"); - } - sb.append(""); - } - if (use instanceof CProjNode proj) { - if (!doProjTable) { - doProjTable = true; - sb.append(""); - } - sb.append("\n"); - sb.append("\t\t\t
").append(proj.glabel()).append("").append("\n"); - sb.append("\t\t\t\t").append("\n"); - sb.append("\t\t\t\t"); - } - sb.append(""); - } - } - if (doProjTable) { - sb.append("").append("\n"); - sb.append("\t\t\t\t
").append(proj.glabel()).append("
").append("\n"); - sb.append("\t\t\t
>\n\t\t"); - - } else { - // control nodes have box shape - // other nodes are ellipses, i.e. default shape - if( n instanceof CFGNode ) sb.append("shape=box style=filled fillcolor=yellow "); - else if (n instanceof PhiNode) sb.append("style=filled fillcolor=lightyellow "); - sb.append("label=\"").append(lab).append("\" "); - } - sb.append("];\n"); - } - if (!_separateControlCluster) { - // Force Region & Phis to line up - for (Node n : all) { - if (n instanceof RegionNode region) { - sb.append("\t\t{ rank=same; "); - sb.append(region.uniqueName()).append(";"); - for (Node phi : region._outputs) - if (phi instanceof PhiNode) sb.append(phi.uniqueName()).append(";"); - sb.append("}\n"); - } - } - } - sb.append("\t}\n"); // End Node cluster - } - - private void nodes(StringBuilder sb, Collection all) { - nodesByCluster(sb, true, all); - nodesByCluster(sb, false, all); - } - - // Build a nested scope display, walking the _prev edge - private void scope( StringBuilder sb, ScopeNode scope ) { - sb.append("\tnode [shape=plaintext];\n"); - int last = scope.nIns(); - int max = scope.depth(); - for( int i = 0; i < max; i++ ) { - int level = max-i-1; - String scopeName = makeScopeName(scope, level); - sb.append("\tsubgraph cluster_").append(scopeName).append(" {\n"); // Magic "cluster_" in the subgraph name - // Special for memory ScopeMinNode - if( level==0 ) { - MemMergeNode n = scope.mem(); - sb.append("\t\t").append(n.uniqueName()).append(" [label=\"").append(n.glabel()).append("\"];\n"); - } - sb.append("\t\t").append(scopeName).append(" [label=<\n"); - sb.append("\t\t\t\n"); - // Add the scope level - sb.append("\t\t\t"); - int lexStart = scope._kinds.at(level)._lexSize; - for( int j=lexStart; j").append(v._name).append(""); - } - last = lexStart; - sb.append("\n"); - sb.append("\t\t\t
").append(level).append("
>];\n"); - } - // Scope clusters nest, so the graphics shows the nested scopes, so - // they are not closed as they are printed; so they just keep nesting. - // We close them all at once here. - sb.append( "\t}\n".repeat( max ) ); // End all Scope clusters - } - - private String makeScopeName(ScopeNode sn, int level) { return sn.uniqueName() + "_" + level; } - private String makePortName(String scopeName, String varName) { return scopeName + "_" + varName; } - - // Walk the node edges - private void nodeEdges(StringBuilder sb, Collection all) { - // All them edge labels - sb.append("\tedge [ fontname=Helvetica, fontsize=8 ];\n"); - for( Node n : all ) { - // Do not display the Constant->Start edge; - // ProjNodes handled by Multi; - // ScopeNodes are done separately - if( n instanceof ConstantNode || n instanceof ProjNode || n instanceof CProjNode || n instanceof ScopeNode || n==Parser.XCTRL ) - continue; - for( int i=0; idef edge from Phi to Region. - sb.append('\t').append(n.uniqueName()); - sb.append(" -> "); - sb.append(def.uniqueName()); - sb.append(" [style=dotted taillabel=").append(i).append("];\n"); - } else if( def != null ) { - // Most edges land here use->def - sb.append('\t').append(n.uniqueName()).append(" -> "); - if( def instanceof CProjNode proj ) { - String mname = proj.ctrl().uniqueName(); - sb.append(mname).append(":p").append(proj._idx); - } else if( def instanceof ProjNode proj ) { - String mname = proj.in(0).uniqueName(); - sb.append(mname).append(":p").append(proj._idx); - } else sb.append(def.uniqueName()); - // Number edges, so we can see how they track - sb.append("[taillabel=").append(i); - // The edge from New to ctrl is just for anchoring the New - if ( n instanceof NewNode ) - sb.append(" color=green"); - // control edges are colored red - else if( def instanceof CFGNode ) - sb.append(" color=red"); - else if( def.isMem() ) - sb.append(" color=blue"); - // Backedges do not add a ranking constraint - if( i==2 && (n instanceof PhiNode || n instanceof LoopNode) ) - sb.append(" constraint=false"); - sb.append("];\n"); - } - } - } - } - - // Walk the scope edges - private void scopeEdges( StringBuilder sb, ScopeNode scope ) { - sb.append("\tedge [style=dashed color=cornflowerblue];\n"); - int level=0; - for( var v : scope._vars ) { - Node def = scope.in(v._idx); - while( def instanceof ScopeNode lazy ) - def = lazy.in(v._idx); - if( def==null ) continue; - while( level < scope.depth() && v._idx >= scope._kinds.at(level)._lexSize ) - level++; - String scopeName = makeScopeName(scope, level-1); - sb.append("\t") - .append(scopeName).append(":") - .append('"').append(makePortName(scopeName, v._name)).append('"') // wrap port name with quotes because $ctrl is not valid unquoted - .append(" -> "); - if( def instanceof CProjNode proj ) { - sb.append(def.in(0).uniqueName()).append(":p").append(proj._idx); - } else if( def instanceof ProjNode proj ) { - sb.append(def.in(0).uniqueName()).append(":p").append(proj._idx); - } else sb.append(def.uniqueName()); - sb.append(";\n"); - } - } - - /** - * Finds all nodes in the graph. - */ - public static Collection findAll(Stack ss, Node... ns) { - final HashMap all = new HashMap<>(); - for( Node n : ns ) - walk(all, n); - if( ss != null ) - for( Node n : ss ) - walk(all, n); - return all.values(); - } - - /** - * Walk a subgraph and populate distinct nodes in the all list. - */ - private static void walk(HashMap all, Node n) { - if(n == null ) return; - if (all.get(n._nid) != null) return; // Been there, done that - all.put(n._nid, n); - for (Node c : n._inputs) - walk(all, c); - for( Node c : n._outputs ) - walk(all, c); - } -} diff --git a/src/main/java/com/seaofnodes/simple/print/IRPrinter.java b/src/main/java/com/seaofnodes/simple/print/IRPrinter.java deleted file mode 100644 index d1265bef..00000000 --- a/src/main/java/com/seaofnodes/simple/print/IRPrinter.java +++ /dev/null @@ -1,331 +0,0 @@ -package com.seaofnodes.simple.print; - -import com.seaofnodes.simple.codegen.CodeGen; -import com.seaofnodes.simple.node.*; -import com.seaofnodes.simple.type.TypeFunPtr; -import com.seaofnodes.simple.util.Ary; -import com.seaofnodes.simple.util.SB; -import com.seaofnodes.simple.util.Utils; -import java.util.ArrayList; -import java.util.BitSet; -import java.util.HashMap; - -public abstract class IRPrinter { - - // Print a node on 1 line, columnar aligned, as: - // NNID NNAME DDEF DDEF [[ UUSE UUSE ]] TYPE - // 1234 sssss 1234 1234 1234 1234 1234 1234 tttttt - public static SB printLine( Node n, SB sb ) { - if( n==null ) return sb; - sb.p("%4d %-7.7s ".formatted(n._nid,n.label())); - if( n._inputs==null ) - return sb.p("DEAD\n"); - for( Node def : n._inputs ) - sb.p(def==null ? "____" : "%4d".formatted(def._nid)) - // Lazy Phi indicator - .p(n instanceof MemMergeNode && def instanceof MemMergeNode ? "^" : " "); - for( int i = n._inputs.size(); i<4; i++ ) - sb.p(" "); - sb.p(" [[ "); - for( Node use : n._outputs ) - sb.p(use==null ? "____ " : "%4d ".formatted(use._nid)); - int lim = 6 - Math.max(n._inputs.size(),4); - for( int i = n._outputs.size(); i CodeGen.Phase.Schedule.ordinal() - ? _prettyPrintScheduled( node, depth ) - : _prettyPrint( node, depth ); - } - - // Another bulk pretty-printer. Makes more effort at basic-block grouping. - private static String _prettyPrint( Node node, int depth ) { - // First, a Breadth First Search at a fixed depth. - BFS bfs = new BFS(node,depth); - // Convert just that set to a post-order - ArrayList rpos = new ArrayList<>(); - BitSet visit = new BitSet(); - for( int i=bfs._lim; i< bfs._bfs.size(); i++ ) - postOrd( bfs._bfs.get(i), null, rpos, visit, bfs._bs); - // Reverse the post-order walk - SB sb = new SB(); - boolean gap=false; - for( int i=rpos.size()-1; i>=0; i-- ) { - Node n = rpos.get(i); - if( n instanceof CFGNode || n instanceof MultiNode ) { - if( !gap ) sb.p("\n"); // Blank before multihead - if( n instanceof FunNode fun ) - fun.sig().print(sb.p("--- ").p(fun._name==null ? "" : fun._name).p(" ")).p("----------------------\n"); - printLine( n, sb ); // Print head - while( --i >= 0 ) { - Node t = rpos.get(i); - if( !(t.in(0) instanceof MultiNode) ) { i++; break; } - printLine( t, sb ); - } - if( n instanceof ReturnNode ret ) { - FunNode fun = ret.fun(); - sb.p("--- "); - if( fun != null ) - fun.sig().print(sb.p(fun._name==null ? "" : fun._name).p(" ")); - sb.p("----------------------\n"); - } - if( !(n instanceof CallNode) ) { - sb.p("\n"); // Blank after multitail - gap = true; - } - } else { - printLine( n, sb ); - gap = false; - } - } - return sb.toString(); - } - - private static void postOrd(Node n, Node prior, ArrayList rpos, BitSet visit, BitSet bfs) { - if( !bfs.get(n._nid) ) - return; // Not in the BFS visit - if( n instanceof FunNode && !(prior instanceof StartNode) ) - return; // Only visit Fun from Start - if( visit.get(n._nid) ) return; // Already post-order walked - visit.set(n._nid); - // First walk the CFG, then everything - if( n instanceof CFGNode ) { - for( Node use : n._outputs ) - // Follow CFG, not across call/function borders, and not around backedges - if( use instanceof CFGNode && !(n instanceof CallNode && use instanceof FunNode) && - use.nOuts() >= 1 && !(use._outputs.get(0) instanceof LoopNode) ) - postOrd(use, n, rpos,visit,bfs); - for( Node use : n._outputs ) - // Follow CFG, not across call/function borders - if( use instanceof CFGNode && !(n instanceof CallNode && use instanceof FunNode) ) - postOrd(use,n,rpos,visit,bfs); - } - // Follow all outputs - for( Node use : n._outputs ) - if( use != null && - !(n instanceof CallNode && use instanceof FunNode) && - (n instanceof FunNode || !(use instanceof ParmNode)) ) - postOrd(use, n, rpos,visit,bfs); - // Post-order - rpos.add(n); - } - - // Breadth-first search, broken out in a class to keep in more independent. - // Maintains a root-set of Nodes at the limit (or past by 1 if MultiHead). - public static class BFS { - // A breadth first search, plus MultiHeads for any MultiTails - public final ArrayList _bfs; - public final BitSet _bs; // Visited members by node id - public final int _depth; // Depth limit - public final int _lim; // From here to _bfs._len can be roots for a reverse search - public BFS( Node base, int d ) { - _depth = d; - _bfs = new ArrayList<>(); - _bs = new BitSet(); - - add(base); // Prime the pump - int idx=0, lim=1; // Limit is where depth counter changes - while( idx < _bfs.size() ) { // Ran out of nodes below depth - Node n = _bfs.get(idx++); - for( Node def : n._inputs ) - if( def!=null && !_bs.get(def._nid) ) - add(def); - if( idx==lim ) { // Depth counter changes at limit - if( --d < 0 ) - break; // Ran out of depth - lim = _bfs.size(); // New depth limit - } - } - // Toss things past the limit except multi-heads - while( idx < _bfs.size() ) { - Node n = _bfs.get(idx); - if( n instanceof MultiNode ) idx++; - else del(idx); - } - // Root set is any node with no inputs in the visited set - lim = _bfs.size(); - for( int i=_bfs.size()-1; i>=0; i-- ) - if( !any_visited(_bfs.get(i)) ) - swap( i,--lim); - _lim = lim; - } - void swap( int x, int y ) { - if( x==y ) return; - Node tx = _bfs.get(x); - Node ty = _bfs.get(y); - _bfs.set(x,ty); - _bfs.set(y,tx); - } - void add(Node n) { - _bfs.add(n); - _bs.set(n._nid); - } - void del(int idx) { - _bs.clear(_bfs.get(idx)._nid); - Utils.del(_bfs, idx); - } - boolean any_visited( Node n ) { - for( Node def : n._inputs ) - if( def!=null && _bs.get(def._nid) ) - return true; - return false; - } - } - - public static String _prettyPrint( CodeGen code ) { - SB sb = new SB(); - // Print the Start "block" - printLine(code._start,sb); - for( Node n : code._start._outputs ) - printLine(n,sb); - sb.nl(); - - // Skip start, stop - for( int i=1; i ds = new HashMap<>(); - Ary ns = new Ary<>(Node.class); - _walk(ds,ns,node,depth); - // Remove data projections, these are force-printed behind their multinode head - for( int i=0; i bns = new Ary<>(Node.class); - while( !ds.isEmpty() ) { - CFGNode blk = null; - for( Node n : ns ) { - CFGNode cfg = n instanceof CFGNode cfg0 && cfg0.blockHead() ? cfg0 : n.cfg0(); - if( blk==null || cfg.idepth() < blk.idepth() ) - blk = cfg; - } - Integer d = ds.remove(blk._nid); - ns.del(ns.find(blk)); - - // Print block header - sb.p("%-13.13s".formatted(label(blk)+":")); - sb.p( " ".repeat(4) ).p(" [[ "); - if( blk instanceof StartNode ) ; - else if( blk instanceof RegionNode || blk instanceof StopNode ) - for( int i=(blk instanceof StopNode ? 3 : 1); i ds, Ary ns, Node node, int d ) { - Integer nd = ds.get(node._nid); - if( nd!=null && d <= nd ) return; // Been there, done that - Integer old = ds.put(node._nid,d) ; - if( old == null ) - ns.add(node); - if( d == 0 ) return; // Depth cutoff - for( Node def : node._inputs ) - if( def != null && - !(node instanceof LoopNode loop && loop.back()==def) && - // Don't walk into or out of functions - !(node instanceof CallEndNode && def instanceof ReturnNode) && - !(node instanceof FunNode && def instanceof CallNode) && - !(node instanceof ParmNode && !(def instanceof FunNode)) - ) - _walk(ds,ns,def,d-1); - } - - static String label( CFGNode blk ) { - if( blk instanceof StartNode ) return "START"; - return (blk instanceof LoopNode ? "LOOP" : "L")+blk._nid; - } - static void label( SB sb, CFGNode blk ) { - if( !blk.blockHead() ) blk = blk.cfg(0); - sb.p( "%-9.9s ".formatted( label( blk ) ) ); - } - static void printLine( Node n, SB sb, Ary bns, int i, HashMap ds, Ary ns ) { - printLine( n, sb ); - if( i != -1 ) bns.del(i); - ds.remove(n._nid); - int idx = ns.find(n); - if( idx!=-1 ) ns.del(idx); - } - -} diff --git a/src/main/java/com/seaofnodes/simple/print/JSViewer.java b/src/main/java/com/seaofnodes/simple/print/JSViewer.java deleted file mode 100644 index 62ff0001..00000000 --- a/src/main/java/com/seaofnodes/simple/print/JSViewer.java +++ /dev/null @@ -1,405 +0,0 @@ -package com.seaofnodes.simple.print; - -import com.seaofnodes.simple.codegen.CodeGen; -import com.seaofnodes.simple.node.*; -import com.seaofnodes.simple.type.*; -import com.seaofnodes.simple.util.SB; -import com.seaofnodes.simple.util.Utils; -import java.io.IOException; -import java.nio.file.Paths; -import java.util.Collection; -import java.util.Stack; -import static com.seaofnodes.simple.codegen.CodeGen.CODE; - -public class JSViewer implements AutoCloseable { - // Display programs in an endless loop - public static void main( String[] args ) throws Exception { - try(var js = new JSViewer()) { - js.run(); - } - } - - static boolean SHOW; - - // WebSocket to the browser for display - static SimpleWebSocket SERVER; - - static int N; // Dot frames - - JSViewer() throws Exception { - // Launch server; handshake - SERVER = new SimpleWebSocket(Paths.get("docs/index.html").toUri(),12345) ; - } - - void run( ) throws Exception { - SERVER.put("!"); - while( true ) { - String src = SERVER.get(); - switch( src ) { - case null: return; - case "null": return; - case "+": break; // Client requests more frames, but we send them all anyway - default: - System.out.println(src); - try { - N=0; - // Use parser scope, xscope when building views - CodeGen code = new CodeGen(src); - SHOW = true; - show(); - // Parse program, generating views at every parse point - code.parse(); - // No longer user parse internal state when building views - code.opto(); - - // Catch and ignore Parser errors - } catch(RuntimeException re) { - System.err.println(re); - } finally { - SHOW = false; - SERVER.put("#"); // Final frame - } - break; - } - } - } - - @Override public void close() throws IOException { - SERVER.close(); - SERVER=null; - } - - public static void show() { if( SERVER!=null && SHOW ) _show(); } - private static void _show() { - // Skip util we at least get the Parse object made - if( CODE._phase==null || CODE._phase.ordinal() < CodeGen.Phase.Parse.ordinal() ) - return; - boolean midParse = CODE._phase == CodeGen.Phase.Parse; - Stack xScopes = midParse ? CODE.P._xScopes : null; - - Collection all = GraphVisualizer.findAll(xScopes, CODE._stop, midParse ? CODE.P._scope: null); - SB sb = new SB(); - sb.p("digraph view_").p(N++).p(" {\n").ii(); - if( midParse ) - sb.i().p("// POS: ").p(CODE.P.pos()).nl(); - - // To keep the Scopes below the graph and pointing up into the graph we - // need to group the Nodes in a subgraph cluster, and the scopes into a - // different subgraph cluster. THEN we can draw edges between the - // scopes and nodes. If we try to cross subgraph cluster borders while - // still making the subgraphs DOT gets confused. - sb.i().p("rankdir=BT;\n"); // Force Nodes before Scopes - - // CNC - turned off Apr/8/2024, gives more flex in the layout and - // removes some of the more ludicrous layout choices. - // Preserve node input order - //sb.append("ordering=\"in\";\n"); - - // Merge multiple edges hitting the same node. Makes common shared - // nodes much prettier to look at. - sb.i().p("concentrate=\"true\";\n"); - - // Force nested scopes to order - sb.i().p("compound=\"true\";\n"); - - // Just the Nodes first, in a cluster no edges - nodes(sb, all); - - // Now the scopes, in a cluster no edges - if( midParse ) - for( ScopeNode xscope : xScopes ) - if( !xscope.isDead() ) - scope( sb, xscope ); - - // Walk the Node edges - nodeEdges(sb, midParse && !xScopes.empty() ? xScopes.peek() : null, all); - - // Walk the active Scope edges - if( midParse ) - for( ScopeNode xscope : xScopes ) - if( !xscope.isDead() ) - scopeEdges( sb, xscope ); - - - sb.p("}\n").di(); - // Tell client another DOT frame - String dot = sb.toString(); - System.out.println(dot); - try { - SERVER.put(dot); - } catch( IOException ioe ) { - try { SERVER.close();} catch( IOException ignored ){} - } - } - - - private static void nodes(SB sb, Collection all) { - // Just the Nodes first, in a cluster no edges - sb.i().p("subgraph cluster_Nodes {\n").ii(); // Magic "cluster_" in the subgraph name - for( Node n : all ) { - if( n instanceof ProjNode || n instanceof CProjNode || n instanceof MemMergeNode ) - continue; // Do not emit, rolled into MultiNode or Scope cluster already - sb.i().p(n.uniqueName()).p(" [ "); - if( n instanceof MultiNode && !(n instanceof StartNode) ) { - // Make a box with the MultiNode on top, and all the projections on the bottom - sb. p("shape=plaintext label=<\n").ii(); - sb.i().p("\n"); - // Row over single cell for node label - cell(sb.i().p(""), n.glabel(), n, null).p("\n"); - // Row over single cell, for nested table - sb.i().p("\n"); - sb.i().p("
\n").ii(); - sb.i().p("").p("\n"); - sb.i().p(""); - n._outputs.sort((x,y) -> x instanceof ProjNode xp && y instanceof ProjNode yp ? (xp._idx - yp._idx) : ((x==null ? 99999 : x._nid) - (y==null ? 99999 : y._nid))); - boolean empty_row=true; - for( Node use : n._outputs ) { - int idx = idx(n); - if( idx != -1 ) { - cell(sb,use.glabel(),use,"p"+idx); - empty_row=false; - } - } - // At least one cell on row - if( empty_row ) sb.p(""); - sb. p("").p("\n"); - sb.i().p("
").p("\n").di(); - sb.i().p("
>\n").di(); - sb.i().p("];\n"); - - } else { - // control nodes have box shape - // other nodes are ellipses, i.e. default shape - //else if (n instanceof PhiNode) sb.p("style=filled fillcolor=lightyellow "); - node(sb,n.glabel(),n); - sb.p("];\n"); - } - } - // Force Region & Phis to line up - for (Node n : all) { - if (n instanceof RegionNode region) { - sb.i().p("{ rank=same; "); - sb.p(region.uniqueName()).p(";"); - for (Node phi : region._outputs) - if (phi instanceof PhiNode) sb.p(phi.uniqueName()).p(";"); - sb.p("}\n"); - } - } - sb.di().i().p("}").nl(); // End Node cluster - } - - // Build a nested scope display, walking the _prev edge - private static void scope( SB sb, ScopeNode scope ) { - sb.i().p("node [shape=plaintext];\n"); - int last = scope.nIns(); - int max = scope.depth()+1; // One more than lexsize - for( int i = 0; i < max; i++ ) { - int level = max-i-1; - String scopeName = makeScopeName(scope, level); - sb.i().p("subgraph cluster_").p(scopeName).p(" {\n").ii(); // Magic "cluster_" in the subgraph name - sb.i().p(scopeName).p(" [label=<\n").ii(); - sb.i().p("\n"); - int lexStart = level==0 ? 0 : scope._kinds.at(level-1)._lexSize; - - // Special for memory ScopeMinNode - MemMergeNode n = scope.nIns()>1 ? scope.mem() : null; - if( level==0 && n!=null && n.nIns()>2 ) { - sb.i().p(""); - for( int m=2; m\n"); // End scope level - } - - // Scope variables, empty if none - if( lexStart\n"); - for( int j=lexStart; j\n"); - } - sb.i().p("
>];\n").di(); - // Scope label - sb.i().p(scopeName).p("A [label=\"").p(level).p("\" shape=doublecircle style=filled fillcolor=aqua fontsize=10 margin=0 width=0.2 ];\n"); - } - // Scope clusters nest, so the graphics shows the nested scopes, so - // they are not closed as they are printed; so they just keep nesting. - // We close them all at once here. - for( int i=0; i"),text,n,dark(color)).p("