summaryrefslogtreecommitdiff
path: root/src/main/java/com/seaofnodes
diff options
context:
space:
mode:
authorCliff Click <cliffc@acm.org>2025-06-13 15:42:08 -0700
committerCliff Click <cliffc@acm.org>2025-07-08 20:29:09 -0700
commitfcce1e13814a02d8358eae9a40e33a7e6f2b5400 (patch)
treeb9623369d88e22a0be21dbba472eebfa476d02ab /src/main/java/com/seaofnodes
parent664761624f4b0e3d6d1593b99e576854beec5949 (diff)
downloadSimple-linear-chapter24.tar.gz
Simple-linear-chapter24.zip
Unifying Structs and Arrayslinear-chapter24
Diffstat (limited to 'src/main/java/com/seaofnodes')
-rw-r--r--src/main/java/com/seaofnodes/simple/IterPeeps.java201
-rw-r--r--src/main/java/com/seaofnodes/simple/Parser.java2002
-rw-r--r--src/main/java/com/seaofnodes/simple/Var.java53
-rw-r--r--src/main/java/com/seaofnodes/simple/codegen/BuildLRG.java90
-rw-r--r--src/main/java/com/seaofnodes/simple/codegen/Coalesce.java105
-rw-r--r--src/main/java/com/seaofnodes/simple/codegen/CodeGen.java521
-rw-r--r--src/main/java/com/seaofnodes/simple/codegen/ElfExpr.java87
-rw-r--r--src/main/java/com/seaofnodes/simple/codegen/ElfFile.java381
-rw-r--r--src/main/java/com/seaofnodes/simple/codegen/Encoding.java531
-rw-r--r--src/main/java/com/seaofnodes/simple/codegen/GlobalCodeMotion.java322
-rw-r--r--src/main/java/com/seaofnodes/simple/codegen/IFG.java549
-rw-r--r--src/main/java/com/seaofnodes/simple/codegen/LRG.java233
-rw-r--r--src/main/java/com/seaofnodes/simple/codegen/LinkMem.java23
-rw-r--r--src/main/java/com/seaofnodes/simple/codegen/ListScheduler.java261
-rw-r--r--src/main/java/com/seaofnodes/simple/codegen/Machine.java97
-rw-r--r--src/main/java/com/seaofnodes/simple/codegen/RIPRelSize.java14
-rw-r--r--src/main/java/com/seaofnodes/simple/codegen/RegAlloc.java639
-rw-r--r--src/main/java/com/seaofnodes/simple/codegen/RegMask.java126
-rw-r--r--src/main/java/com/seaofnodes/simple/codegen/RegMaskRW.java36
-rw-r--r--src/main/java/com/seaofnodes/simple/node/AddFNode.java44
-rw-r--r--src/main/java/com/seaofnodes/simple/node/AddNode.java169
-rw-r--r--src/main/java/com/seaofnodes/simple/node/AndNode.java50
-rw-r--r--src/main/java/com/seaofnodes/simple/node/ArithNode.java101
-rw-r--r--src/main/java/com/seaofnodes/simple/node/BoolNode.java164
-rw-r--r--src/main/java/com/seaofnodes/simple/node/CFGNode.java208
-rw-r--r--src/main/java/com/seaofnodes/simple/node/CProjNode.java68
-rw-r--r--src/main/java/com/seaofnodes/simple/node/CallEndMach.java36
-rw-r--r--src/main/java/com/seaofnodes/simple/node/CallEndNode.java108
-rw-r--r--src/main/java/com/seaofnodes/simple/node/CallNode.java176
-rw-r--r--src/main/java/com/seaofnodes/simple/node/CalleeSaveNode.java23
-rw-r--r--src/main/java/com/seaofnodes/simple/node/CastMach.java19
-rw-r--r--src/main/java/com/seaofnodes/simple/node/CastNode.java74
-rw-r--r--src/main/java/com/seaofnodes/simple/node/ConFldOffNode.java43
-rw-r--r--src/main/java/com/seaofnodes/simple/node/ConstantNode.java52
-rw-r--r--src/main/java/com/seaofnodes/simple/node/CtrlNode.java15
-rw-r--r--src/main/java/com/seaofnodes/simple/node/DivFNode.java41
-rw-r--r--src/main/java/com/seaofnodes/simple/node/DivNode.java28
-rw-r--r--src/main/java/com/seaofnodes/simple/node/ExternNode.java23
-rw-r--r--src/main/java/com/seaofnodes/simple/node/FRefNode.java35
-rw-r--r--src/main/java/com/seaofnodes/simple/node/FunNode.java227
-rw-r--r--src/main/java/com/seaofnodes/simple/node/IfNode.java97
-rw-r--r--src/main/java/com/seaofnodes/simple/node/LoadNode.java233
-rw-r--r--src/main/java/com/seaofnodes/simple/node/LoopNode.java105
-rw-r--r--src/main/java/com/seaofnodes/simple/node/MachConcreteNode.java27
-rw-r--r--src/main/java/com/seaofnodes/simple/node/MachNode.java60
-rw-r--r--src/main/java/com/seaofnodes/simple/node/MemMergeNode.java180
-rw-r--r--src/main/java/com/seaofnodes/simple/node/MemOpNode.java109
-rw-r--r--src/main/java/com/seaofnodes/simple/node/MinusFNode.java36
-rw-r--r--src/main/java/com/seaofnodes/simple/node/MinusNode.java39
-rw-r--r--src/main/java/com/seaofnodes/simple/node/MulFNode.java51
-rw-r--r--src/main/java/com/seaofnodes/simple/node/MulNode.java63
-rw-r--r--src/main/java/com/seaofnodes/simple/node/MultiNode.java29
-rw-r--r--src/main/java/com/seaofnodes/simple/node/NeverNode.java22
-rw-r--r--src/main/java/com/seaofnodes/simple/node/NewNode.java131
-rw-r--r--src/main/java/com/seaofnodes/simple/node/Node.java738
-rw-r--r--src/main/java/com/seaofnodes/simple/node/NotNode.java32
-rw-r--r--src/main/java/com/seaofnodes/simple/node/OrNode.java45
-rw-r--r--src/main/java/com/seaofnodes/simple/node/ParmNode.java45
-rw-r--r--src/main/java/com/seaofnodes/simple/node/PhiNode.java271
-rw-r--r--src/main/java/com/seaofnodes/simple/node/ProjNode.java54
-rw-r--r--src/main/java/com/seaofnodes/simple/node/ReadOnlyMach.java19
-rw-r--r--src/main/java/com/seaofnodes/simple/node/ReadOnlyNode.java30
-rw-r--r--src/main/java/com/seaofnodes/simple/node/RegionNode.java168
-rw-r--r--src/main/java/com/seaofnodes/simple/node/ReturnNode.java166
-rw-r--r--src/main/java/com/seaofnodes/simple/node/RoundF32Node.java38
-rw-r--r--src/main/java/com/seaofnodes/simple/node/SarNode.java38
-rw-r--r--src/main/java/com/seaofnodes/simple/node/ScopeNode.java468
-rw-r--r--src/main/java/com/seaofnodes/simple/node/ShlNode.java50
-rw-r--r--src/main/java/com/seaofnodes/simple/node/ShrNode.java34
-rw-r--r--src/main/java/com/seaofnodes/simple/node/SplitNode.java25
-rw-r--r--src/main/java/com/seaofnodes/simple/node/StartNode.java56
-rw-r--r--src/main/java/com/seaofnodes/simple/node/StopNode.java75
-rw-r--r--src/main/java/com/seaofnodes/simple/node/StoreNode.java140
-rw-r--r--src/main/java/com/seaofnodes/simple/node/StructNode.java47
-rw-r--r--src/main/java/com/seaofnodes/simple/node/SubFNode.java44
-rw-r--r--src/main/java/com/seaofnodes/simple/node/SubNode.java43
-rw-r--r--src/main/java/com/seaofnodes/simple/node/ToFloatNode.java34
-rw-r--r--src/main/java/com/seaofnodes/simple/node/XCtrlNode.java15
-rw-r--r--src/main/java/com/seaofnodes/simple/node/XorNode.java44
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/AddARM.java20
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/AddFARM.java22
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/AddIARM.java27
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/AndARM.java21
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/AndIARM.java24
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/AsrARM.java18
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/AsrIARM.java31
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/BranchARM.java77
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/CallARM.java50
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/CallRRARM.java32
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/CmpARM.java22
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/CmpFARM.java22
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/CmpIARM.java40
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/DivARM.java19
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/DivFARM.java19
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/FloatARM.java43
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/FunARM.java22
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/I2F8ARM.java25
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/IntARM.java67
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/LoadARM.java39
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/LslARM.java19
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/LslIARM.java33
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/LsrARM.java19
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/LsrIARM.java28
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/MemOpARM.java68
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/MulARM.java19
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/MulFARM.java18
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/NegARM.java25
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/NewARM.java37
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/NotARM.java27
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/OrARM.java20
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/OrIARM.java24
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/ParmARM.java17
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/ProjARM.java15
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/RetARM.java15
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/SetARM.java32
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/SplitARM.java65
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/StoreARM.java43
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/SubARM.java19
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/SubFARM.java18
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/SubIARM.java28
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/TFPARM.java56
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/TMPARM.java54
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/UJmpARM.java49
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/XorARM.java19
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/XorIARM.java26
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/arm.java816
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/AUIPC.java42
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/AddFRISC.java17
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/AddIRISC.java21
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/AddRISC.java21
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/AndIRISC.java12
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/AndRISC.java16
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/BranchRISC.java93
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/CallRISC.java63
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/CallRRISC.java31
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/DivFRISC.java19
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/DivRISC.java17
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/FltRISC.java46
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/FunRISC.java22
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/I2F8RISC.java23
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/ImmRISC.java53
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/Int8RISC.java46
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/IntRISC.java30
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/LUI.java32
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/LoadRISC.java29
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/MemOpRISC.java76
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/MulFRISC.java17
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/MulRISC.java17
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/NegRISC.java20
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/NewRISC.java47
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/NotRISC.java20
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/OrIRISC.java12
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/OrRISC.java16
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/ParmRISC.java17
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/ProjRISC.java15
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/RetRISC.java21
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/SetFRISC.java33
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/SetIRISC.java17
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/SetRISC.java20
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/SllIRISC.java12
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/SllRISC.java17
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/SplitRISC.java66
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/SraIRISC.java14
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/SraRISC.java17
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/SrlIRISC.java12
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/SrlRISC.java17
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/StoreRISC.java47
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/SubFRISC.java17
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/SubRISC.java16
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/TFPRISC.java55
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/TMPRISC.java41
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/UJmpRISC.java47
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/XorIRISC.java13
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/XorRISC.java16
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/riscv.java543
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddFMemX86.java44
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddFX86.java33
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddIX86.java24
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddMemX86.java34
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddX86.java11
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AndIX86.java11
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AndX86.java11
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CallRX86.java33
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CallX86.java48
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CmpFX86.java32
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CmpIX86.java44
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CmpMemX86.java78
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CmpX86.java32
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/DivFX86.java34
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/DivX86.java33
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/FltX86.java47
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/FunX86.java34
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/I2f8X86.java31
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ImmX86.java38
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/IntX86.java76
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/JmpX86.java81
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/LeaX86.java55
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/LoadX86.java57
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MemAddX86.java40
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MemOpX86.java88
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MulFX86.java32
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MulIX86.java34
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MulX86.java29
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/NegX86.java22
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/NewX86.java25
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/NotX86.java39
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/OrIX86.java11
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/OrX86.java11
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ParmX86.java17
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ProjX86.java15
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/RegX86.java27
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/RetX86.java19
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SarIX86.java39
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SarX86.java24
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SetX86.java54
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShlIX86.java39
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShlX86.java24
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShrIX86.java34
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShrX86.java24
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SplitX86.java107
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/StoreX86.java85
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SubFX86.java31
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SubX86.java10
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/TFPX86.java67
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/TMPX86.java39
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/UJmpX86.java54
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/XorIX86.java11
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/XorX86.java11
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/x86_64_v2.java649
-rw-r--r--src/main/java/com/seaofnodes/simple/print/ASMPrinter.java306
-rw-r--r--src/main/java/com/seaofnodes/simple/print/GraphVisualizer.java287
-rw-r--r--src/main/java/com/seaofnodes/simple/print/IRPrinter.java331
-rw-r--r--src/main/java/com/seaofnodes/simple/print/JSViewer.java405
-rw-r--r--src/main/java/com/seaofnodes/simple/print/SimpleWebSocket.java149
-rw-r--r--src/main/java/com/seaofnodes/simple/sys.java25
-rw-r--r--src/main/java/com/seaofnodes/simple/type/Field.java136
-rw-r--r--src/main/java/com/seaofnodes/simple/type/Type.java514
-rw-r--r--src/main/java/com/seaofnodes/simple/type/TypeConAry.java82
-rw-r--r--src/main/java/com/seaofnodes/simple/type/TypeConAryB.java21
-rw-r--r--src/main/java/com/seaofnodes/simple/type/TypeConAryI.java17
-rw-r--r--src/main/java/com/seaofnodes/simple/type/TypeFloat.java110
-rw-r--r--src/main/java/com/seaofnodes/simple/type/TypeFunPtr.java241
-rw-r--r--src/main/java/com/seaofnodes/simple/type/TypeInteger.java153
-rw-r--r--src/main/java/com/seaofnodes/simple/type/TypeMem.java108
-rw-r--r--src/main/java/com/seaofnodes/simple/type/TypeMemPtr.java147
-rw-r--r--src/main/java/com/seaofnodes/simple/type/TypeNil.java62
-rw-r--r--src/main/java/com/seaofnodes/simple/type/TypePtr.java55
-rw-r--r--src/main/java/com/seaofnodes/simple/type/TypeRPC.java99
-rw-r--r--src/main/java/com/seaofnodes/simple/type/TypeStruct.java480
-rw-r--r--src/main/java/com/seaofnodes/simple/type/TypeTuple.java119
-rw-r--r--src/main/java/com/seaofnodes/simple/util/Ary.java229
-rw-r--r--src/main/java/com/seaofnodes/simple/util/AryInt.java192
-rw-r--r--src/main/java/com/seaofnodes/simple/util/SB.java99
-rw-r--r--src/main/java/com/seaofnodes/simple/util/Utils.java53
254 files changed, 0 insertions, 23108 deletions
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.
- * <br>
- * The theoretical overall worklist is mindless just grabbing the next thing and
- * doing it. If the graph changes, put the neighbors on the worklist.
- * <br>
- * Lather, Rinse, Repeat until the worklist runs dry.
- * <p>
- * The main issues we have to deal with:
- *
- * <ul>
- * <li>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.</li>
- *
- * <li>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.</li>
- *
- * <li>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);`</li>
- * </ul>
- */
-public class IterPeeps {
-
- private final WorkList<Node> _work;
-
- public IterPeeps( long seed ) { _work = new WorkList<>(seed); }
-
- @SuppressWarnings("unchecked")
- public <N extends Node> N add( N n ) { return (N)_work.push(n); }
-
- public void addAll( Ary<Node> 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<E extends Node> {
-
- 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<E> 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.
- * <p>
- * 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()}.
- * <p>
- * 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.
- * <p>
- * 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<String> 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<ScopeNode> _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<String, Type> TYPES = defaultTypes();
-
- // Mapping from a type name to the constructor for a Type.
- public final HashMap<String, StructNode> 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<String, Type> 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 extends Node> 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<ids.length; i++ ) {
- Type t = sig.arg(i);
- _scope.define(ids[i], t, false, new ParmNode(ids[i],i+2,t,fun,con(t)).peephole(), loc);
- }
-
- // Parse the body
- Node last=ZERO;
- while (!peek('}') && !_lexer.isEOF())
- last = parseStatement();
-
- // Last expression is the return except for the top-level main
- if( ctrl()._type==Type.CONTROL && fun.sig() != _code._main )
- fun.addReturn(ctrl(), _scope.mem().merge(), last);
-
- // Pop off the inProgress node on the multi-exit Region merge
- assert r.inProgress();
- r ._inputs.pop();
- rmem._inputs.pop();
- rrez._inputs.pop();
- r._loc = loc(); // Final position
- assert !r.inProgress();
-
- // Force peeps, which have been avoided due to inProgress
- ret.setDef(1,rmem.peephole());
- ret.setDef(2,rrez.peephole());
- ret.setDef(0,r.peephole());
- ret = (ReturnNode)ret.peephole();
-
- // Function scope ends
- _scope.pop();
- _fun = oldfun;
- _breakScope = breakScope;
- _continueScope = continueScope;
-
- // Reset all fields to pre-parse days
- for( int i=0; i<preParse.length; i++ )
- _scope.setDef(i,preParse[i]==null ? null : preParse[i].unkeep());
-
- return ret;
- }
-
- /**
- * Parses a block
- *
- * <pre>
- * '{' statements '}'
- * </pre>
- * 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
- *
- * <pre>
- * returnStatement | declStatement | blockStatement | ifStatement | expressionStatement
- * </pre>
- * @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
- *
- * <pre>
- * while ( expression ) statement
- * </pre>
- * @return a {@link Node}, never {@code null}
- */
- private Node parseWhile() {
- require("(");
- return parseLooping(false);
- }
-
-
- /**
- * Parses a for statement
- *
- * <pre>
- * for( var x=init; test; incr ) body
- * </pre>
- * @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
- *
- * <pre>
- * if ( expression ) statement [else statement]
- * </pre>
- * @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.
- *
- * <pre>
- * 'return' expr ;
- * </pre>
- * @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 ';'
- * <p>
- * 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<varlen; i++ )
- if( !_scope.var(i).isFRef() ) // Promote to outer scope, not defined here
- s.addDef(_scope.in(i)); // Keep the program code to generate instance
-
- // Save instance default constructor
- s.setType(s.compute()); // Do not call peephole, in case the entire struct is a big plain constant
- INITS.put(typeName,s.keep());
-
- // Having finalized the struct, upgrade all the 'self' pointers
- for( Node n : s._inputs )
- if( n._type instanceof TypeFunPtr tfp && _code.link(tfp) instanceof FunNode fun )
- _code.addAll( fun._outputs );
-
-
- // Done with struct/block scope
- _nestedType = old;
- require("}");
- require(";");
- _scope.pop();
- return ZERO;
- }
-
-
- // Parse and return a type or null. Valid types always are followed by an
- // 'id' which the caller must parse. This lets us distinguish forward ref
- // types (which ARE valid here) from local vars in an (optional) forward
- // ref type position.
-
- // t = int|i8|i16|i32|i64|u8|u16|u32|u64|byte|bool | flt|f32|f64 | val | var | struct[?]
- private Type type() {
- int old1 = pos();
- // Only type with a leading `{` is a function pointer...
- if( peek('{') ) return typeFunPtr();
-
- // Otherwise you get a type name
- String tname = _lexer.matchId();
- if( tname==null ) return null;
-
- // Convert the type name to a type.
- Type t0 = TYPES.get(tname);
- // No new types as keywords
- if( t0 == null && KEYWORDS.contains(tname) )
- return posT(old1);
- if( t0 == Type.BOTTOM || t0 == Type.TOP ) return t0; // var/val type inference
-
- // Check for subtype.
- while( true ) {
- int old2 = pos();
- if( !match(".") ) break;
- String sname = _lexer.matchId();
- if( sname==null ) { pos(old2); break; }
- String tsname = tname+"."+sname;
- t0 = TYPES.get(tsname);
- if( t0==null ) { pos(old2); t0 = TYPES.get(tname); break; }
- tname = tsname;
- }
-
- // Still no type found? Assume forward reference
- Type t1 = t0 == null ? TypeMemPtr.make(TypeStruct.open(tname)) :t0; // Null: assume a forward ref type
- // Nest arrays and '?' as needed
- Type t2 = t1;
- while( true ) {
- if( match("?") ) {
- if( !(t2 instanceof TypeMemPtr tmp) )
- throw error("Type "+t0+" cannot be null");
- if( tmp.nullable() ) throw error("Type "+t2+" already allows null");
- t2 = tmp.makeNullable();
- } else if( match("[~]") ) {
- t2 = typeAry(t2,true);
- } else if( match("[]") ) {
- t2 = typeAry(t2,false);
- } else
- break;
- }
-
- // Check no forward ref
- if( t0 != null ) return t2;
- // Check valid forward ref, after parsing all the type extra bits.
- // Cannot check earlier, because cannot find required 'id' until after "[]?" syntax
- int old2 = pos();
- match("!");
- String id = _lexer.matchId();
- if( !(peek(',') || peek(';') || match("->")) || 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<Type> 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:
- *
- * <pre>
- * expr : bitwise [? expr [: expr]]
- * </pre>
- * @return an expression {@link Node}, never {@code null}
- */
- private Node parseExpression() {
- Node expr = parseBitwise();
- return match("?") ? parseTrinary(expr,false,":") : expr;
- }
-
- /**
- * Parse an bitwise expression
- *
- * <pre>
- * bitwise : compareExpr (('&' | '|' | '^') compareExpr)*
- * </pre>
- * @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:
- *
- * <pre>
- * expr : additiveExpr op additiveExpr
- * </pre>
- * @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
- *
- * <pre>
- * shiftExpr : additiveExpr (('<<' | '>>' | '>>>') additiveExpr)*
- * </pre>
- * @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
- *
- * <pre>
- * additiveExpr : multiplicativeExpr (('+' | '-') multiplicativeExpr)*
- * </pre>
- * @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
- *
- * <pre>
- * multiplicativeExpr : unaryExpr (('*' | '/') unaryExpr)*
- * </pre>
- * @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.
- *
- * <pre>
- * unaryExpr : ('-') unaryExpr | '!') unaryExpr | postfixExpr | primaryExpr | '--' Id | '++' Id
- * </pre>
- * @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:
- *
- * <pre>
- * primaryExpr : integerLiteral | "string" | 'char' | true | false | null |
- * new Type | '(' expression ')' | Id['++','--'] |
- * [static.]* Id
- * </pre>
- * @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<Node> 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<fs.length; i++ )
- // An initial TOP means the field needs to be initialized. We
- // store a BOT initially; any partial init will fall to BOT
- // (merge BOT and the partial) and be obviously only a partial
- // init. To be initialized the field needs a full clobber.
- _scope.define(fs[i]._fname, fs[i]._t, fs[i]._final, s.in(i)._type==Type.TOP ? con(Type.BOTTOM) : s.in(i), loc);
- // Parse the constructor body
- require(parseBlock(new Kind.Alloc(tmp)),"}");
- init = _scope._inputs;
- }
- // Check that all fields are initialized
- for( int i=idx; i<init.size(); i++ )
- if( init.at(i)._type == Type.TOP || init.at(i)._type == Type.BOTTOM )
- throw error("'"+tmp._obj._name+"' is not fully initialized, field '" + fs[i-idx]._fname + "' needs to be set in a constructor");
- Node ptr = newStruct(tmp._obj, off(tmp._obj, " len"), idx, init );
- if( hasConstructor )
- _scope.pop();
- return ptr;
- }
-
-
- /**
- * Return a NewNode initialized memory.
- * @param obj is the declared type, with GLB fields
- * @param init is a collection of initialized fields
- */
- private Node newStruct( TypeStruct obj, Node size, int idx, Ary<Node> 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<Node> 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 '#'
- *
- * <pre>
- * expr ('.' FIELD)* [ = expr ] // Field reference
- * expr '#' // Postfix unary read operator
- * expr ['++' | '--' ] // Postfix unary write operator
- * expr ('[' expr ']')* = [ = expr ] // Array reference
- * expr '(' [args,]* ')' // Function call
- * </pre>
- */
- 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 '#'
- *
- * <pre>
- * expr ['++' | '--' ] // Postfix unary write operator
- * expr [ [op]= expr ] // Field reference
- * expr ('[' expr ']')* [ [op]= expr ] // Array reference
- * </pre>
- */
- private Node parsePostfixName(Node expr, String name) {
-
- if( expr._type==Type.NIL )
- throw error("Accessing unknown field '" + name + "' from 'null'");
-
- // 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 "{}"
- *
- * <pre>
- * { [type arg,]* -> expr }
- * { expr } // The no-argument function
- * </pre>
- */
- private Node func() {
- Ary<Type> ts = new Ary<>(Type.class);
- Ary<String> 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 "()"
- * <pre>
- * ( arg* )
- * </pre>
- */
- 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<Node> args = new Ary<>( Node.class );
- args.push(null); // Space for ctrl,mem
- args.push(null);
- if( self != null ) // Method call has a self
- args.push(self);
- while( !peek(')') ) {
- Node arg = parseAsgn();
- if( arg==null ) break;
- 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
- *
- * <pre>
- * integerLiteral: [1-9][0-9]* | [0]
- * floatLiteral: [digits].[digits]?[e [digits]]?
- * </pre>
- */
- 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> 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<phi.nIns(); i++ )
- if( (lrg = alloc.lrg(phi.in(i))) != null )
- break;
- // If none, make one.
- if( lrg==null ) lrg = alloc.newLRG(n);
- if( phi instanceof MachNode ) defLRG(alloc,n);
- // Pass 2: everybody uses the same LRG
- lrg=alloc.union(lrg,phi);
- for( int i=phi instanceof ParmNode ? 2 : 1; i<n.nIns(); i++ )
- lrg=alloc.union(lrg,n.in(i));
- if( lrg._mask!=null && lrg._mask.isEmpty() )
- alloc.fail(lrg);
-
- } else if( n instanceof MachNode mach ) {
- // Attempt to commute ops to keep live ranges compatible.
- if( mach.commutes() && n.nOuts()==1 ) {
- int uidx = n.out(0)._inputs.find(n);
- RegMask mask1 = n.in (1) instanceof MachNode machx ? machx.outregmap() : alloc.lrg(n.in(1))._mask;
- RegMask mask2 = n.in (2) instanceof MachNode machx ? machx.outregmap() : alloc.lrg(n.in(2))._mask;
- RegMask masko = n.out(0) instanceof MachNode machx ? machx.regmap(uidx): alloc.lrg(n )._mask;
- if( !mask1.overlap(masko) && mask2.overlap(masko) )
- n.swap12();
- }
-
- // Define live range
- defLRG(alloc,n);
-
- // Now, look in the opposite direction. How are incoming
- // LRGs affected by this node: For all uses, make live lrgs
- for( int i=1; i<n.nIns(); i++ )
- if( n.in(i)!=null ) {
- LRG lrg2 = alloc.lrg(n.in(i));
- if( lrg2 != null ) { // Anti-dep or other, no LRG
- RegMask use_mask = mach.regmap(i); // use_mask is also null for anti-dep
- if( use_mask!=null && !lrg2.machUse(mach,(short)i,use_mask.size1()).and(use_mask) )
- alloc.fail(lrg2); // Empty register mask, must split
- }
- }
-
- }
-
- // MultiNodes have projections which set registers
- if( n instanceof MultiNode && !(n instanceof CFGNode) )
- for( Node proj : n.outs() )
- if( proj instanceof MachNode )
- defLRG(alloc,proj);
- }
-
- // Collect live ranges
- alloc.unify();
-
- return alloc.success();
- }
-
- private static void defLRG( RegAlloc alloc, Node n ) {
- MachNode mach = (MachNode)n;
- RegMask def_mask = mach.outregmap();
- if( def_mask == null ) return;
- LRG lrg = mach.twoAddress() == 0
- ? alloc.newLRG(n) // Define a new LRG for N
- : alloc.lrg2(n,mach.twoAddress()); // Use the matching 2-adr input
- // Record mask and mach
- if( !lrg.machDef(mach,def_mask.size1()).and(def_mask) )
- alloc.fail(lrg); // Empty register mask, must split
- }
-
-}
diff --git a/src/main/java/com/seaofnodes/simple/codegen/Coalesce.java b/src/main/java/com/seaofnodes/simple/codegen/Coalesce.java
deleted file mode 100644
index 689ceceb..00000000
--- a/src/main/java/com/seaofnodes/simple/codegen/Coalesce.java
+++ /dev/null
@@ -1,105 +0,0 @@
-package com.seaofnodes.simple.codegen;
-
-import com.seaofnodes.simple.node.*;
-import com.seaofnodes.simple.util.Ary;
-import com.seaofnodes.simple.util.Utils;
-import java.util.BitSet;
-import java.util.IdentityHashMap;
-
-// Coalesce copies
-abstract public class Coalesce {
-
- public static boolean coalesce( int round, RegAlloc alloc ) {
-
- // Walk all the splits, looking for coalesce chances
- boolean progress = false;
- for( CFGNode bb : alloc._code._cfg ) {
- outer:
- for( int j=0; j<bb.nOuts(); j++ ) {
- Node n = bb.out(j);
- if( !(n instanceof SplitNode) ) continue;
- LRG v1 = alloc.lrg(n);
- LRG v2 = alloc.lrg(n.in(1));
- if( v1 != v2 ) {
- LRG ov1 = v1, ov2 = v2;
- // Get the smaller neighbor count in v1
- if( v1.nadj() > 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<LRG> 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<Node,Node> _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<TypeFunPtr,FunNode> _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 extends Node> N add( N n ) { return _iter.add(n); }
- public void addAll( Ary<Node> 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<Machine>) 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<maxReg; reg++ )
- if( !_callerSave.test(reg) && ((1L<<reg)&neverSave)==0 )
- _retMasks[i++] = new RegMask(reg);
- _rpcMask = new RegMask(_mach.rpc());
- _retMasks[3] = _rpcMask;
-
- // Convert to machine ops
- long t0 = System.currentTimeMillis();
- _uid = 1; // All new machine nodes reset numbering
- var map = new IdentityHashMap<Node,Node>();
- _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<Node,Node> 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<CFGNode> _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<Section> _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<Symbol> _symbols = new Ary<>(Symbol.class);
- Ary<Symbol> _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<TypeFunPtr,Symbol> _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
- * <p>
- * 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).
- * <p>
- * 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<Node,Relo> _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<<log; i++ ) {
- bits.write((int)x);
- x >>= 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<Node,CFGNode> _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<Node,String> _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<LoopNode,Ary<CFGNode>> rpos = new IdentityHashMap<>();
- Ary<CFGNode> 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<LoopNode,Ary<CFGNode>> 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<CFGNode> 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<CFGNode> 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<CFGNode> 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<CFGNode> 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<len; i++ )
- oldStarts[i] = _opStart[_code._cfg.at(i)._nid];
-
- // TODO: Rewrite this algo to use the small "_jmps" list of just the
- // jumps instead of walking all blocks.
- // TODO2: Allow short encodings on non-jumps. This algo doesn't
- // really care jumps vs other rip-relative things.
-
- // Check the size of short jumps, adjust the block starts for a whole
- // pass over all blocks. If some jumps go long, they might stretch the
- // distance for other jumps, so another pass is needed.
- int slide = -1;
- while( slide != 0) { // While no fails
- slide = 0;
- for( int i=0; i<len; i++ ) {
- CFGNode bb = _code._cfg.at(i);
- _opStart[bb._nid] += slide;
- // Slide down all other (non-CFG) ops in the block
- for( Node n : bb._outputs )
- if( n instanceof MachNode && !(n instanceof CFGNode) )
- _opStart[n._nid] += slide;
- if( bb instanceof RIPRelSize riprel ) {
- CFGNode target = (bb instanceof IfNode iff ? iff.cproj(0) : (CFGNode)bb.out(0)).uctrlSkipEmpty();
- // Delta is from opStart to opStart. X86 at least counts
- // the delta from the opEnd, but we don't have the end until
- // we decide the size - so the encSize has to deal
- int delta = _opStart[target._nid] - _opStart[bb._nid];
- byte opLen = riprel.encSize(delta);
- // Recorded size is smaller than the current size?
- if( _opLen[bb._nid] < opLen ) {
- // Start sliding the code down; record slide amount and new size
- slide += opLen - _opLen[bb._nid];
- _opLen[bb._nid] = opLen;
- }
- }
- }
- }
-
-
- // Go again, inserting padding on function headers. Since no
- // short-jumps span function headers, the padding will not make any
- // short jumps fail.
- for( int i=0; i<len; i++ ) {
- CFGNode bb = _code._cfg.at(i);
- // Functions pad to align 16
- if( bb instanceof FunNode ) {
- int newStart = _opStart[bb._nid]+slide;
- slide += (newStart+15 & -16)-newStart;
- }
- _opStart[bb._nid] += slide;
- for( Node n : bb._outputs )
- if( n instanceof MachNode && !(n instanceof CFGNode) )
- _opStart[n._nid] += slide;
- }
- _padFunHeads = true;
-
- // Copy/slide the bits to make space for all the longer branches
- int grow = _opStart[_code._cfg.at(len-1)._nid] - oldStarts[len-1];
- if( grow > 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<Relo>[] raligns = new Ary[5];
- for( Node op : _bigCons.keySet() ) {
- Relo relo = _bigCons.get(op);
- int align = relo._t.alignment();
- Ary<Relo> 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<Type,Integer> targets = new HashMap<>();
-
- // By alignment
- for( int align = 4; align >= 0; align-- ) {
- Ary<Relo> 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<ts._fields.length; fn++ ) {
- Field f = ts._fields[layout[fn]];
- int foff = ts. offset(layout[fn]);
- // Pad up to field
- while( off < foff ) { bits.write(0); off++; };
- // Constant array fields are special
- if( f._fname=="[]" ) { // Must be a constant array
- ((TypeConAry)f._t).write(bits);
- off += ((TypeConAry)f._t).len();
- } else {
- int log = f._t.log_size();
- addN(log,f._t,bits);
- off += 1<<log;
- }
- }
- }
-
-
- // A series of libc/external calls that Simple can link against in a JIT.
- // Since no runtime in the JVM process, using magic numbers for the CPU
- // emulators to pick up on.
- public static int SENTINEL_CALLOC = -4;
- public static int SENTINEL_WRITE = -8;
-
- void patchGlobalRelocations() {
- for( Node src : _externals.keySet() ) {
- int start = _opStart[src._nid];
- String dst = _externals.get(src);
- int target = switch( dst ) {
- case "calloc" -> 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<CFGNode> 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<CFGNode> 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<con._outputs._len; j++ ) {
- Node use = con.out(j);
- FunNode fun2 = use.cfg0().fun();
- if( fun2==fun ) {
- use.setDef(use._inputs.find(con),con2);
- j--;
- }
- }
- }
- }
- }
- }
-
- // ------------------------------------------------------------------------
- // Visit all nodes in CFG Reverse Post-Order, essentially defs before uses
- // (except at loops). Since defs are visited first - and hoisted as early
- // as possible, when we come to a use we place it just after its deepest
- // input.
- private static void schedEarly(CodeGen code) {
- // Reverse Post-Order on CFG
- for( CFGNode cfg : code._cfg ) {
- cfg.loopDepth();
- for( Node n : cfg._inputs )
- _schedEarly(n,code );
- if( cfg instanceof RegionNode )
- for( Node phi : cfg._outputs )
- if( phi instanceof PhiNode )
- _schedEarly(phi,code );
- }
- }
-
- private static void _schedEarly(Node n, CodeGen code) {
- if( n==null || code._visit.get(n._nid) ) return; // Been there, done that
- assert !(n instanceof CFGNode);
- code._visit.set(n._nid);
- // Schedule not-pinned not-CFG inputs before self. Since skipping
- // Pinned, this never walks the backedge of Phis (and thus spins around
- // a data-only loop), eventually attempting relying on some pre-visited-
- // not-post-visited data op with no scheduled control.
- for( Node def : n._inputs )
- if( def!=null && !(def instanceof PhiNode) )
- _schedEarly(def,code);
-
- // If not-pinned (e.g. constants, projections, phi) and not-CFG
- if( !n.isPinned() ) {
- // Schedule at deepest input
- CFGNode early = code._start; // Maximally early, lowest idepth
- if( n.in(0) instanceof CFGNode cfg ) early = cfg;
- for( int i=1; i<n.nIns(); i++ )
- if( n.in(i)!=null && n.in(i).cfg0().idepth() > 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<late.length; i++ )
- if( ns[i] != null && !(ns[i] instanceof ProjNode) )
- ns[i].setDef(0,late[i]);
- }
-
- private static void breadth(Node stop, Node[] ns, CFGNode[] late) {
- // Things on the worklist have some (but perhaps not all) uses done.
- WorkList<Node> 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<lca2.nIns(); i++ ) {
- RegMask mask = mach2.regmap(i);
- if( mask!=null && mask.test(reg) ) {
- early = lca2 instanceof IfNode ? lca2.idom() : lca2;
- break outer;
- }
- }
- RegMask kill = mach2.killmap();
- if( kill != null )
- throw Utils.TODO();
- }
- }
- }
- }
-
- // Walk up from the LCA to the early, looking for best place. This is
- // the lowest execution frequency, approximated by least loop depth and
- // deepest control flow.
- CFGNode best = lca;
- lca = lca.idom(); // Already found best for starting LCA
- for( ; lca != early.idom(); lca = lca.idom() )
- if( better(lca,best) )
- best = lca;
- assert !(best instanceof IfNode);
- ns [n._nid] = n;
- late[n._nid] = best;
- }
-
- // Block of use. Normally from late[] schedule, except for Phis, which go
- // to the matching Region input.
- private static CFGNode use_block(Node n, Node use, CFGNode[] late) {
- if( !(use instanceof PhiNode phi) )
- return late[use._nid];
- CFGNode found=null;
- for( int i=1; i<phi.nIns(); i++ )
- if( phi.in(i)==n )
- found = phi.region().cfg(i)._idom(found,null);
-
- assert found!=null;
- return found;
- }
-
-
- // Least loop depth first, then largest idepth
- private static boolean better( CFGNode lca, CFGNode best ) {
- return lca.loopDepth() < best.loopDepth() ||
- lca instanceof NeverNode ||
- lca.idepth() > 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<phi.nIns(); i++ )
- if( phi.in(i)==load.mem() )
- lca = anti_dep(load,phi.region().cfg(i),load.mem().cfg0(),lca,null);
- break;
- case NewNode st: break;
- case ReturnNode ret: break; // Load must already be ahead of Return
- case MemMergeNode ret: break; // Mem uses now on ScopeMin
- case NeverNode never: break;
- default: throw Utils.TODO();
- }
- }
- return lca;
- }
-
- //
- private static CFGNode anti_dep( MemOpNode load, CFGNode stblk, CFGNode defblk, CFGNode lca, Node st ) {
- // Store and Load overlap, need anti-dependence
- if( stblk._anti==load._nid ) {
- lca = stblk._idom(lca,null); // Raise Loads LCA
- if( lca == stblk && st != null && st._inputs.find(load) == -1 ) // And if something moved,
- st.addDef(load); // Add anti-dep as well
- return lca; // Cap this stores' anti-dep to here
- }
- return lca;
- }
-
-}
diff --git a/src/main/java/com/seaofnodes/simple/codegen/IFG.java b/src/main/java/com/seaofnodes/simple/codegen/IFG.java
deleted file mode 100644
index 6dd6fbf1..00000000
--- a/src/main/java/com/seaofnodes/simple/codegen/IFG.java
+++ /dev/null
@@ -1,549 +0,0 @@
-package com.seaofnodes.simple.codegen;
-
-import com.seaofnodes.simple.node.*;
-import com.seaofnodes.simple.util.Ary;
-import com.seaofnodes.simple.util.Utils;
-import java.util.BitSet;
-import java.util.IdentityHashMap;
-
-// Interference Graph
-abstract public class IFG {
- // Interference Graph Support
-
- // Map from a Basic Block to Live-Out: {a map from a Live Range to a Def}
- private static final IdentityHashMap<CFGNode,IdentityHashMap<LRG,Node>> BBOUTS = new IdentityHashMap<>();
- static void resetBBLiveOut() {
- for( IdentityHashMap<LRG,Node> bbout : BBOUTS.values() )
- bbout.clear();
- }
- static final IdentityHashMap<LRG,Node> TMP = new IdentityHashMap<>();
-
- // Inteference Graph: Array of Bitsets
- static final Ary<BitSet> 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<CFGNode> 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<LRG,Node> live_out = BBOUTS.get(bb);
- TMP.clear();
- if( live_out != null )
- TMP.putAll(live_out); // Copy bits to temp
-
- // A backwards walk over instructions in the basic block
- for( int inum = bb.nOuts()-1; inum >= 0; inum-- ) {
- Node n = bb.out(inum);
- if( n.in(0) != bb ) continue;
- // In a backwards walk, proj users come before the node itself
- if( n instanceof MultiNode )
- for( Node proj : n.outs() )
- if( proj instanceof ProjNode )
- do_node(alloc,proj);
- if( n instanceof MachNode )
- do_node(alloc,n);
- }
-
- // The bb head kills register, e.g. a CallEnd and caller-save registers
- if( bb instanceof MachNode mach )
- kills(alloc,mach);
-
- // Push live-sets backwards to priors in CFG.
- if( bb instanceof RegionNode )
- for( int i=1; i<bb.nIns(); i++ )
- mergeLiveOut(alloc,bb,i);
- else
- mergeLiveOut(alloc,bb,0);
- }
-
- private static void do_node(RegAlloc alloc, Node n) {
- // Defining means killing live LRG
- LRG lrg = alloc.lrg(n);
- if( lrg != null ) {
- // Check for def-side self-conflict live ranges. These must split,
- // and only happens during the first round a particular LRG splits.
- selfConflict(alloc,n,lrg);
- TMP.remove(lrg); // Kill def
- }
-
- // Phis use and define the same live range, i.e. these LRGs already
- // marked conflicted, no need to mark again
- if( n instanceof PhiNode )
- return;
-
- // Kill any killed registers; milli-code routines like New can kill
- // will not being a CFG.
- if( n instanceof MachNode m )
- kills(alloc,m);
-
- // Interfere n with all live
- if( lrg!=null ) {
- // Interfere n with all live
- for( LRG tlrg : TMP.keySet() ) {
- assert tlrg.leader();
- // Skip self
- if( lrg != tlrg &&
- // And register sets overlap
- lrg._mask.overlap(tlrg._mask) )
- // Then tlrg and lrg interfere. If lrg needs the single
- // last tlrg register at some point, either tlrg or lrg
- // must fail. If *n* (a subset of lrg) needs the single
- // last tlrg register then only tlrg must fail.
- if( ((MachNode)n).outregmap().size1() ) {
- if( !tlrg.clr(lrg._mask.firstReg()) ) // Clear bit, no interference
- alloc.fail(tlrg); // Clearing drives mask to empty
- } else addIFG(lrg,tlrg); // Add interference
- }
- }
-
-
- // Check for self-conflict live ranges. These must split, and only
- // happens during the first round when a particular LRG splits.
- // Also record use-side spills for biased coloring.
- // Then make all inputs live.
- for( int i=1; i<n.nIns(); i++ ) {
- Node def = n.in(i);
- if( def==null ) continue;
- LRG lrg1 = alloc.lrg(def);
- if( lrg1==null ) continue; // Anti-dependence, etc, no register
-
- // Check use-side for self-conflict; if so we MUST split
- selfConflict(alloc,def,lrg1);
-
- // Look for a must-use single register conflicting with some other must-def.
- if( n instanceof MachNode m ) {
- RegMask ni_mask = m.regmap(i);
- if( ni_mask!=null && ni_mask.size1() ) { // Must-use single register
- // Search all current live
- for( LRG tlrg : TMP.keySet() ) {
- assert tlrg.leader();
- Node live = TMP.get(tlrg);
- if( live != def && live instanceof MachNode lmach && lmach.outregmap().overlap(ni_mask) ) {
- // Deny the register, since it absolutely must be used here
- if( !tlrg.clr(ni_mask.firstReg()) )
- // Then direct reg-reg conflict between use here (at n.in(i)) and def (of tlrg) there.
- // Fail the older live range, it must move its register.
- alloc.fail( tlrg );
- }
- }
- }
- }
-
- // All inputs live - except in self conflicts, where we keep the prior def alive
- // until it goes.
- TMP.put(lrg1,def);
- }
- }
-
- // Single-register defines *must* have their register; other live ranges
- // *must* avoid this register, so instead of interfering we remove the
- // single register from the other rmask.
- private static void kills( RegAlloc alloc, MachNode m ) {
- RegMask killMask = m.killmap();
- if( killMask==null ) return;
- // Kill registers with all live
- for( LRG tlrg : TMP.keySet() ) {
- assert tlrg.leader();
- // Always, tlrg cannot use kills
- if( tlrg._mask.overlap(killMask) ) {
- // Disallow clone-ables from killing registers. Just fail
- // them and re-clone closer to target... so no kill.
- // Special case for Intel XOR used to zero.
- Node n = (Node)m;
- CFGNode effUseBlk = n.out(0) instanceof PhiNode phi ? phi.region().cfg(phi._inputs.find(n)) : n.out(0).cfg0();
- if( m.isClone() && // Must be clonable
- (n.nOuts()>1 || // 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<LRG, Node> 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<maxlrg; i++ ) {
- BitSet ifg = IFG.atX(i);
- if( ifg != null ) {
- LRG lrg0 = alloc._LRGS[i];
- for( int lrg = ifg.nextSetBit(0); lrg>=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<maxlrg; i++ )
- if( alloc._LRGS[i] != null )
- nlrgs++;
-
- // Simplify
-
- // Walk all the LRGS looking for some trivial ones to start with.
- // During this pass all LRGS are broken into 3 disjoint sets:
- // - trivial, removed from IFG; color_stack[0 to sptr]
- // - trivial, not (yet) removed from IFG; color_stack[sptr to swork]
- // - unknown; color_stack[work to maxlrg]
-
- // Gather all not-unified (not-null); separate trivial and non-trivial set.
- int sptr = 0, swork = 0;
- LRG[] color_stack = new LRG[nlrgs];
- for( int i=0, j=0; i<maxlrg; i++ ) {
- LRG lrg = alloc._LRGS[i];
- if( lrg==null ) continue; // Unified lrgs are null here
- color_stack[j++] = lrg;
- if( lrg.lowDegree() ) swap(color_stack, swork++, j-1);
- }
-
- // Pull all lrgs from IFG, in trivial order if possible
- while( sptr < color_stack.length ) {
- // Swap best color up front
- pickColor(color_stack,sptr,swork);
-
- // Pick a trivial lrg, and (temporarily) remove from the IFG.
- LRG lrg = color_stack[sptr++];
- // If sptr was swork, then pulled an at-risk lrg
- if( sptr > 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<color_stack.length; i++ ) {
- if( bestScore == 1000000 ) return best; // Already max score
- int iScore = pickRiskyScore(color_stack[i]);
- if( iScore > 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
- * <p>
- * 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.
- * <p>
- * 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 -
- * (<a href="https://en.wikipedia.org/wiki/Disjoint-set_data_structure">this is the Union-Find algorithm</a>)
- * - 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<Node,String> _selfConflicts;
-
- // AND of all masks involved; null if none have been applied yet
- RegMask _mask;
-
- // Adjacent Live Range neighbors. Only valid during coloring
- Ary<LRG> _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<Node,XSched> 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<XSched> 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<use.nIns(); i++ )
- if( use.in(i)==n )
- rmask.and(mach.regmap(i));
- }
- // Defines in a single register
- _single |= rmask!=null && rmask.size1();
- }
-
- // If _bcnt==0, declare ready; move user bcnts into rcnts.
- boolean isReady() {
- if( _bcnt > 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<len; i++ )
- XSched.alloc(bb,bb.out(i));
-
- // Nodes are ready if they are not used by other nodes in this block.
- // Move ready up front.
- int ready = 0;
- for( int i=0; i<len; i++ )
- if( XSched.get(bb.out(i)).isReady() ) // No block-local inputs?
- bb._outputs.swap(ready++,i); // Move into ready set
- assert len==0 || ready > 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<Node> 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<ready; i++ ) {
- Node n = blk.at(i);
- int nscore = score(n);
- if( trace ) { System.out.printf("%4d N%d ",nscore,n._nid); System.out.println(n); }
- if( nscore > 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; i<n.nIns(); i++ ) {
- XSched xd = XSched.get( n.in(i) );
- if( xd != null && n.in(i).nOuts()==1 )
- CNT[xd._single || ((MachNode)n).regmap(i)!=null && ((MachNode)n).regmap(i).size1() ? 2 : 1]++;
- }
- score += 10 * Math.min( CNT[1], 2 );
- score += 100 * Math.min( CNT[2], 2 );
-
- // Nothing special
- assert 10 <= score && score <= 990;
- return score;
- }
-
- // Single use, and that use is not ready to fire once this one does. If
- // true, stalling 'n' might reduce 'n's lifetime. Stall by 1 (2 if single)
- // per not-ready.
-
- // If CFG user in this block, it must be last, so definitely stall
- // single-def (which stalls flags on x86,arm until the jmp).
- private static boolean singleUseNotReady( Node n, boolean single ) {
- // If n can rematerialize, assume allocator will split into private uses
- // as needed. No impact on local scheduling.
- if( n.nOuts()>1 && 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.
- * <p>
- * 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.
- * <p>
- * 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.
- * <p>
- * 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).
- * <p>
- * 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.
- * <p>
- * Liveness computation and interference graph built in the same one pass (one
- * fewer passes per round of coloring).
- * <p>
- * 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<LRG,String> _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<Node,LRG> _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<LRG> 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<len; reg++ )
- if( !_code._callerSave.test(reg) && ((1L<<reg)&neverSave)==0 ) {
- ret.addDef(new CalleeSaveNode(fun,reg,regs[reg]));
- assert ret.regmap(ret.nIns()-1).firstReg()==reg;
- }
- }
- // Cache reg masks for New and Call
- for( CFGNode bb : _code._cfg ) {
- if( bb instanceof CallEndMach cend ) cend.cacheRegs(_code);
- for( Node n : bb._outputs )
- if( n instanceof NewNode nnn ) nnn.cacheRegs(_code);
- }
-
- // Top driver: repeated rounds of coloring and splitting.
- byte round=0;
- while( !graphColor(round) ) {
- split(round);
- if( round >= 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<RegMask> 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<use.nIns(); i++ )
- if( use.in(i)==def )
- done = putIntoRegClass( rclass, mach.regmap(i) );
- }
- }
-
- // See how many register classes we split into. Generally not
- // productive to split like this across calls, which are going to kill
- // all registers anyways.
- if( rclass._len <= 1 || ncalls > 1 ) return false;
-
- // Split by class
- Ary<Node> 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<RegMask> rclass, RegMask rmask ) {
- for( int i=0; i<rclass._len; i++ ) {
- RegMask omask = rclass.at(i);
- if( omask.and(rmask) == omask ) return true; // Within the same register class
- if( omask.overlap(rmask) ) {
- rclass.set(i,new RegMask(omask.copy().and(rmask)));
- return false; // Need go again
- }
- }
- // Add a new class, no need to go again
- rclass.push(rmask);
- return true;
- }
-
- // Self conflicts require Phis (or two-address).
- // Insert a split after every def.
- boolean splitSelfConflict( byte round, LRG lrg ) {
- // Sort conflict set, so we're deterministic
- Node[] conflicts = lrg._selfConflicts.keySet().toArray(new Node[0]);
- Arrays.sort(conflicts, (x,y) -> x._nid - y._nid );
-
- // For all conflicts
- for( Node def : conflicts ) {
- assert lrg(def)==lrg; // Might be conflict use-side
- // Split before each use that extends the live range; i.e. is a
- // Phi or two-address
- for( int i=0; i<def._outputs._len; i++ ) {
- Node use = def.out(i);
- if( (use instanceof PhiNode phi &&
- !(phi.region() instanceof LoopNode loop && phi.in(2)==def && def.cfg0().idepth() > 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<n.nIns(); i++ )
- ld = ldepth(ld, phi.in(i), phi.region().cfg(i));
- } else {
- // Others check uses
- for( int i=1; i<n.nIns(); i++ )
- if( lrgSame(n.in(i),lrg) ) // This is a LRG use
- ld = ldepth(ld,n,n.cfg0());
- }
- }
- int min = (int)ld;
- int max = (int)(ld>>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<n.nIns(); i++ )
- // No split in front of a split
- if( !(n.in(i) instanceof SplitNode) &&
- // splitting in inner loop or at loop border
- (min==max || phi.region().cfg(i).loopDepth() <= min) &&
- // and not around the backedge of a loop (bad place to force a split, hard to remove)
- !(phi.region() instanceof LoopNode && i==2 && (phi.in(i) instanceof PhiNode pp && pp.region()==phi.region())) )
- // Split before phi-use in prior block
- insertBefore(phi,i, "use/loop/phi",round,lrg);
-
- } else {
- // Others check uses
- for( int i=1; i<n.nIns(); i++ ) {
- // This is a LRG use
- // splitting in inner loop or at loop border
- if( lrgSame( n.in( i ), lrg ) &&
- (min == max || (n.in(i) instanceof MachNode mach && mach.isClone()) || n.cfg0().loopDepth() <= min) )
- // Split before in this block
- insertBefore( n, i, "use/loop/use", round,lrg, false );
- }
- }
- }
- return true;
- }
-
- private boolean lrgSame(Node x, LRG lrg) {
- LRG xlrg = lrg(x);
- while( xlrg==null && x instanceof SplitNode )
- xlrg=lrg(x = x.in(1));
- return xlrg==lrg;
- }
-
- private static long ldepth( long ld, Node n, CFGNode cfg ) {
- // Do not count splits
- if( n instanceof SplitNode ) return ld;
- // Collect min/max loop depth
- int min = (int)ld;
- int max = (int)(ld>>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<Node> _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<bb.nOuts(); j++ ) {
- Node n = bb.out(j);
- if( lrg(n)!=null )
- maxReg = Math.max(maxReg,lrg(n)._reg+1);
- // Raise frame size by max stack args passed to New
- if( n instanceof NewNode nnn )
- maxReg = Math.max(maxReg,nnn._xslot);
-
- if( !(n instanceof SplitNode ) ) continue;
- int defreg = lrg(n )._reg;
- int usereg = lrg(n.in(1))._reg;
- // Attempt to bypass split
- if( defreg != usereg && splitBypass(bb,j,n,defreg) )
- usereg = lrg(n.in(1))._reg;
-
- // Split has same reg? Useless! Can remove it!
- if( defreg == usereg ) {
- n.removeSplit();
- j--;
- continue;
- }
- // Split is kept; count against split score
- _spills++;
- _spillScaled += (1<<bb.loopDepth()*3);
- assert _spillScaled >= 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.
- * <p>
- * 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<<bit;
- else _bits1 = 1L<<(bit-64);
- }
- public RegMask(long bits ) { _bits0 = bits; }
- public RegMask(long bits0, long bits1 ) { _bits0 = bits0; _bits1 = bits1; }
- public RegMask(RegMask r) { _bits0 = r._bits0; _bits1 = r._bits1; }
- private RegMask() { _bits0 = _bits1 = 0; }
-
- // AND, with copy-on-write if changing
- RegMask and( RegMask mask ) {
- if( mask==null ) return this;
- long bits0 = _bits0 & mask._bits0;
- long bits1 = _bits1 & mask._bits1;
- if( bits0==_bits0 && bits1==_bits1 ) return this;
- if( bits0==mask._bits0 && bits1==mask._bits1 ) return mask;
- if( bits0==0 && bits1==0 ) return EMPTY;
- // Update-in-place a mutable mask, or make a defensive copy
- return null;
- }
- RegMask sub( RegMask mask ) {
- if( mask==null ) return this;
- long bits0 = _bits0 & ~mask._bits0;
- long bits1 = _bits1 & ~mask._bits1;
- if( bits0==_bits0 && bits1==_bits1 ) return this;
- if( bits0==mask._bits0 && bits1==mask._bits1 ) return mask;
- if( bits0==0 && bits1==0 ) return EMPTY;
- // Update-in-place a mutable mask, or make a defensive copy
- return null;
- }
-
- // Fails if bit is set, because this is immutable
- public boolean clr( int reg ) {
- if( reg < 64 ) return ((_bits0 >> 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<regs.length ? regs[i] : "[stk#"+i+"]";
- }
-}
diff --git a/src/main/java/com/seaofnodes/simple/codegen/RegMaskRW.java b/src/main/java/com/seaofnodes/simple/codegen/RegMaskRW.java
deleted file mode 100644
index 1da9a3b1..00000000
--- a/src/main/java/com/seaofnodes/simple/codegen/RegMaskRW.java
+++ /dev/null
@@ -1,36 +0,0 @@
-package com.seaofnodes.simple.codegen;
-
-// Mutable regmask(writable)
-public class RegMaskRW extends RegMask {
- public RegMaskRW(long x, long y) { super(x,y); }
- // clears bit at position r. Returns true if the mask is still not empty.
- public boolean clr(int r) {
- if( r < 64 ) _bits0 &= ~(1L<<(r ));
- else _bits1 &= ~(1L<<(r-64));
- return _bits0!=0 || _bits1!=0;
- }
- // sets bit at position r.
- public void set(int r) {
- if( r < 64 ) _bits0 |= (1L<<(r ));
- else _bits1 |= (1L<<(r-64));
- }
- @Override RegMaskRW and( RegMask mask ) {
- if( mask==null ) return this;
- _bits0 &= mask._bits0;
- _bits1 &= mask._bits1;
- return this;
- }
- @Override RegMaskRW sub( RegMask mask ) {
- if( mask==null ) return this;
- _bits0 &= ~mask._bits0;
- _bits1 &= ~mask._bits1;
- return this;
- }
- public RegMaskRW or( RegMask mask ) {
- if( mask!=null ) {
- _bits0 |= mask._bits0;
- _bits1 |= mask._bits1;
- }
- return this;
- }
-}
diff --git a/src/main/java/com/seaofnodes/simple/node/AddFNode.java b/src/main/java/com/seaofnodes/simple/node/AddFNode.java
deleted file mode 100644
index 792da62e..00000000
--- a/src/main/java/com/seaofnodes/simple/node/AddFNode.java
+++ /dev/null
@@ -1,44 +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 AddFNode extends Node {
- public AddFNode(Node lhs, Node rhs) { super(null, lhs, rhs); }
-
- @Override public String label() { return "AddF"; }
-
- @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() {
- Node lhs = in(1);
- Type t2 = in(2)._type;
-
- // Add of 0.
- if ( t2.isConstant() && t2 instanceof TypeFloat i && i.value()==0 )
- return lhs;
-
- return null;
- }
- @Override Node copy(Node lhs, Node rhs) { return new AddFNode(lhs,rhs); }
-}
diff --git a/src/main/java/com/seaofnodes/simple/node/AddNode.java b/src/main/java/com/seaofnodes/simple/node/AddNode.java
deleted file mode 100644
index f1615f70..00000000
--- a/src/main/java/com/seaofnodes/simple/node/AddNode.java
+++ /dev/null
@@ -1,169 +0,0 @@
-package com.seaofnodes.simple.node;
-
-import com.seaofnodes.simple.type.*;
-
-import static com.seaofnodes.simple.Parser.con;
-
-public class AddNode extends ArithNode {
- public AddNode(Node lhs, Node rhs) { super(null, lhs, rhs); }
-
- @Override public String label() { return "Add"; }
- @Override public String op() { return "+"; }
-
- @Override long doOp( long x, long y ) { return x + y; }
- @Override TypeInteger doOp(TypeInteger x, TypeInteger y) {
- // Fold ranges like {0-1} + {2-3} into {2-4}.
- if( !overflow(x._min,y._min) &&
- !overflow(x._max,y._max) )
- return TypeInteger.make(x._min+y._min,x._max+y._max);
- return TypeInteger.BOT;
- }
-
- @Override
- public Node idealize () {
- Node lhs = in(1);
- Node rhs = in(2);
- if( rhs instanceof AddNode add && add.err()!=null )
- return null;
- Type t2 = rhs._type;
-
- // Add of 0. We do not check for (0+x) because this will already
- // canonicalize to (x+0)
- if( t2 == TypeInteger.ZERO )
- return lhs;
-
- // Add of same to a multiply by 2
- if( lhs==rhs )
- return new ShlNode(null,lhs,con(1));
-
- // Goal: a left-spine set of adds, with constants on the rhs (which then fold).
-
- // Move non-adds to RHS
- if( !(lhs instanceof AddNode) && rhs instanceof AddNode )
- return swap12();
-
- // x+(-y) becomes x-y
- if( rhs instanceof MinusNode minus )
- return new SubNode(lhs,minus.in(1));
-
- // Now we might see (add add non) or (add non non) or (add add add) but never (add non add)
-
- // Do we have x + (y + z) ?
- // Swap to (x + y) + z
- // Rotate (add add add) to remove the add on RHS
- if( rhs instanceof AddNode add )
- return new AddNode(new AddNode(lhs,add.in(1)).peephole(), add.in(2));
-
- // Now we might see (add add non) or (add non non) but never (add non add) nor (add add add)
- if( !(lhs instanceof AddNode) )
- // Rotate; look for (add (phi cons) con/(phi cons))
- return spine_cmp(lhs,rhs,this) ? swap12() : phiCon(this,true);
-
- // Now we only see (add add non)
-
- // Dead data cycle; comes about from dead infinite loops. Do nothing,
- // the loop will peep as dead after a bit.
- if( lhs.in(1) == lhs )
- return null;
-
- // Do we have (x + con1) + con2?
- // Replace with (x + (con1+con2) which then fold the constants
- // lhs.in(2) is con1 here
- // If lhs.in(2) is not a constant, we add ourselves as a dependency
- // because if it later became a constant then we could make this
- // transformation.
- if( addDep(lhs.in(2))._type.isConstant() && rhs._type.isConstant() )
- return new AddNode(lhs.in(1),new AddNode(lhs.in(2),rhs).peephole());
-
-
- // 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 = phiCon(this,true);
- if( phicon!=null ) return phicon;
-
- // Now we sort along the spine via rotates, to gather similar things together.
-
- // Do we rotate (x + y) + z
- // into (x + z) + y ?
- if( spine_cmp(lhs.in(2),rhs,this) )
- return new AddNode(new AddNode(lhs.in(1),rhs).peephole(),lhs.in(2));
-
- return super.idealize();
- }
-
- // Rotation is only valid for associative ops, e.g. Add, Mul, And, Or, Xor.
- // Do we have ((phi cons)|(x + (phi cons)) + con|(phi cons)) ?
- // Push constant up through the phi: x + (phi con0+con0 con1+con1...)
- static Node phiCon(Node op, boolean rotate) {
- Node lhs = op.in(1);
- Node rhs = op.in(2);
- if( rhs._type==TypeInteger.TOP ) return null;
- // LHS is either a Phi of constants, or another op with Phi of constants
- PhiNode lphi = pcon(lhs,op);
- if( rotate && lphi==null && lhs.nIns() > 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<ns.length; i++ )
- ns[i] = op.copy(lphi.in(i), rhs instanceof PhiNode ? rhs.in(i) : rhs).peephole();
- String label = lphi._label + (rhs instanceof PhiNode rphi ? rphi._label : "");
- Node phi = new PhiNode(label,lphi._minType,ns).peephole();
- // Rotate needs another op, otherwise just the phi
- return lhs==lphi ? phi : op.copy(lhs.in(1),phi);
- }
-
- /**
- * Tests if the op is a phi and has all constant inputs.
- * If not, returns null.
- * If op is a phi, but its inputs are not all constants, then dep is added as
- * a dependency to the phi's non-const input, because if later the input turn into a constant
- * dep can make progress.
- */
- static PhiNode pcon(Node op, Node dep) {
- return op instanceof PhiNode phi && phi.allCons(dep) ? phi : null;
- }
-
- // Compare two off-spine nodes and decide what order they should be in.
- // Do we rotate ((x + hi) + lo) into ((x + lo) + hi) ?
- // Generally constants always go right, then Phi-of-constants, then muls, then others.
- // Ties with in a category sort by node ID.
- // TRUE if swapping hi and lo.
- static boolean spine_cmp( Node hi, Node lo, Node dep ) {
- if( lo._type.isConstant() || lo instanceof ConFldOffNode ) return false;
- if( hi._type.isConstant() || hi instanceof ConFldOffNode ) return true ;
-
- if( lo instanceof PhiNode lphi && lphi.region()._type==Type.XCONTROL ) return false;
- if( hi instanceof PhiNode hphi && hphi.region()._type==Type.XCONTROL ) return false;
-
- if( lo instanceof PhiNode && lo.allCons(dep) ) return false;
- if( hi instanceof PhiNode && hi.allCons(dep) ) return true ;
-
- if( lo instanceof PhiNode && !(hi instanceof PhiNode) ) return true;
- if( hi instanceof PhiNode && !(lo instanceof PhiNode) ) return false;
-
- // Same category of "others"
- return lo._nid > 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<lhs.nIns(); i++ )
- if( isCon(lhs,rhs,i) )
- cnt++;
- if( lhs.nIns()-1 - cnt <= 1 ) {
- // Profit! Push up-phi and fold
- Node[] ns = new Node[lhs.nIns()];
- ns[0] = lhs.in(0);
- for( int i=1; i<lhs.nIns(); i++ ) {
- ns[i] = isCon(lhs,rhs,i)
- ? new ConstantNode(con(lhs.in(i)._type, rhs.in(i)._type)).peephole()
- : copy(lhs.in(i), rhs.in(i)).peephole();
- }
- String label = lhs._label==rhs._label ? lhs._label : lhs._label + rhs._label;
- return new PhiNode(label,lhs._minType,ns).peephole();
- }
- }
- return null;
- }
-
- static boolean isCon( PhiNode lhs, PhiNode rhs, int i ) {
- return lhs.in(i)._type.isConstant() && rhs.in(i)._type.isConstant();
- }
-
-
- static boolean overflow( long x, long y ) {
- if( (x ^ y ) < 0 ) return false; // unequal signs, never overflow
- return (x ^ (x + y)) < 0; // sum has unequal signs, so overflow
- }
-
- @Override public Parser.ParseException err() {
- if( in(1)._type.isHigh() || in(2)._type.isHigh() ) return null;
- if( !(in(1)._type instanceof TypeInteger) ) return Parser.error("Cannot '"+op()+"' " + in(1)._type,_loc);
- if( !(in(2)._type instanceof TypeInteger) ) return Parser.error("Cannot '"+op()+"' " + in(2)._type,_loc);
- return null;
- }
-}
diff --git a/src/main/java/com/seaofnodes/simple/node/BoolNode.java b/src/main/java/com/seaofnodes/simple/node/BoolNode.java
deleted file mode 100644
index 2765c308..00000000
--- a/src/main/java/com/seaofnodes/simple/node/BoolNode.java
+++ /dev/null
@@ -1,164 +0,0 @@
-package com.seaofnodes.simple.node;
-
-import com.seaofnodes.simple.Parser;
-import com.seaofnodes.simple.type.*;
-import com.seaofnodes.simple.util.Utils;
-import java.util.BitSet;
-import static com.seaofnodes.simple.type.TypeInteger.*;
-
-abstract public class BoolNode extends Node {
-
- public BoolNode(Node lhs, Node rhs) {
- super(null, lhs, rhs);
- }
-
- abstract public String op(); // String opcode name
-
- @Override
- public String label() { return getClass().getSimpleName(); }
-
- @Override
- public String glabel() { return 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(")");
- }
-
- @Override
- public TypeInteger compute() {
- Type t1 = in(1)._type;
- Type t2 = in(2)._type;
- // Exactly equals?
- if( t1.isHigh() || t2.isHigh() )
- return (TypeInteger)BOOL.dual();
- if( in(1)==in(2) )
- // LT fails, both EQ and LE succeed
- return this instanceof LT ? FALSE : TRUE;
- if( t1 instanceof TypeInteger i1 &&
- t2 instanceof TypeInteger i2 )
- return doOp(i1,i2);
- if( t1 instanceof TypeFloat f1 &&
- t2 instanceof TypeFloat f2 &&
- f1.isConstant() && f2.isConstant() )
- return doOp(f1.value(), f2.value()) ? TRUE : FALSE;
- return BOOL;
- }
-
- TypeInteger doOp(TypeInteger t1, TypeInteger t2) { throw Utils.TODO(); }
- boolean doOp(double lhs, double rhs) { throw Utils.TODO(); }
- Node copyF(Node lhs, Node rhs) { return null; }
- public boolean isFloat() { return false; }
-
- @Override
- public Node idealize() {
- // Compare of same
- if( in(1)==in(2) )
- return this instanceof LT ? Parser.ZERO : new ConstantNode(TRUE);
-
- // Equals pushes constant to the right; 5==X becomes X==5.
- if( this instanceof EQ ) {
- if( !(in(2) instanceof ConstantNode) ) {
- // con==noncon becomes noncon==con
- if( in(1) instanceof ConstantNode || in(1)._nid > 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 "&lt;"; }
- 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 "&lt;="; }
- 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&lt;"; }
- 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
- * <p>
- * CFG nodes have a immediate dominator depth (idepth) and a loop nesting
- * depth(loop_depth).
- * <p>
- * idepth is computed lazily upon first request, and is valid even in the
- * Parser, and is used by peepholes during parsing and afterward.
- * <p>
- * 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.
- * <p>
- * See {@link <a href="https://en.wikipedia.org/wiki/Dominator_(graph_theory)">...</a>}
- */
- 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; i<xslot; i++ )
- kills.set((maxReg+fslot)+i);
- _kills = kills;
- }
- public String op() { return "cend"; }
- public RegMask regmap(int i) { return null; }
- public RegMask outregmap() { return null; }
- public RegMask outregmap(int idx) { return idx==2 ? _retMask : null; }
- public RegMask killmap() { return _kills; }
- public void encoding( Encoding enc ) { }
- public void asm(CodeGen code, SB sb) { }
-}
diff --git a/src/main/java/com/seaofnodes/simple/node/CallEndNode.java b/src/main/java/com/seaofnodes/simple/node/CallEndNode.java
deleted file mode 100644
index b75852a4..00000000
--- a/src/main/java/com/seaofnodes/simple/node/CallEndNode.java
+++ /dev/null
@@ -1,108 +0,0 @@
-package com.seaofnodes.simple.node;
-
-import com.seaofnodes.simple.*;
-import com.seaofnodes.simple.codegen.*;
-import com.seaofnodes.simple.type.*;
-import java.util.BitSet;
-
-/**
- * CallEnd
- */
-public class CallEndNode extends CFGNode implements MultiNode {
-
- // When set true, this Call/CallEnd/Fun/Return is being trivially inlined
- private boolean _folding;
- public final TypeRPC _rpc;
-
- public CallEndNode(CallNode call) { super(new Node[]{call}); _rpc = TypeRPC.constant(_nid); }
- public CallEndNode(CallEndNode cend) { super(cend); _rpc = cend._rpc; }
-
- @Override public String label() { return "CallEnd"; }
- @Override public boolean blockHead() { return true; }
-
- public CallNode call() { return (CallNode)in(0); }
-
- @Override public CFGNode idom(Node dep) {
- // Folding the idom is the one inlining Return
- return _folding ? cfg(1) : super.idom(dep);
- }
-
- @Override
- public StringBuilder _print1(StringBuilder sb, BitSet visited) {
- sb.append("cend( ");
- sb.append( in(0) instanceof CallNode ? "Call, " : "----, ");
- for( int i=1; i<nIns()-1; i++ )
- in(i)._print0(sb,visited).append(",");
- sb.setLength(sb.length()-1);
- return sb.append(")");
- }
-
- @Override
- public Type compute() {
- if( !(in(0) instanceof CallNode call) )
- return TypeTuple.RET.dual();
- Type ret = Type.BOTTOM;
- if( addDep(call.fptr())._type instanceof TypeFunPtr tfp ) {
- ret = tfp.ret();
- // Here, if I can figure out I've found *all* callers, then I can meet
- // across the linked returns and join with the function return type.
- if( tfp.isConstant() && nIns()>1 ) {
- assert nIns()==2; // Linked exactly once for a constant
- ret = ((TypeTuple)in(1)._type).ret(); // Return type
- }
- }
- return TypeTuple.make(call._type,TypeMem.BOT,ret);
- }
-
- @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; i<nIns()-1; i++ )
- in(i)._print0(sb,visited).append(",");
- sb.setLength(sb.length()-1);
- return sb.append(")");
- }
- public String name() {
- if( fptr()._type instanceof TypeFunPtr tfp && tfp.isConstant() ) {
- FunNode fun = CodeGen.CODE.link(tfp);
- if( fun !=null ) return fun._name;
- if( fptr() instanceof ExternNode ex ) return ex._extern;
- }
- return null;
- }
-
-
-
- Node ctrl() { return in(0); }
- Node mem () { return in(1); }
- // Args mapped 1-to-1 on inputs, so conceptually start at 2
- public Node arg(int idx) { return in(idx); }
- // Same arg accounting as TFPs, although the numbering starts at 2
- public int nargs() { return nIns()-3; } // Minus control, memory, fptr
- // args from input 2 to last; last is function input
- public Node fptr() { return _inputs.last(); }
- // Error if not a TFP
- public TypeFunPtr tfp() { return (TypeFunPtr)fptr()._type; }
-
- // Call is to an externally supplied code
- public boolean external() { return false; }
-
-
- // Find the Call End from the Call
- public CallEndNode cend() {
- // Always in use slot 0
- if( nOuts()>0 && 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<tfp.nargs(); i++ )
- if( !arg(i+2)._type.isa(tfp.arg(i)) ) {
- cend().addDep(arg(i+2)); // Upgrading arg type may allow inlining
- return Parser.error( "Argument #" + i + " isa " + arg(i+2)._type + ", but must be a " + tfp.arg(i), _loc );
- }
-
- if( tfp.fidxs() < 0 )
- throw Utils.TODO(); // Infinite unknown TFPs? Should be fairly precise CG
-
- return null;
- }
-
-}
diff --git a/src/main/java/com/seaofnodes/simple/node/CalleeSaveNode.java b/src/main/java/com/seaofnodes/simple/node/CalleeSaveNode.java
deleted file mode 100644
index 0afc5827..00000000
--- a/src/main/java/com/seaofnodes/simple/node/CalleeSaveNode.java
+++ /dev/null
@@ -1,23 +0,0 @@
-package com.seaofnodes.simple.node;
-
-import com.seaofnodes.simple.codegen.Encoding;
-import com.seaofnodes.simple.codegen.RegMask;
-import com.seaofnodes.simple.type.Type;
-
-public class CalleeSaveNode extends ProjNode implements MachNode {
- final RegMask _mask;
- public CalleeSaveNode(FunNode fun, int reg, String label) {
- super(fun,reg,label);
- fun._outputs.pop();
- int i=0;
- while( fun.out(i) instanceof PhiNode ) i++;
- fun._outputs.insert(this,i);
- _mask = new RegMask(reg);
- }
- @Override public RegMask regmap(int i) { return null; }
- @Override public RegMask outregmap() { return _mask; }
- @Override public void encoding( Encoding enc ) { }
-
- @Override public Type compute() { return Type.BOTTOM; }
- @Override public Node idealize() { return null; }
-}
diff --git a/src/main/java/com/seaofnodes/simple/node/CastMach.java b/src/main/java/com/seaofnodes/simple/node/CastMach.java
deleted file mode 100644
index e45898f5..00000000
--- a/src/main/java/com/seaofnodes/simple/node/CastMach.java
+++ /dev/null
@@ -1,19 +0,0 @@
-package com.seaofnodes.simple.node;
-
-import com.seaofnodes.simple.codegen.CodeGen;
-import com.seaofnodes.simple.codegen.Encoding;
-import com.seaofnodes.simple.codegen.RegMask;
-import com.seaofnodes.simple.util.SB;
-
-/**
- * Cast a pointer to read-only
- */
-public class CastMach extends CastNode implements MachNode {
- public CastMach( CastNode n ) { super(n); }
- @Override public String op() { return label(); }
- @Override public RegMask regmap(int i) { assert i==1; return RegMask.FULL; }
- @Override public RegMask outregmap() { return RegMask.FULL; }
- @Override public int twoAddress( ) { return 1; }
- @Override public void encoding( Encoding enc ) { }
- @Override public void asm(CodeGen code, SB sb) { sb.p(code.reg(in(1))); }
-}
diff --git a/src/main/java/com/seaofnodes/simple/node/CastNode.java b/src/main/java/com/seaofnodes/simple/node/CastNode.java
deleted file mode 100644
index 6dc8e9fe..00000000
--- a/src/main/java/com/seaofnodes/simple/node/CastNode.java
+++ /dev/null
@@ -1,74 +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 com.seaofnodes.simple.type.TypeMemPtr;
-import com.seaofnodes.simple.util.Utils;
-import java.util.BitSet;
-
-// Upcast (join) the input to a t. Used after guard test to lift an input.
-// Can also be used to make a type-assertion if ctrl is null.
-public class CastNode extends Node {
- public Type _t;
- public CastNode(Type t, Node ctrl, Node in) {
- super(ctrl, in);
- _t = t;
- setType(compute());
- }
-
- public CastNode(CastNode c) {
- super(c.in(0), c.in(1)); // Call parent constructor
- this._t = c._t; // Copy the Type field
- setType(compute()); // Ensure type is recomputed
- }
-
- @Override public String label() { return "("+_t.str()+")"; }
-
- @Override
- public String uniqueName() { return "Cast_" + _nid; }
-
- @Override public boolean isPinned() { return true; }
- @Override public boolean isConst() { return true; }
-
- @Override
- public StringBuilder _print1(StringBuilder sb, BitSet visited) {
- return in(1)._print0(sb.append(label()), visited);
- }
-
- @Override
- public Type compute() {
- // Cast array to int
- Type t1 = in(1)._type;
- if( _t == TypeInteger.BOT && t1 instanceof TypeMemPtr tmp && tmp._obj.isAry() )
- return _t;
- // Freeze if the join is high but both inputs are low
- Type tj = t1.join(_t);
- if( tj.isHigh() && !t1.isHigh() )
- return _type==null ? _t : _type;
-
- return tj;
- }
-
- @Override
- public Node idealize() {
- return in(1)._type.isa(_t) ? in(1) : null;
- }
-
- @Override
- public boolean eq(Node n) {
- CastNode cast = (CastNode)n; // Contract
- return _t==cast._t;
- }
-
- @Override
- int hash() { return _t.hashCode(); }
-
- @Override
- public Parser.ParseException err() {
- // Has a condition to test, so OK
- if( in(0) != null ) return null;
- // No condition to test, so this must optimize away
- return Parser.error( "Type " + in(1)._type.str() + " is not of declared type " + _t.str(), null );
- }
-}
diff --git a/src/main/java/com/seaofnodes/simple/node/ConFldOffNode.java b/src/main/java/com/seaofnodes/simple/node/ConFldOffNode.java
deleted file mode 100644
index 3e1d3c69..00000000
--- a/src/main/java/com/seaofnodes/simple/node/ConFldOffNode.java
+++ /dev/null
@@ -1,43 +0,0 @@
-package com.seaofnodes.simple.node;
-
-import com.seaofnodes.simple.codegen.CodeGen;
-import com.seaofnodes.simple.type.Type;
-import com.seaofnodes.simple.type.TypeInteger;
-import com.seaofnodes.simple.type.TypeStruct;
-import com.seaofnodes.simple.util.SB;
-import java.util.BitSet;
-
-/**
- * A field offset. This becomes a constant *after* other optimizations which
- * determine field sizes - some fields might be dead, or hold only zeros or
- * small values.
- */
-
-public class ConFldOffNode extends ConstantNode {
- public final String _name; // Struct name
- public final String _fname; // Field name
- public ConFldOffNode( String name, String fname ) { super(TypeInteger.BOT); _name = name; _fname = fname; }
-
- @Override public String label() {
- return _fname == " len" ? "sizeof("+_name+")" : "#"+_fname;
- }
- @Override public String glabel() { return label(); }
- @Override public String uniqueName() { return "Off_" + _nid; }
-
- @Override
- public StringBuilder _print1(StringBuilder sb, BitSet visited) {
- return sb.append(label());
- }
-
- // Convert field offset to an integer
- public Node asOffset(TypeStruct ts) {
- return new ConstantNode(TypeInteger.constant(ts.offset(_fname==" len" ? ts._fields.length : ts.find(_fname)))).peephole();
- }
-
-
- @Override public boolean eq(Node n) {
- ConFldOffNode off = (ConFldOffNode)n; // Invariant
- return _name==off._name && _fname==off._fname && super.eq(n);
- }
- @Override int hash() { return super.hash() * _name.hashCode() * _fname.hashCode(); }
-}
diff --git a/src/main/java/com/seaofnodes/simple/node/ConstantNode.java b/src/main/java/com/seaofnodes/simple/node/ConstantNode.java
deleted file mode 100644
index 22bf03c4..00000000
--- a/src/main/java/com/seaofnodes/simple/node/ConstantNode.java
+++ /dev/null
@@ -1,52 +0,0 @@
-package com.seaofnodes.simple.node;
-
-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 node represents a constant value.
- * <p>
- * Constants have no semantic inputs. However, we set Start as an input to
- * Constants to enable a forward graph walk. This edge carries no semantic
- * meaning, and it is present <em>solely</em> to allow visitation.
- * <p>
- * 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<n.nIns(); i++ )
- walkUp(n.cfg(i),cfgs);
- else walkUp(n.cfg0(),cfgs);
- }
-
- private static boolean walkDown( Node n, BitSet cfgs, BitSet body, BitSet visit ) {
- if( visit.get(n._nid) ) return body.get(n._nid);
- visit.set(n._nid);
-
- if( n instanceof CFGNode && !cfgs.get(n._nid) )
- return false;
- if( n.in(0)!=null && !cfgs.get(n.in(0)._nid) )
- return false;
- boolean in = n.in(0)!=null || n instanceof CFGNode;
- for( Node use : n._outputs )
- in |= walkDown(use,cfgs,body,visit);
- if( in ) body.set(n._nid);
- return in;
- }
-
-
- // ------------
- // MachNode specifics, shared across all CPUs
-
- // Function may call other functions? X86 requires 16b aligned SP on
- // outbound calls, but not leaf. Set during an unrelated RA scan.
- public boolean _hasCalls;
-
- // Maximum incoming argument slot, based on function type
- public int _maxArgSlot;
-
- // Frame adjust, including padding, set during early Encoding
- public int _frameAdjust;
-
- public String op() { return _frameAdjust==0 ? "entry" : "subi"; }
- public void postSelect(CodeGen code) {
- code.link(this);
- // Max slot seen past space reserved for incoming arguments
- _maxArgSlot = code._mach.maxArgSlot(sig());
- }
- public RegMask regmap(int i) { return null; }
- public RegMask outregmap() { return null; }
- public void encoding( Encoding enc ) { throw Utils.TODO(); }
- public void asm(CodeGen code, SB sb) {
- if( _frameAdjust!=0 )
- sb.p("rsp -= #").p(_frameAdjust);
- }
-
- public void computeFrameAdjust(CodeGen code, int maxReg) {
- // Max slot seen, or 0.
- int maxSlot = Math.max(maxReg - code._mach.regs().length,_maxArgSlot);
- // Frame adjust before rounding, can be negative for no spills
- int size = maxSlot - _maxArgSlot;
- _frameAdjust = size*8;
- }
-
- // Given a high register number and during encoding compute the stack
- // pointer offset. Pre-RA we lack the fun._frameAdjust. We also need a
- // FunNode.
- public int computeStackOffset(CodeGen code, int reg) {
- String[] regs = code._mach.regs();
- int stackSlot = reg - regs.length;
- int slotRotate = stackSlot < _maxArgSlot
- ? stackSlot + (_frameAdjust>>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(); i++ )
- (in(i)==null ? sb.append(" ---") : in(i)._print0(sb, visited)).append(",");
- sb.setLength(sb.length()-1);
- return sb.append(")");
- }
-
-}
diff --git a/src/main/java/com/seaofnodes/simple/node/MachNode.java b/src/main/java/com/seaofnodes/simple/node/MachNode.java
deleted file mode 100644
index c0b3af66..00000000
--- a/src/main/java/com/seaofnodes/simple/node/MachNode.java
+++ /dev/null
@@ -1,60 +0,0 @@
-package com.seaofnodes.simple.node;
-
-import com.seaofnodes.simple.*;
-import com.seaofnodes.simple.codegen.*;
-import com.seaofnodes.simple.util.SB;
-import com.seaofnodes.simple.util.Utils;
-
-public interface MachNode {
-
- // Easy access to the abstract Machine
- default Machine machine() { return CodeGen.CODE._mach; }
-
- // Run a post-instruction-selection action
- default void postSelect( CodeGen code ) { }
-
- // Register mask allowed on input i. 0 for no register.
- RegMask regmap( int i );
- // Register mask allowed as a result. 0 for no register.
- RegMask outregmap();
- // Multi-reg-defining machine op; idx comes from Proj.
- // Sometimes these Projs have no uses, and just exist to kill a register.
- default RegMask outregmap(int idx) { throw Utils.TODO(); }
-
- // Set of killed registers
- default RegMask killmap() { return null; }
-
- // If this op updates the same register as input 'idx', e.g. many X86 "R1 +=
- // R2" style ops. This forces the output live range to equal the input live
- // range, so they both get the same register.
-
- // This can hold true for projections, if the instruction updates many
- // registers that it also takes in. Note that ops also use the same
- // registers for both inputs and outputs but these are pinned and without any
- // choices (e.g. always RCX for both an input and output) and are better
- // served with just declaring as a fixed register.
-
- // Returns either 0 for not-two-adr or the updated input; for projections
- // this is their Multi's updated input.
- default int twoAddress( ) { return 0; }
- // Ok to switch arguments
- default boolean commutes() { return false; }
-
- // Instructions cheaper to recreate than to spill, such as loading small constants
- default boolean isClone() { return false; }
-
- // Make a clone of a cheap instruction
- default Node copy() { return null; }
-
- // Encoding is appended, relocations gathered.
- void encoding( Encoding enc );
-
- // Human-readable form appended to the SB. Things like the encoding,
- // indentation, leading address or block labels not printed here.
- // Just something like "R17=[R18+12] // Load array base".
- // General form: "dst=src+src".
- default void asm(CodeGen code, SB sb) { throw Utils.TODO(); }
-
- // Human readable opcode; e.g. "ld4" or "call".
- default String op() { throw Utils.TODO(); }
-}
diff --git a/src/main/java/com/seaofnodes/simple/node/MemMergeNode.java b/src/main/java/com/seaofnodes/simple/node/MemMergeNode.java
deleted file mode 100644
index 19acc5b9..00000000
--- a/src/main/java/com/seaofnodes/simple/node/MemMergeNode.java
+++ /dev/null
@@ -1,180 +0,0 @@
-package com.seaofnodes.simple.node;
-
-import com.seaofnodes.simple.*;
-import com.seaofnodes.simple.codegen.CodeGen;
-import com.seaofnodes.simple.type.*;
-import java.util.*;
-
-/**
- * Memory Merge - a merge of many aliases into a "fat memory". All aliases
- * are here, but most will be lazy - take the default fat memory.
- */
-public class MemMergeNode extends Node {
-
- /*
- * In-Progress means this is being used by the Parser to track memory
- * aliases. No optimizations are allowed. When no longer "in progress"
- * normal peeps work.
- */
- public final boolean _inProgress;
-
- public MemMergeNode( boolean inProgress) { _type = TypeMem.BOT; _inProgress = inProgress; }
- public MemMergeNode(MemMergeNode mem) { super(mem); _inProgress = false; }
-
-
- // If being used by a Scope, this is "in progress" from the Parser.
- // Otherwise, it's a memory merge
- boolean inProgress() { return _inProgress; }
-
- @Override public String label() { return "ALLMEM"; }
- @Override public boolean isMem() { return true; }
-
- @Override
- public StringBuilder _print1(StringBuilder sb, BitSet visited) {
- sb.append(_inProgress ? "Merge[" : "MEM[ ");
- for( int j=2; j<nIns(); j++ ) {
- sb.append(j);
- sb.append(":");
- Node n = in(j);
- while( n instanceof ScopeNode loop ) {
- sb.append("Lazy_");
- n = loop.mem(j);
- }
- if( n==null ) sb.append("___ ");
- else n._print0(sb, visited).append(" ");
- }
- sb.setLength(sb.length()-1);
- return sb.append("]");
- }
-
- // Make a memory merge: no longer a Scope really, tracking memory state but
- // not related to the parser in any way.
- public Node merge() {
- // Force default memory to not be lazy
- MemMergeNode merge = new MemMergeNode(false);
- for( Node n : _inputs )
- merge.addDef(n);
- for( int i=1; i<nIns(); i++ )
- merge._mem(i,null);
- return merge.peephole();
- }
-
-
- @Override public Type compute() { return TypeMem.BOT; }
-
- @Override public Node idealize() {
- if( inProgress() ) return null;
-
- // Fold defaults into the default
- boolean progress=false, allDefault=true;
- for( int i=2; i<nIns(); i++ ) {
- if( in(i) instanceof CastNode cast )
- { setDef(i,cast.in(1)); progress=true; }
- if( in(1) == in(i) ) { setDef(i,null); progress=true; }
- else { allDefault=false; }
- }
-
- // If not merging any memory (all memory is just the default)
- if( allDefault )
- return in(1); // Become default memory
-
- // Collapse stacked all-mem
- if( in(1) instanceof MemMergeNode mem ) {
- // Goal is to swap my default mem with mem's default mem
- for( int i=2; i<mem.nIns(); i++ ) {
- if( mem.in(i) != null ) {
- // deeper default mem has a non-default
- if( 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<nIns(); i++ ) {
- if( in(i) instanceof PhiNode phi && phi.region()==scope.ctrl() ) {
- assert phi.in(2)==null;
- phi.setDef(2,back.alias(i)==scope ? phi : back.alias(i)); // Fill backedge
- }
- if( exit_def == scope ) // Replace a lazy-phi on the exit path also
- exit.alias(i,in(i));
- }
- }
-
- // Now one-time do a useless-phi removal
- void _useless( ) {
- for( int i=2; i<nIns(); i++ ) {
- if( in(i) instanceof PhiNode phi ) {
- // Do an eager useless-phi removal
- Node in = phi.peephole();
- CodeGen.CODE.addAll(phi._outputs);
- phi.moveDepsToWorklist();
- if( in != phi ) {
- if( !phi.iskeep() ) // Keeping phi around for parser elsewhere
- phi.subsume(in);
- setDef(i,in); // Set the update back into Scope
- }
- }
- }
- }
-
- @Override public boolean eq( Node n ) {
- return this==n || !_inProgress;
- }
-}
diff --git a/src/main/java/com/seaofnodes/simple/node/MemOpNode.java b/src/main/java/com/seaofnodes/simple/node/MemOpNode.java
deleted file mode 100644
index ead82322..00000000
--- a/src/main/java/com/seaofnodes/simple/node/MemOpNode.java
+++ /dev/null
@@ -1,109 +0,0 @@
-package com.seaofnodes.simple.node;
-
-import com.seaofnodes.simple.Parser;
-import com.seaofnodes.simple.type.Type;
-import com.seaofnodes.simple.type.TypeMemPtr;
-import com.seaofnodes.simple.util.Utils;
-import java.lang.StringBuilder;
-import java.util.BitSet;
-
-/**
- * Convenience common base for Load and Store.
- *
- * 0 - Control
- * 1 - Memory
- * 2 - Base of object, OOP
- * 3 - Offset, integer types
- * 4 - Value, for Stores only
- */
-public abstract class MemOpNode extends Node {
-
- // The equivalence alias class
- public final int _alias;
-
- // True if load-like, false if store-like.
- //
- // Stores produce memory (maybe as part of a tuple with other things),
- // loads do not.
- //
- // Loads might pick up anti-dependences on prior Stores, and never cause an
- // anti-dependence themselves.
- //
- // Stores must maximally sink to the least dominator use. Loads can be
- // opportunistically hoisted.
- public final boolean _isLoad;
-
- // Declared type; not final because it might be a forward-reference
- // which will be lazily improved when the reference is declared.
- public Type _declaredType;
-
- // A debug name, no semantic meaning
- public final String _name;
- // Source location for late reported errors
- public final Parser.Lexer _loc;
-
- public MemOpNode(Parser.Lexer loc, String name, int alias, boolean isLoad, Type glb, Node mem, Node ptr, Node off) {
- super(null, mem, ptr, off);
- _name = name;
- _alias = alias;
- _declaredType = glb;
- _loc = loc;
- _isLoad = isLoad;
- }
- public MemOpNode(Parser.Lexer loc, String name, int alias, boolean isLoad, Type glb, Node mem, Node ptr, Node off, Node value) {
- this(loc,name, alias, isLoad, glb, mem, ptr, off);
- addDef(value);
- }
- public MemOpNode( Node mach, MemOpNode mop ) {
- super(mach);
- _name = mop==null ? null : mop._name;
- _alias = mop==null ? 0 : mop._alias;
- _loc = mop==null ? null : mop._loc;
- _isLoad= mop==null ? true : mop._isLoad;
- _declaredType = mop==null ? Type.BOTTOM : mop._declaredType;
- if( mop==null )
- throw Utils.TODO("Load or not");
- }
-
- // Used by M2 when translating graph to Simple
- public MemOpNode( boolean isLoad ) {
- super((Node)null);
- _name = null;
- _alias = 0;
- _loc = null;
- _isLoad = isLoad;
- _declaredType = Type.BOTTOM;
- }
-
- static String mlabel(String name) { return "[]".equals(name) ? "ary" : ("#".equals(name) ? "len" : name); }
- String mlabel() { return mlabel(_name); }
-
- public Node mem() { return in(1); }
- public Node ptr() { return in(2); }
- public Node off() { return in(3); }
-
- @Override public StringBuilder _print1( StringBuilder sb, BitSet visited ) { return _printMach(sb,visited); }
- public StringBuilder _printMach( StringBuilder sb, BitSet visited ) { throw Utils.TODO(); }
- public int log_size() { return _declaredType.log_size(); }
-
- @Override
- public boolean eq(Node n) {
- MemOpNode mem = (MemOpNode)n; // Invariant
- return _alias==mem._alias; // When comparing types error to use "equals"; always use "=="
- }
-
- @Override
- int hash() { return _alias; }
-
- @Override
- public Parser.ParseException err() {
- Type ptr = ptr()._type;
- // Already an error, but better error messages come from elsewhere
- if( ptr == Type.BOTTOM ) return null;
- if( ptr.isHigh() ) return null; // Assume it will fall to not-null
- // Better be a not-nil TMP
- if( ptr instanceof TypeMemPtr tmp && tmp.notNull() )
- return null;
- return Parser.error( "Might be null accessing '" + _name + "'",_loc);
- }
-}
diff --git a/src/main/java/com/seaofnodes/simple/node/MinusFNode.java b/src/main/java/com/seaofnodes/simple/node/MinusFNode.java
deleted file mode 100644
index 99fc3144..00000000
--- a/src/main/java/com/seaofnodes/simple/node/MinusFNode.java
+++ /dev/null
@@ -1,36 +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 MinusFNode extends Node {
- public MinusFNode(Node in) { super(null, in); }
-
- @Override public String label() { return "MinusF"; }
-
- @Override public String glabel() { return "-"; }
-
- @Override
- public StringBuilder _print1(StringBuilder sb, BitSet visited) {
- in(1)._print0(sb.append("(-"), visited);
- return sb.append(")");
- }
-
- @Override
- public Type compute() {
- if (in(1)._type instanceof TypeFloat i0)
- return i0.isConstant() ? TypeFloat.constant(-i0.value()) : i0;
- return TypeFloat.F64;
- }
-
- @Override
- public Node idealize() {
- // -(-x) is x
- if( in(1) instanceof MinusFNode minus )
- return minus.in(1);
-
- return null;
- }
-}
diff --git a/src/main/java/com/seaofnodes/simple/node/MinusNode.java b/src/main/java/com/seaofnodes/simple/node/MinusNode.java
deleted file mode 100644
index f26ce37e..00000000
--- a/src/main/java/com/seaofnodes/simple/node/MinusNode.java
+++ /dev/null
@@ -1,39 +0,0 @@
-package com.seaofnodes.simple.node;
-
-import com.seaofnodes.simple.type.Type;
-import com.seaofnodes.simple.type.TypeInteger;
-import com.seaofnodes.simple.util.Utils;
-import java.util.BitSet;
-
-public class MinusNode extends Node {
- public MinusNode(Node in) { super(null, in); }
-
- @Override public String label() { return "Minus"; }
-
- @Override public String glabel() { return "-"; }
-
- @Override
- public StringBuilder _print1(StringBuilder sb, BitSet visited) {
- in(1)._print0(sb.append("(-"), visited);
- return sb.append(")");
- }
-
- @Override
- public Type compute() {
- if( in(1)._type.isHigh() ) return TypeInteger.TOP;
- if( in(1)._type instanceof TypeInteger i0 &&
- i0._min != Long.MIN_VALUE && i0._max != Long.MIN_VALUE )
- return TypeInteger.make(-i0._max,-i0._min);
- return TypeInteger.BOT;
- }
-
- @Override
- public Node idealize() {
- // -(-x) is x
- if( in(1) instanceof MinusNode minus )
- return minus.in(1);
-
- return null;
- }
- @Override Node copyF() { return new MinusFNode(null); }
-}
diff --git a/src/main/java/com/seaofnodes/simple/node/MulFNode.java b/src/main/java/com/seaofnodes/simple/node/MulFNode.java
deleted file mode 100644
index 6fbf4bbb..00000000
--- a/src/main/java/com/seaofnodes/simple/node/MulFNode.java
+++ /dev/null
@@ -1,51 +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 MulFNode extends Node {
- public MulFNode(Node lhs, Node rhs) { super(null, lhs, rhs); }
-
- @Override public String label() { return "MulF"; }
-
- @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() {
- Node lhs = in(1);
- Node rhs = in(2);
- Type t1 = lhs._type;
- Type t2 = rhs._type;
-
- // Mul of 1. We do not check for (1*x) because this will already
- // canonicalize to (x*1)
- if ( t2.isConstant() && t2 instanceof TypeFloat i && i.value()==1 )
- return lhs;
-
- // Move constants to RHS: con*arg becomes arg*con
- if ( t1.isConstant() && !t2.isConstant() )
- return swap12();
-
- return null;
- }
- @Override Node copy(Node lhs, Node rhs) { return new MulFNode(lhs,rhs); }
-}
diff --git a/src/main/java/com/seaofnodes/simple/node/MulNode.java b/src/main/java/com/seaofnodes/simple/node/MulNode.java
deleted file mode 100644
index debc2c56..00000000
--- a/src/main/java/com/seaofnodes/simple/node/MulNode.java
+++ /dev/null
@@ -1,63 +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 class MulNode extends ArithNode {
- public MulNode(Node lhs, Node rhs) { super(null, lhs, rhs); }
-
- @Override public String label() { return "Mul"; }
- @Override public String op() { 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;
-
- // Move constants to RHS: con*arg becomes arg*con
- if ( t1.isConstant() && !t2.isConstant() )
- return swap12();
-
- // Multiply by constant
- if ( t2.isConstant() && t2 instanceof TypeInteger i ) {
- // Mul of 1. We do not check for (1*x) because this will already
- // canonicalize to (x*1)
- long c = i.value();
- if( c==1 ) return lhs;
- if( c==0 ) return Parser.ZERO;
- // Mul by a power of 2, +/-1. Bit patterns more complex than this
- // are unlikely to win on an X86 vs the normal "imul", and so
- // become machine-specific.
- if( (c & (c-1)) == 0 )
- return new ShlNode(null,lhs,con(Long.numberOfTrailingZeros(c)));
- // 2**n + 1, e.g. 9
- long d = c-1;
- if( (d & (d-1)) == 0 )
- return new AddNode(new ShlNode(null,lhs,con(Long.numberOfTrailingZeros(d))).peephole(),lhs);
- // 2**n - 1, e.g. 31
- long e = c+1;
- if( (e & (e-1)) == 0 )
- return new SubNode(new ShlNode(null,lhs,con(Long.numberOfTrailingZeros(e))).peephole(),lhs);
-
- }
-
- // 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 MulNode(lhs,rhs); }
- @Override Node copyF() { return new MulFNode(null,null); }
-}
diff --git a/src/main/java/com/seaofnodes/simple/node/MultiNode.java b/src/main/java/com/seaofnodes/simple/node/MultiNode.java
deleted file mode 100644
index b4d6d78e..00000000
--- a/src/main/java/com/seaofnodes/simple/node/MultiNode.java
+++ /dev/null
@@ -1,29 +0,0 @@
-package com.seaofnodes.simple.node;
-
-import com.seaofnodes.simple.util.Ary;
-
-
-public interface MultiNode {
-
- abstract Ary<Node> 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<fs.length; i++ ) {
- if( _ptr._obj._fields[i]._one ) {
- // Once-only fields use the declared type
- ts[i+2] = _ptr._obj._fields[i]._t;
- } else {
- // Others take from the inputs
- Type mt = in(i+2)._type;
- TypeMem mem = mt==Type.TOP ? TypeMem.TOP : (TypeMem)mt;
- Type tfld = mem._t.meet(mem._t.makeZero());
- Type tfld2 = tfld.join(fs[i]._t );
- ts[i+2] = TypeMem.make(fs[i]._alias,tfld2);
- }
- }
- return TypeTuple.make(ts);
- }
-
- @Override
- public Node idealize() {
- Node progress=null;
- for( int i=1; i<nIns(); i++ ) {
- if( in(i) instanceof CastNode cast )
- progress = setDef(i,cast.in(1));
- }
- return progress == null ? null : this;
- }
-
- @Override
- public boolean eq(Node n) { return this == n; }
-
- @Override
- int hash() { return _ptr.hashCode(); }
-
- // ------------
- // MachNode specifics, shared across all CPUs
- public int _arg2Reg, _xslot;
- private RegMask _arg3Mask;
- private RegMask _retMask;
- private RegMask _kills;
- public void cacheRegs(CodeGen code) {
- _arg2Reg = code._mach.callArgMask(TypeFunPtr.CALLOC,2,0).firstReg();
- _arg3Mask = code._mach.callArgMask(TypeFunPtr.CALLOC,3,0);
- // Return mask depends on TFP (either GPR or FPR)
- _retMask = code._mach.retMask(TypeFunPtr.CALLOC);
- // 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 = cfg0().fun()._maxArgSlot;
- // Killed slots for this calls outgoing args
- int xslot = code._mach.maxArgSlot(TypeFunPtr.CALLOC);
- _xslot = (maxReg+fslot)+xslot;
- for( int i=0; i<xslot; i++ )
- kills.set((maxReg+fslot)+i);
- _kills = kills;
- }
- public String op() { return "alloc"; }
- public RegMask regmap(int i) { return i==1 ? _arg3Mask : null; }
- public RegMask outregmap() { return null; }
- public RegMask outregmap(int idx) { return idx==1 ? _retMask : null; }
- public RegMask killmap() { return _kills; }
- public void asm(CodeGen code, SB sb) {
- sb.p("#calloc, ").p(code.reg(size()));
- }
-}
diff --git a/src/main/java/com/seaofnodes/simple/node/Node.java b/src/main/java/com/seaofnodes/simple/node/Node.java
deleted file mode 100644
index 5e5be196..00000000
--- a/src/main/java/com/seaofnodes/simple/node/Node.java
+++ /dev/null
@@ -1,738 +0,0 @@
-package com.seaofnodes.simple.node;
-
-import com.seaofnodes.simple.Parser;
-import com.seaofnodes.simple.util.Ary;
-import com.seaofnodes.simple.util.Utils;
-import com.seaofnodes.simple.codegen.CodeGen;
-import com.seaofnodes.simple.print.IRPrinter;
-import com.seaofnodes.simple.print.JSViewer;
-import com.seaofnodes.simple.type.Type;
-import com.seaofnodes.simple.type.TypeFloat;
-import com.seaofnodes.simple.type.TypeInteger;
-import java.util.*;
-import java.util.function.Function;
-import static com.seaofnodes.simple.codegen.CodeGen.CODE;
-
-/**
- * All Nodes in the Sea of Nodes IR inherit from the Node class.
- * The Node class provides common functionality used by all subtypes.
- * Subtypes of Node specialize by overriding methods.
- */
-public abstract class Node implements Cloneable {
-
- /**
- * Each node has a unique dense Node ID within a compilation context
- * The ID is useful for debugging, for using as an offset in a bitvector,
- * as well as for computing equality of nodes (to be implemented later).
- */
- public int _nid;
-
- /**
- * Inputs to the node. These are use-def references to Nodes.
- * <p>
- * 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<Node> _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.
- * <p>
- * 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<Node> _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<Node> 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 <em>def</em> into a Node. Keeps the edges correct, by removing
- * the corresponding <em>use->def</em> edge. This may make the original
- * <em>def</em> go dead. This function is co-recursive with {@link #kill}.
- * <p>
-
- * This method is the normal path for altering a Node, because it does the
- * proper default edge maintenance. It also <em>immediately</em> 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 extends Node> 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 extends Node> 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 <em>def->use</em> 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 extends Node> 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 <em>uses</em>, by setting all of its <em>defs</em>
- * 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 extends Node> N keep() { return addUse(null); }
- // Remove bogus null.
- @SuppressWarnings("unchecked")
- public <N extends Node> 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:
- * <ul>
- * <li>if the Type {@link Type#isConstant}, we replace with a {@link ConstantNode}</li>
- * <li>in a future chapter we will look for a
- * <a href="https://en.wikipedia.org/wiki/Common_subexpression_elimination">Common Subexpression</a>
- * to eliminate.</li>
- * <li>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. </li>
- *
- * Unlike peephole above, this explicitly returns null for no-change, or not-null
- * for a better replacement (which can be this).
- * </ul>
- */
- 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
- * <a href="https://en.wikipedia.org/wiki/Monotonic_function">Monotonic</a>
- * as it is part of a Monotone Analysis Framework.
- * <a href="https://www.cse.psu.edu/~gxt29/teaching/cse597s21/slides/08monotoneFramework.pdf">See for example this set of slides</a>.
- * <p>
- * 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.
- * <p>
- * {@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 extends Node> 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:
- * <pre> ary[idx+1]</pre>
- * might turn into Sea-of-Nodes IR:
- * <pre> (ary+12)+((idx+1) * 4)</pre>
- * This expression can then be idealized into:
- * <pre> ary + ((idx*4) + (12 + (1*4)))</pre>
- * And more folding:
- * <pre> ary + ((idx<<2) + 16)</pre>
- * And during code-gen:
- * <pre> MOV4 Rary,[Ridx<<2 +16] // or some such hardware-specific notation </pre>
- * <p>
- * {@link #idealize} has a very specific calling convention:
- * <ul>
- * <li>If NO change is made, return {@code null}
- * <li>If ANY change is made, return not-null; this can be {@code this}
- * <li>The returned Node does NOT call {@link #peephole} on itself; the {@link #peephole} call will recursively peephole it.
- * <li>Any NEW nodes that are not directly returned DO call {@link #peephole}.
- * </ul>
- * <p>
- * Examples:
- * <table border="3">
- * <tr><th> before </th><th> after </th><th>return </th><th>comment </th></tr>
- * <tr><td>{@code (x+5) }</td><td>{@code (x+5) }</td><td>{@code null }</td><td>No change</td></tr>
- * <tr><td>{@code (5+x) }</td><td>{@code (x+5) }</td><td>{@code this }</td><td>Swapped arguments</td></tr>
- * <tr><td>{@code ((x+1)+2)}</td><td>{@code (x+(1+2))}</td><td>{@code (x+_) }</td><td>Returns 2 new Nodes</td></tr>
- * </table>
- *
- * 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.
- * <p>
- * Since idealize calls peephole and peephole calls idealize, you must be
- * careful that all idealizations are <em>monotonic</em>: 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<Node> _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 extends Node> 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<len; i++ )
- if( in(i) != n.in(i) )
- return false;
- return eq(n);
- }
- // Subclasses add extra checks (such as ConstantNodes have same constant),
- // and can assume "this!=n" and has the same Java class.
- public boolean eq( Node n ) { return true; }
-
-
- // Cached hash. If zero, then not computed AND this Node is NOT in the GVN
- // table - and can have its edges hacked (which will change his hash
- // anyway). If Non-Zero then this Node is IN the GVN table, or is being
- // probed to see if it can be inserted. His edges are "locked", because
- // hacking his edges will change his hash.
- int _hash;
-
- // If the _hash is set, then the Node is in the GVN table; remove it.
- void unlock() {
- if( _hash==0 ) return;
- Node old = CODE._gvn.remove(this); // Pull from table
- assert old==this;
- _hash=0; // Out of table now
- }
-
-
- // Hash of opcode and inputs
- @Override public final int hashCode() {
- if( _hash != 0 ) return _hash;
- int hash = hash();
- for( Node n : _inputs )
- if( n != null )
- hash = hash ^ (hash<<17) ^ (hash>>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<nIns(); i++ )
- flt.setDef(i, in(i)._type instanceof TypeFloat ? in(i) : new ToFloatNode(in(i)).peephole());
- kill();
- return flt;
- }
- private boolean hasFloatInput() {
- for( int i=1; i<nIns(); i++ )
- if( in(i)._type instanceof TypeFloat )
- return true;
- return false;
- }
- Node copyF() { return null; }
-
-
- // ------------------------------------------------------------------------
- // Peephole utilities
-
- // Swap inputs without letting either input go dead during the swap.
- public Node swap12() {
- unlock(); // Hash is order dependent
- _inputs.swap(1,2);
- return this;
- }
-
- /**
- * Does this node contain all constants? Ignores in(0), as is usually
- * control. In an input is not a constant, we add dep as a dependency to
- * it because dep can make progress if the input becomes a constant later.
- * It is sufficient for one of the non-const inputs to have the dependency,
- * so we don't bother checking the rest.
- */
- boolean allCons(Node dep) {
- for( int i=1; i<nIns(); i++ )
- if( !(in(i)._type.isConstant()) ) {
- dep.addDep(in(i)); // If in(i) becomes a constant later, will trigger some peephole
- return false;
- }
- return true;
- }
-
-
- // Make a shallow copy (same class) of this Node, with given inputs and
- // empty outputs and a new Node ID. The original inputs are ignored.
- // Does not need to be implemented in isCFG() nodes.
- Node copy(Node lhs, Node rhs) { throw Utils.TODO("Binary ops need to implement copy"); }
-
- public Node copy() {
- Node n;
- try { n = (Node)clone(); }
- catch( Exception e ) { throw new RuntimeException(e); }
- n._nid = CODE.getUID(); // allocate unique dense ID
- n._inputs = new Ary<>(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> E walk( Function<Node,E> pred ) {
- assert CODE._visit.isEmpty();
- E rez = _walk(pred);
- CODE._visit.clear();
- return rez;
- }
-
- private <E> E _walk( Function<Node,E> 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 "&phi;_"+_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<nIns(); i++ )
- if( r.in(i)._type == Type.XCONTROL )
- return null;
-
- // Simple Phi-after-MemMerge to a known alias can bypass. Happens when inlining.
- if( _type instanceof TypeMem tmem && tmem._alias!=1 ) {
- for( int i=1; i<nIns(); i++ )
- if( in(i) instanceof MemMergeNode mem ) {
- setDef(i,mem.alias(tmem._alias));
- return this;
- }
- }
-
- // Generic "pull down op"
- Node progress;
- if( same_op() && (progress = drop_same_op()) != null )
- return progress;
-
- // If merging Phi(N, cast(N)) - we are losing the cast JOIN effects, so just remove.
- if( nIns()==3 ) {
- if( in(1) instanceof CastNode cast && addDep(cast.in(1))==in(2) ) return in(2);
- if( in(2) instanceof CastNode cast && addDep(cast.in(1))==in(1) ) return in(1);
- }
- // If merging a null-checked null and the checked value, just use the value.
- // if( val ) ..; phi(Region,False=0/null,True=val);
- // then replace with plain val.
- if( nIns()==3 ) {
- int nullx = -1;
- if( in(1)._type == in(1)._type.makeZero() ) nullx = 1;
- if( in(2)._type == in(2)._type.makeZero() ) nullx = 2;
- if( nullx != -1 ) {
- Node val = in(3-nullx);
- if( val instanceof CastNode cast )
- val = cast.in(1);
- if( addDep(r.idom(this)) instanceof IfNode iff && addDep(iff.pred())==val ) {
- // Must walk the idom on the null side to make sure we hit False.
- CFGNode idom = (CFGNode)r.in(nullx);
- while( idom != null && idom.nIns() > 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<nIns(); i++ ) {
- Node op = in(i);
- if( in(1).getClass() != op.getClass() || op.in(0)!=null || in(1).nIns() != op.nIns() )
- return false; // Wrong class or CFG bound or mismatched inputs
- if( in(1) instanceof MemOpNode mem && mem._alias != ((MemOpNode)op)._alias )
- return false;
- if( op.nOuts() > 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; j<in(1).nIns(); j++ )
- if( op.in(j) instanceof ScopeNode || (op.in(j)==null ^ in(1).in(j)==null) )
- return false; // Lazy Phi input
- }
- return true;
- }
-
- private Node drop_same_op() {
- assert !(in(1) instanceof CFGNode);
- Node op = in(1);
- Node cp = op.copy();
- cp._type = null; // Fresh type
- cp.addDef(null); // No control
-
- for( int j=1; j<op.nIns(); j++ ) {
- boolean needsPhi = false;
- Node x = op.in(j); // Jth input from sample #1
- for( int i=2; i<nIns(); i++ )
- if( in(i).in(j) != x )
- { needsPhi=true; break; }
- if( needsPhi ) {
- x = new PhiNode(_label,op.in(j)._type.glb(false));
- x.addDef(region());
- for( int i=1; i<nIns(); i++ )
- x.addDef(in(i).in(j));
- x = x.peephole();
- }
- cp.addDef(x);
- }
- // Test not running backwards, which can happen for e.g. And's
- if( cp.compute().isa(compute()) )
- return cp;
- cp.kill();
- return null;
- }
-
- /**
- * If only single unique input, return it
- */
- private Node singleUniqueInput() {
- if( region() instanceof LoopNode loop && loop.entry()._type == Type.XCONTROL )
- return null; // Dead entry loops just ignore and let the loop collapse
- Node live = null;
- 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(region().in(i))._type != Type.XCONTROL && in(i) != this )
- if( live == null || live == in(i) ) live = in(i);
- else return null;
- }
- return live;
- }
-
- @Override
- boolean allCons(Node dep) {
- if( !(region() instanceof RegionNode r) ) return false;
- // When the region completes (is no longer in progress) the Phi can
- // become a "all constants" Phi, and the "dep" might make progress.
- dep.addDep(this);
- if( r.inProgress() ) return false;
- return super.allCons(dep);
- }
-
- // True if last input is null
- public boolean inProgress() {
- return in(nIns()-1) == null;
- }
-
- // Never equal if inProgress.
- // Also, joins
- @Override public boolean eq( Node n ) {
- if( inProgress() ) return false;
- Type min = ((PhiNode)n)._minType;
- if( _minType==min ) return true;
- Type mt = min.meet(_minType);
- if( min!=mt && _minType!=mt ) return false;
- //// Theory says these 2 Phis CAN be merged/GVNd, but I need to pick the
- //// most general minType.
- //_minType = ((PhiNode)n)._minType = mt;
- //return true;
- return false;
- }
-
- @Override
- public Parser.ParseException err() {
- if( _type != Type.BOTTOM ) return null;
-
- // BOTTOM means we mixed e.g. int and ptr
- for( int i=1; i<nIns(); i++ )
- // Already an error, but better error messages come from elsewhere
- if( in(i)._type == Type.BOTTOM )
- return null;
-
- // Gather a minimal set of types that "cover" all the rest
- boolean ti=false, tf=false, tp=false, tn=false;
- for( int i=1; i<nIns(); i++ ) {
- Type t = in(i)._type;
- ti |= t instanceof TypeInteger x;
- tf |= t instanceof TypeFloat x;
- tp |= t instanceof TypeMemPtr x;
- tn |= t==Type.NIL;
- }
- return ReturnNode.mixerr(ti,tf,tp,tn, ((RegionNode)region())._loc);
- }
-}
diff --git a/src/main/java/com/seaofnodes/simple/node/ProjNode.java b/src/main/java/com/seaofnodes/simple/node/ProjNode.java
deleted file mode 100644
index a0394e5b..00000000
--- a/src/main/java/com/seaofnodes/simple/node/ProjNode.java
+++ /dev/null
@@ -1,54 +0,0 @@
-package com.seaofnodes.simple.node;
-
-import com.seaofnodes.simple.type.Type;
-import com.seaofnodes.simple.type.TypeMem;
-import com.seaofnodes.simple.type.TypeTuple;
-
-import java.util.BitSet;
-
-public class ProjNode extends Node {
-
- // Which slice of the incoming multipart value
- public final int _idx;
-
- // Debugging label
- public final String _label;
-
- public ProjNode(Node ctrl, int idx, String label) {
- super(new Node[]{ctrl});
- _idx = idx;
- _label = label;
- }
- public ProjNode(ProjNode p) { super(p); _idx = p._idx; _label = p._label; }
-
- @Override public String label() { return _label; }
-
- @Override
- public StringBuilder _print1(StringBuilder sb, BitSet visited) {
- if( _label != null ) return sb.append(_label);
- if( in(0) instanceof CallEndNode cend && cend.call()!=null )
- return cend.call()._print0(sb,visited);
- return sb.append("LONELY PROJ");
- }
-
- @Override public CFGNode cfg0() {
- return in(0) instanceof CFGNode cfg ? cfg : in(0).cfg0();
- }
-
- @Override public boolean isMem() { return _type instanceof TypeMem; }
- @Override public boolean isPinned() { return true; }
-
- @Override
- public Type compute() {
- Type t = in(0)._type;
- return t instanceof TypeTuple tt ? tt._types[_idx] : Type.BOTTOM;
- }
-
- @Override public Node idealize() { return ((MultiNode)in(0)).pcopy(_idx); }
-
- @Override
- public boolean eq( Node n ) { return _idx == ((ProjNode)n)._idx; }
-
- @Override
- int hash() { return _idx; }
-}
diff --git a/src/main/java/com/seaofnodes/simple/node/ReadOnlyMach.java b/src/main/java/com/seaofnodes/simple/node/ReadOnlyMach.java
deleted file mode 100644
index a35f5a1d..00000000
--- a/src/main/java/com/seaofnodes/simple/node/ReadOnlyMach.java
+++ /dev/null
@@ -1,19 +0,0 @@
-package com.seaofnodes.simple.node;
-
-import com.seaofnodes.simple.codegen.CodeGen;
-import com.seaofnodes.simple.codegen.Encoding;
-import com.seaofnodes.simple.codegen.RegMask;
-import com.seaofnodes.simple.util.SB;
-
-/**
- * Cast a pointer to read-only
- */
-public class ReadOnlyMach extends ReadOnlyNode implements MachNode {
- public ReadOnlyMach( ReadOnlyNode n ) { super(n); }
- @Override public String op() { return label(); }
- @Override public RegMask regmap(int i) { assert i==1; return RegMask.FULL; }
- @Override public RegMask outregmap() { return RegMask.FULL; }
- @Override public int twoAddress( ) { return 1; }
- @Override public void encoding( Encoding enc ) { }
- @Override public void asm(CodeGen code, SB sb) { sb.p(code.reg(in(1))); }
-}
diff --git a/src/main/java/com/seaofnodes/simple/node/ReadOnlyNode.java b/src/main/java/com/seaofnodes/simple/node/ReadOnlyNode.java
deleted file mode 100644
index d8fe6286..00000000
--- a/src/main/java/com/seaofnodes/simple/node/ReadOnlyNode.java
+++ /dev/null
@@ -1,30 +0,0 @@
-package com.seaofnodes.simple.node;
-
-import com.seaofnodes.simple.type.Type;
-import com.seaofnodes.simple.type.TypeMemPtr;
-import java.util.BitSet;
-
-/**
- * Cast a pointer to read-only
- */
-public class ReadOnlyNode extends Node {
- public ReadOnlyNode( Node n ) { super(null,n); }
- public ReadOnlyNode( ReadOnlyNode n ) { super(n); }
- @Override public String label() { return "ReadOnly"; }
-
- @Override
- public StringBuilder _print1(StringBuilder sb, BitSet visited) {
- return in(1)._print0(sb.append("(const)"),visited);
- }
- @Override public Type compute() {
- Type t = in(1)._type;
- return t instanceof TypeMemPtr tmp ? tmp.makeRO() : t;
- }
-
- @Override public Node idealize() {
- if( in(1)._type instanceof TypeMemPtr tmp && tmp.isFinal() )
- return in(1);
- return null;
- }
-
-}
diff --git a/src/main/java/com/seaofnodes/simple/node/RegionNode.java b/src/main/java/com/seaofnodes/simple/node/RegionNode.java
deleted file mode 100644
index 6f740a47..00000000
--- a/src/main/java/com/seaofnodes/simple/node/RegionNode.java
+++ /dev/null
@@ -1,168 +0,0 @@
-package com.seaofnodes.simple.node;
-
-import com.seaofnodes.simple.*;
-import com.seaofnodes.simple.Parser;
-import com.seaofnodes.simple.codegen.CodeGen;
-import com.seaofnodes.simple.type.Type;
-import java.util.*;
-
-public class RegionNode extends CFGNode {
- // Source location for late discovered errors
- public Parser.Lexer _loc;
-
- public RegionNode(Parser.Lexer loc, Node... nodes) { super(nodes); _loc = loc; }
- public RegionNode(RegionNode r, Parser.Lexer loc) { super(r); _loc = loc; }
- public RegionNode(RegionNode r) { super(r); if( r!=null ) _loc = r._loc; }
-
- @Override
- public String label() { return "Region"; }
-
- @Override
- public StringBuilder _print1(StringBuilder sb, BitSet visited) {
- return sb.append(label());
- }
-
- @Override public boolean blockHead() { return true; }
-
- @Override
- public Type compute() {
- if( inProgress() ) return Type.CONTROL;
- Type t = Type.XCONTROL;
- for (int i = 1; i < nIns(); i++)
- t = t.meet(in(i)._type);
- return t;
- }
-
- @Override
- public Node idealize() {
- if( inProgress() ) return null;
-
- Node progress = deadPath();
- if( progress!=null ) return progress;
-
- // If down to a single input, become that input
- if( nIns()==2 && !hasPhi() )
- return in(1); // Collapse if no Phis; 1-input Phis will collapse on their own
-
- // If a CFG diamond with no merging, delete: "if( pred ) {} else {};"
- if( !hasPhi() && nIns()>3 && // 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; i<nIns(); i++ )
- if( cfg(i) instanceof RegionNode region && region.getClass() == RegionNode.class && region.nIns()>2 && !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; j<region.nIns(); j++ )
- phi.addDef(phi2==null ? phi.in(i) : phi2.in(j));
- CodeGen.CODE.add(phi);
- }
- }
-
- // Fold Region
- for( int j=1; j<region.nIns(); j++ )
- addDef(region.in(j));
- setDef(i,Parser.XCTRL);
- return this;
- }
- }
-
- return null;
- }
-
- Node deadPath() {
- // Delete dead paths into a Region
- int path = findDeadInput();
- if( path==0 ) return null;
- // Do not delete the entry path of a loop (ok to remove the back edge
- // and make the loop a single-entry Region which folds away the Loop).
- // Folding the entry path confused the loop structure, moving the
- // backedge to the entry point.
- if( this instanceof LoopNode loop && loop.entry()==in(path) )
- return null;
- // Cannot use the obvious output iterator here, because a Phi deleting
- // an input might recursively delete *itself*. This shuffles the
- // output array, and we might miss iterating an unrelated Phi. So on
- // rare occasions we repeat the loop to get all the Phis.
- int nouts = 0;
- while( nouts != nOuts() ) {
- nouts = nOuts();
- for( int i=0; i<nOuts(); i++ )
- if( out(i) instanceof PhiNode phi && phi.nIns()==nIns() )
- CodeGen.CODE.addAll(phi.delDef(path)._outputs);
- }
- return isDead() ? Parser.XCTRL : delDef(path);
- }
-
-
- private int findDeadInput() {
- for( int i=1; i<nIns(); i++ )
- if( in(i)._type==Type.XCONTROL )
- return i;
- return 0; // All inputs alive
- }
-
- boolean hasPhi() {
- for( Node phi : _outputs )
- if( phi instanceof PhiNode )
- return true;
- return false;
- }
-
- // Is there a value in-between 2 Regions? Difficult to correctly fuse
- // without extensive testing, so just ignore.
- boolean hasMidUser(RegionNode r) {
- for( Node use : r._outputs ) {
- if( use==this ) continue;
- if( use instanceof PhiNode ) {
- for( Node data : use._outputs ) {
- if( !(data instanceof PhiNode phi2) || phi2.region()!=this )
- { addDep(use); addDep(data); return true; }
- }
- } else
- { addDep(use); return true; } // Control user
- }
- return false;
- }
-
-
- // Immediate dominator of Region is a little more complicated.
- @Override public int idepth() {
- if( CodeGen.CODE.validIDepth(_idepth) )
- return _idepth;
- int d=0;
- for( Node n : _inputs )
- if( n!=null )
- d = Math.max(d,CodeGen.CODE.iDepthFrom(((CFGNode)n).idepth()));
- return _idepth = d;
- }
-
- @Override public CFGNode idom(Node dep) {
- CFGNode lca = null;
- // Walk the LHS & RHS idom trees in parallel until they match, or either fails.
- // Because this does not cache, it can be linear in the size of the program.
- for( int i=1; i<nIns(); i++ )
- lca = cfg(i)._idom(lca,dep);
- return lca;
- }
-
- // True if last input is null
- public boolean inProgress() { return nIns()>1 && 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.
- * <p>
- * In this presentation, Return functions as a Stop node, since multiple <code>return</code> statements are not possible.
- * The Stop node will be introduced in Chapter 6 when we implement <code>if</code> statements.
- * <p>
- * 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<nIns(); i++ )
- sb.p(code.reg(in(i),fun())).p(" ");
- // If we did not get the expected rpc, print which one we got
- else if( code._regAlloc.regnum(rpc()) != code._mach.rpc() )
- sb.p("[").p(code.reg(rpc())).p("]");
- }
-
-}
diff --git a/src/main/java/com/seaofnodes/simple/node/RoundF32Node.java b/src/main/java/com/seaofnodes/simple/node/RoundF32Node.java
deleted file mode 100644
index 7936086d..00000000
--- a/src/main/java/com/seaofnodes/simple/node/RoundF32Node.java
+++ /dev/null
@@ -1,38 +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 RoundF32Node extends Node {
- public RoundF32Node(Node lhs) { super(null, lhs); }
-
- @Override public String label() { return "RoundF32"; }
-
- @Override public String glabel() { return "(f32)"; }
-
- @Override
- public StringBuilder _print1(StringBuilder sb, BitSet visited) {
- return in(1)._print0(sb.append("((f32)"), visited).append(")");
- }
-
- @Override
- public Type compute() {
- if (in(1)._type instanceof TypeFloat i0 && i0.isConstant() )
- return TypeFloat.constant((float)i0.value());
- return in(1)._type;
- }
-
- @Override
- public Node idealize() {
- Node lhs = in(1);
- Type t1 = lhs._type;
-
- // RoundF32 of float
- if( t1 instanceof TypeFloat tf && tf._sz==32 )
- return lhs;
-
- return null;
- }
-}
diff --git a/src/main/java/com/seaofnodes/simple/node/SarNode.java b/src/main/java/com/seaofnodes/simple/node/SarNode.java
deleted file mode 100644
index ee15fa65..00000000
--- a/src/main/java/com/seaofnodes/simple/node/SarNode.java
+++ /dev/null
@@ -1,38 +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 SarNode extends ArithNode {
- public SarNode(Parser.Lexer loc, Node lhs, Node rhs) { super(loc, lhs, rhs); }
-
- @Override public String label() { return "Sar"; }
- @Override public String op() { return ">>"; }
- @Override public String glabel() { return "&gt;&gt;"; }
-
- @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<Var> _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<Kind> _kinds;
- // Lexical scope nesting depth
- public int depth() { return _kinds._len; }
-
- // Extra guards; tested predicates and casted results
- private final Ary<Node> _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<nIns(); i++ ) {
- if( j < depth() && i == _kinds.at(j)._lexSize ) { sb.append("| "); j++; }
- Var v = _vars.at(i);
- sb.append(v.type());
- sb.append(" ");
- if( v._final ) sb.append("!");
- sb.append(v._name);
- sb.append("=");
- Node n = in(i);
- while( n instanceof ScopeNode loop ) {
- sb.append("Lazy_");
- n = loop.in(i);
- }
- if( n==null ) sb.append("___");
- else n._print0(sb, visited);
- sb.append(", ");
- }
- sb.setLength(sb.length()-2);
- return sb.append("]");
- }
-
-
- public Node ctrl() { return in(0); }
- public MemMergeNode mem() { return (MemMergeNode)in(1); }
- public Var var(int i) { return _vars.at(i); }
-
- /**
- * The ctrl of a ScopeNode is always bound to the currently active
- * control node in the graph, via a special name '$ctrl' that is not
- * a valid identifier in the language grammar and hence cannot be
- * referenced in Simple code.
- *
- * @param n The node to be bound to '$ctrl'
- *
- * @return Node that was bound
- */
- public <N extends Node> 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<nIns(); i++ ) {
- Var v = var(i);
- if( !v.isFRef() ) continue;
- if( depth()==1 )
- throw Parser.error("Undefined name '" + v._name + "'",v._loc);
- _vars .swap(n,i);
- _inputs.swap(n,i);
- v._idx = n;
- kind._lexSize = ++n;
- }
- }
-
-
- public boolean inConstructor() { return _kinds.last() instanceof Kind.Define; }
- public boolean inAllocation () { return _kinds.last() instanceof Kind.Alloc ; }
- public boolean inFunction () { return _kinds.last() instanceof Kind.Func ; }
-
- public Kind kind( Var v ) {
- for( int i=depth()-1; i>=0; i-- )
- if( v._idx >= _kinds.at(i)._lexSize )
- return _kinds.at(i);
- throw Utils.TODO();
- }
-
- // Return the kind for the defining scope, or a function scope if found
- // first. Block scopes means the variable can be r/w directly in the
- // scope, Function scopes require final constants (no capture), and
- // Constructor scopes mean an instance variable reference.
-
- // Walk up-lexical scope looking for defining lexical Kind.
- // - Skip any amount of nested block scopes
- // - Walking out of a function requires v be a final constant (for now) -
- // and we can stop walking.
-
- // - Walking out of a method and (skipping Blocks) into the matching struct
- // is OK, and this is a instance var load.
-
- // - Walking out of a method and into the wrong struct is an error;
- // including nested structs containing (no nested classes yet)
-
- // Returns error, forward-ref, ok containing struct, ok containing function/block.
-
- public String outOfFunction( Var v ) {
- //if( v==null ) return null; // Prolly forward reference
- //int i; for( i=_lexSize._len-1; i>=0 && v._idx<_lexSize.at(i); i-- )
- // if( _kinds.at(i)=="{->}" ) return "{->}";
- //return _kinds.at(i);
- throw Utils.TODO();
- }
-
-
- // 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).
- * <p>
- * 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.
- * <p>
- * 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<mem.nIns(); i++ )
- // For lazy phis on loops we use a sentinel
- // that will trigger phi creation on update
- memdup.addDef(loop ? this : mem.in(i));
- dup.addDef(memdup);
-
- // Copy of other inputs
- for( int i=2; i<nIns(); i++ )
- // For lazy phis on loops we use a sentinel
- // that will trigger phi creation on update
- dup.addDef(loop && !var(i)._final ? this : in(i));
- return dup;
- }
-
- /**
- * Merges the names whose node bindings differ, by creating Phi node for such names
- * The names could occur at all stack levels, but a given name can only differ in the
- * innermost stack level where the name is bound.
- *
- * @param that The ScopeNode to be merged into this
- * @return A new node representing the merge point
- */
- public RegionNode mergeScopes(ScopeNode that, Parser.Lexer loc) {
- RegionNode r = ctrl(new RegionNode(loc,null,ctrl(), that.ctrl()).keep());
- mem()._merge(that.mem(),r);
- this ._merge(that ,r);
- that.kill(); // Kill merged scope
- CodeGen.CODE.add(r);
- return r.unkeep();
- }
-
- private void _merge(ScopeNode that, RegionNode r) {
- for( int i = 2; i < nIns(); i++)
- if( in(i) != that.in(i) ) { // No need for redundant Phis
- // If we are in lazy phi mode we need to a lookup
- // by name as it will trigger a phi creation
- Var v = var(i);
- Node lhs = this.in(this.update(v,null));
- Node rhs = that.in(that.update(v,null));
- setDef(i, new PhiNode(v._name, v.type(), r, lhs, rhs).peephole());
- }
- }
-
- // Balance arms of an IF. Extra lonely defs are thrown: "if(pred) int x;".
- // Forward refs are copied to the other side, "as if" they were there all along.
- public void balanceIf( ScopeNode scope ) {
- for( int i = nIns(); i < scope.nIns(); i++ ) {
- Var n = scope.var(i);
- if( n.isFRef() ) { // RHS has forward refs
- _vars.add(n); // Copy to LHS
- addDef(scope.in(i));
- } else
- throw Parser.error("Cannot define a '"+n._name+"' on one arm of an if",n._loc);
- }
- }
-
-
- // peephole the backedge scope into this loop head scope
- // We set the second input to the phi from the back edge (i.e. loop body)
- public void endLoop(ScopeNode back, ScopeNode exit ) {
- Node ctrl = ctrl();
- assert ctrl instanceof LoopNode loop && loop.inProgress();
- ctrl.setDef(2,back.ctrl());
-
- mem()._endLoopMem( this, back.mem(), exit.mem() );
- this ._endLoop ( this, back , exit );
- back.kill(); // Loop backedge is dead
- // Now one-time do a useless-phi removal
- mem()._useless();
- this ._useless();
-
- // The exit mem's lazy default value had been the loop top,
- // now it goes back to predating the loop.
- exit.mem().setDef(1,mem().in(1));
- }
-
- // Fill in the backedge of any inserted Phis
- void _endLoop( ScopeNode scope, Node back, Node exit ) {
- for( int i=2; i<nIns(); i++ ) {
- if( var(i)._final ) continue; // Final vars did not get modified in the loop
- if( var(i).type().isHighOrConst() ) continue; // Cannot lift higher than a constant, so no Phi
- if( back.in(i) != scope ) {
- PhiNode phi = (PhiNode)in(i);
- assert phi.region()==scope.ctrl() && phi.in(2)==null;
- phi.setDef(2,back.in(i)); // Fill backedge
- }
- if( exit.in(i) == scope ) // Replace a lazy-phi on the exit path also
- exit.setDef(i,in(i));
- }
- }
-
-
- // Up-casting: using the results of an If to improve a value.
- // E.g. "if( ptr ) ptr.field;" is legal because ptr is known not-null.
- public void addGuards( Node ctrl, Node pred, boolean invert ) {
- assert ctrl instanceof CFGNode;
- _guards.add(ctrl); // Marker to distinguish 0,1,2 guards
- // add pred & its cast to the normal input list, with special Vars
- if( pred==null || pred.isDead() )
- return; // Dead, do not add any guards
- // Invert the If conditional
- if( invert )
- pred = pred instanceof NotNode not ? not.in(1) : CodeGen.CODE.add(new NotNode(pred).peephole());
- // This is a zero/null test.
- // Compute the positive test type.
- Type tnz = pred._type.nonZero();
- if( tnz!=null )
- _addGuard(tnz,ctrl,pred);
-
- // Compute the negative test type.
- if( pred instanceof NotNode not ) {
- Node npred = not.in(1);
- Type tzero = npred._type.makeZero();
- _addGuard(tzero,ctrl,npred);
- }
- }
-
- private void _addGuard(Type guard, Node ctrl, Node pred) {
- Type tcast = guard.join(pred._type);
- if( tcast != pred._type && !tcast.isHigh() ) {
- Node cast = new CastNode(tcast,ctrl,pred.keep()).peephole().keep();
- _guards.add(pred);
- _guards.add(cast);
- replace(pred,cast);
- }
- }
-
-
- // Remove matching pred/cast pairs from this guarded region.
- public ScopeNode removeGuards( Node ctrl ) {
- assert ctrl instanceof CFGNode;
- // 0,1 or 2 guards
- while( true ) {
- Node g = _guards.pop();
- if( g == ctrl ) break;
- if( g instanceof CFGNode ) continue;
- g .unkill(); // Pop/kill cast
- _guards.pop().unkill(); // Pop/kill pred
- }
- return this;
- }
-
- // If we find a guarded instance of pred, replace with the upcasted version
- public Node upcastGuard( Node pred ) {
- // If finding an instanceof pred, replace with cast.
- // Otherwise, just pred itself.
- int i = _guards._len;
- while( 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<nIns(); i++ )
- if( in(i)==old )
- setDef(i,cast);
- }
-
-}
diff --git a/src/main/java/com/seaofnodes/simple/node/ShlNode.java b/src/main/java/com/seaofnodes/simple/node/ShlNode.java
deleted file mode 100644
index 2557bf6b..00000000
--- a/src/main/java/com/seaofnodes/simple/node/ShlNode.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 ShlNode extends ArithNode {
- public ShlNode(Parser.Lexer loc, Node lhs, Node rhs) { super(loc, lhs, rhs); }
-
- @Override public String label() { return "Shl"; }
- @Override public String op() { return "<<"; }
- @Override public String glabel() { return "&lt;&lt;"; }
-
- @Override long doOp( long x, long y ) { return x << y; }
- @Override TypeInteger doOp( TypeInteger x, TypeInteger y ) {
- if( x == TypeInteger.ZERO )
- return TypeInteger.ZERO;
- if( y.isConstant() ) {
- int shf = (int)y.value();
- // If no overflow, shift endpoints
- if( !(((x._min<<shf)>>shf) != x._min ||
- ((x._max<<shf)>>shf) != x._max ) )
- return TypeInteger.make(x._min<<shf,x._max<<shf);
- }
- return TypeInteger.BOT;
- }
-
- @Override
- public Node idealize() {
- Node lhs = in(1);
- Node rhs = in(2);
-
- if( rhs._type instanceof TypeInteger shl && shl.isConstant() ) {
- // Shl of 0.
- if( (shl.value()&63)==0 )
- return lhs;
- // (x + c) << i => (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 "&gt;&gt;&gt;"; }
-
- @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.
- * <p>
- * 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<nIns(); i++ )
- if( addDep(((ReturnNode)in(i)).fun()).isDead() )
- delDef(i--);
- if( len != nIns() ) return this;
- return null;
- }
-
- @Override public int idepth() {
- if( _idepth!=0 ) return _idepth;
- int d=0;
- for( Node ret : _inputs )
- d = Math.max(d,((ReturnNode)ret).idepth()+1);
- return _idepth=d;
- }
-}
diff --git a/src/main/java/com/seaofnodes/simple/node/StoreNode.java b/src/main/java/com/seaofnodes/simple/node/StoreNode.java
deleted file mode 100644
index 5fb274db..00000000
--- a/src/main/java/com/seaofnodes/simple/node/StoreNode.java
+++ /dev/null
@@ -1,140 +0,0 @@
-package com.seaofnodes.simple.node;
-
-import com.seaofnodes.simple.*;
-import com.seaofnodes.simple.type.*;
-import java.util.BitSet;
-
-/**
- * Store represents setting a value to a memory based object, in chapter 10
- * this means a field inside a struct.
- */
-public class StoreNode extends MemOpNode {
-
- private final boolean _init; // Initializing writes are allowed to write null
-
- /**
- * @param name The struct field we are assigning to
- * @param mem The memory alias node - this is updated after a Store
- * @param ptr The ptr to the struct base where we will store a value
- * @param off The offset inside the struct base
- * @param value Value to be stored
- */
- public StoreNode(Parser.Lexer loc, String name, int alias, Type glb, Node mem, Node ptr, Node off, Node value, boolean init) {
- super(loc, name, alias, false, glb, mem, ptr, off, value);
- _init = init;
- }
-
- // GraphVis DOT code and debugger labels
- @Override public String label() { return "st_"+mlabel(); }
- // GraphVis node-internal labels
- @Override public String glabel() { return "." +_name+"="; }
- @Override public boolean isMem() { return true; }
-
- public Node val() { return in(4); }
-
- @Override
- public StringBuilder _print1(StringBuilder sb, BitSet visited) {
- return sb.append(".").append(_name).append("=").append( val()).append(";");
- }
-
- @Override
- public Type compute() {
- Type val = val()._type;
- Type mem0 = mem()._type;
- if( mem0 == Type.TOP ) return TypeMem.TOP;
- TypeMem mem = (TypeMem)mem0; // Invariant
- if( mem == TypeMem.TOP ) return TypeMem.TOP;
- Type t = Type.BOTTOM; // No idea on field contents
- // Same alias, lift val to the declared type and then meet into other fields
- if( mem._alias == _alias ) {
- assert !_declaredType.isFRef();
- // Sharpen memory value; required for narrowing stores where the parser inserts
- // zero/sign masking and somebody reads the TypeMem type.
- val = val.join(_declaredType);
- t = val.meet(mem._t);
- }
- return TypeMem.make(_alias,t);
- }
-
- @Override
- public Node idealize() {
-
- if( mem() instanceof CastNode cast ) {
- setDef(1,cast.in(1));
- return this;
- }
-
- // Simple store-after-store on same address. Should pick up the
- // required init-store being stomped by a first user store.
- if( mem() instanceof StoreNode st &&
- ptr()==st.ptr() && // Must check same object
- off()==st.off() && // And same offset (could be "same alias" but this handles arrays to same index)
- ptr()._type instanceof TypeMemPtr && // No bother if weird dead pointers
- // Must have exactly one use of "this" or you get weird
- // non-serializable memory effects in the worse case.
- checkOnlyUse(st) ) {
- assert _name.equals(st._name); // Equiv class aliasing is perfect
- setDef(1,st.mem());
- return this;
- }
-
- // Simple store-after-MemMerge to a known alias can bypass. Happens when inlining.
- if( mem() instanceof MemMergeNode mem ) {
- setDef(1,mem.alias(_alias));
- return this;
- }
-
- // Value is automatically truncated by narrow store
- if( val() instanceof AndNode and && and.in(2)._type.isConstant() ) {
- int log = _declaredType.log_size();
- if( log<3 ) { // And-mask vs narrow store
- long mask = ((TypeInteger)and.in(2)._type).value();
- long bits = (1L<<(8<<log))-1;
- // Mask does not mask any of the stored bits
- if( (bits&mask)==bits )
- // So and-mask is already covered by the store
- { setDef(4,and.in(1)); return this; }
- }
- }
-
- // Store will chop high order bits off; math to change those bits can be dropped.
- if( val() instanceof SarNode shr &&
- shr.in(1) instanceof ShlNode shl &&
- shr.in(2)._type.isConstant() &&
- shl.in(2)._type.isConstant() ) {
- TypeInteger shrC = (TypeInteger) shr.in(2)._type;
-
- // size of the thing that sign-extends
- int base_size = (1 << shr.in(1)._type.log_size()) << 3;
- int not_affected_bits = base_size - (int) shrC.value();
- int store_size = (1 << log_size()) << 3;
- // if the store is unrelated to the shift amount, then get rid of the shift
- if( shl.in(2)._type == shr.in(2)._type && shrC.value() >= 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<nIns(); i++ ) {
- sb.append(_ts._fields[i]._fname).append(":");
- sb.append(in(i)==null ? Type.BOTTOM : in(i)._type);
- sb.append("; ");
- }
- sb.setLength(sb.length()-2);
- return sb.append("}");
- }
-
- @Override
- public TypeStruct compute() {
- if( _ts==null ) return TypeStruct.BOT;
- Field[] fs = new Field[_ts._fields.length];
- for( int i=0; i<fs.length; i++ )
- fs[i] = _ts._fields[i].makeFrom(in(i)==null ? Type.TOP : in(i)._type);
- return TypeStruct.make(_ts._name,false,fs);
- }
-
- @Override
- public Node idealize() { return null; }
-
- @Override
- public boolean eq(Node n) { return _ts == ((StructNode)n)._ts; }
-
- @Override
- int hash() { return _ts==null ? 0 : _ts.hashCode(); }
-}
diff --git a/src/main/java/com/seaofnodes/simple/node/SubFNode.java b/src/main/java/com/seaofnodes/simple/node/SubFNode.java
deleted file mode 100644
index 5c7e969f..00000000
--- a/src/main/java/com/seaofnodes/simple/node/SubFNode.java
+++ /dev/null
@@ -1,44 +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 SubFNode extends Node {
- public SubFNode(Node lhs, Node rhs) { super(null, lhs, rhs); }
-
- @Override public String label() { return "SubF"; }
-
- @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() {
- Node lhs = in(1);
- Type t2 = in(2)._type;
-
- // Sub of 0.
- if ( t2.isConstant() && t2 instanceof TypeFloat i && i.value()==0 )
- return lhs;
-
- return null;
- }
- @Override Node copy(Node lhs, Node rhs) { return new SubFNode(lhs,rhs); }
-}
diff --git a/src/main/java/com/seaofnodes/simple/node/SubNode.java b/src/main/java/com/seaofnodes/simple/node/SubNode.java
deleted file mode 100644
index 8e4f6f79..00000000
--- a/src/main/java/com/seaofnodes/simple/node/SubNode.java
+++ /dev/null
@@ -1,43 +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 class SubNode extends ArithNode {
- public SubNode(Node lhs, Node rhs) { super(null, lhs, rhs); }
-
- @Override public String label() { return "Sub"; }
- @Override public String op() { return "-"; }
-
- @Override long doOp( long x, long y ) { return x - y; }
- @Override TypeInteger doOp(TypeInteger x, TypeInteger y) {
- // Sub of same is 0
- if( in(1)==in(2) )
- return TypeInteger.ZERO;
- // Fold ranges like {2-3} - {0-1} into {1-3}.
- if( !AddNode.overflow(x._min,-y._max) &&
- !AddNode.overflow(x._max,-y._min) &&
- y._min != Long.MIN_VALUE )
- return TypeInteger.make(x._min-y._max,x._max-y._min);
-
- return TypeInteger.BOT;
- }
-
- @Override
- public Node idealize() {
- // x - (-y) is x+y
- if( in(2) instanceof MinusNode minus )
- return new AddNode(in(1),minus.in(1));
-
- // (-x) - y is -(x+y)
- if( in(1) instanceof MinusNode minus )
- return new MinusNode(new AddNode(minus.in(1),in(2)).peephole());
-
- return super.idealize();
- }
-
- @Override Node copy(Node lhs, Node rhs) { return new SubNode(lhs,rhs); }
- @Override Node copyF() { return new SubFNode(null,null); }
-}
diff --git a/src/main/java/com/seaofnodes/simple/node/ToFloatNode.java b/src/main/java/com/seaofnodes/simple/node/ToFloatNode.java
deleted file mode 100644
index c098ad99..00000000
--- a/src/main/java/com/seaofnodes/simple/node/ToFloatNode.java
+++ /dev/null
@@ -1,34 +0,0 @@
-package com.seaofnodes.simple.node;
-
-import com.seaofnodes.simple.type.Type;
-import com.seaofnodes.simple.type.TypeFloat;
-import com.seaofnodes.simple.type.TypeInteger;
-
-import java.util.BitSet;
-
-public class ToFloatNode extends Node {
- public ToFloatNode(Node lhs) { super(null, lhs); }
-
- @Override public String label() { return "ToFloat"; }
-
- @Override public String glabel() { return "(flt)"; }
-
- @Override
- public StringBuilder _print1(StringBuilder sb, BitSet visited) {
- return in(1)._print0(sb.append("(flt)"), visited);
- }
-
- @Override
- public Type compute() {
- if( in(1)._type == Type.NIL ) return TypeFloat.FZERO;
- if (in(1)._type instanceof TypeInteger i0 ) {
- if( i0.isHigh() ) return TypeFloat.F64.dual();
- if( i0.isConstant() )
- return TypeFloat.constant(i0.value());
- }
- return TypeFloat.F64;
- }
-
- @Override public Node idealize() { return null; }
- @Override Node copy(Node lhs, Node rhs) { return new ToFloatNode(lhs); }
-}
diff --git a/src/main/java/com/seaofnodes/simple/node/XCtrlNode.java b/src/main/java/com/seaofnodes/simple/node/XCtrlNode.java
deleted file mode 100644
index 8c620a04..00000000
--- a/src/main/java/com/seaofnodes/simple/node/XCtrlNode.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 XCtrlNode extends CFGNode {
- public XCtrlNode() { super(new Node[]{CodeGen.CODE._start}); }
- @Override public String label() { return "Xctrl"; }
- @Override public StringBuilder _print1(StringBuilder sb, BitSet visited) { return sb.append("Xctrl"); }
- @Override public boolean isConst() { return true; }
- @Override public Type compute() { return Type.XCONTROL; }
- @Override public Node idealize() { return null; }
-}
diff --git a/src/main/java/com/seaofnodes/simple/node/XorNode.java b/src/main/java/com/seaofnodes/simple/node/XorNode.java
deleted file mode 100644
index fdb4863e..00000000
--- a/src/main/java/com/seaofnodes/simple/node/XorNode.java
+++ /dev/null
@@ -1,44 +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 XorNode extends ArithNode {
- public XorNode(Parser.Lexer loc, Node lhs, Node rhs) { super(loc, lhs, rhs); }
-
- @Override public String label() { return "Xor"; }
- @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;
-
- // Xor 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 XorNode(_loc,lhs,rhs); }
-}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/AddARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/AddARM.java
deleted file mode 100644
index 1c865254..00000000
--- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/AddARM.java
+++ /dev/null
@@ -1,20 +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 AddARM extends MachConcreteNode implements MachNode {
- AddARM( Node add) { super(add); }
- @Override public String op() { return "add"; }
- @Override public RegMask regmap(int i) { return arm.RMASK; }
- @Override public RegMask outregmap() { return arm.WMASK; }
-
- // ADD (shifted register)
- @Override public void encoding( Encoding enc ) { arm.r_reg(enc,this,arm.OP_ADD); }
- // General form: "rd = rs1 + rs2"
- @Override public void asm(CodeGen code, SB sb) {
- sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" + ").p(code.reg(in(2)));
- }
-
-}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/AddFARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/AddFARM.java
deleted file mode 100644
index eb75c1a0..00000000
--- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/AddFARM.java
+++ /dev/null
@@ -1,22 +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 AddFARM extends MachConcreteNode implements MachNode {
- AddFARM(Node addf) { super(addf); }
- @Override public String op() { return "addf"; }
- @Override public RegMask regmap(int i) { return arm.DMASK; }
- @Override public RegMask outregmap() { return arm.DMASK; }
-
- //FADD (scalar)
- @Override public void encoding( Encoding enc ) { arm.f_scalar(enc,this,arm.OPF_OP_ADD);}
-
- // Default on double precision for now(64 bits)
- // General form: "addf 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/AddIARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/AddIARM.java
deleted file mode 100644
index 93c9e8c3..00000000
--- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/AddIARM.java
+++ /dev/null
@@ -1,27 +0,0 @@
-package com.seaofnodes.simple.node.cpus.arm;
-
-import com.seaofnodes.simple.util.SB;
-import com.seaofnodes.simple.codegen.*;
-import com.seaofnodes.simple.node.*;
-
-public class AddIARM extends MachConcreteNode implements MachNode {
- final int _imm;
- AddIARM(Node add, int imm) {
- super(add);
- _inputs.pop();
- _imm = imm;
- }
- @Override public String op() {
- return _imm == 1 ? "inc" : (_imm == -1 ? "dec" : "addi");
- }
- @Override public RegMask regmap(int i) { return arm.RMASK; }
- @Override public RegMask outregmap() { return arm.WMASK; }
- //ADD (immediate)
- @Override public void encoding( Encoding enc ) {
- arm.imm_inst(enc,this, in(1), arm.OPI_ADD,_imm);
- }
- // General form: "addi rd = rs1 + imm"
- @Override public void asm(CodeGen code, SB sb) {
- sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" + #").p(_imm);
- }
-}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/AndARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/AndARM.java
deleted file mode 100644
index 77115d54..00000000
--- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/AndARM.java
+++ /dev/null
@@ -1,21 +0,0 @@
-package com.seaofnodes.simple.node.cpus.arm;
-
-import com.seaofnodes.simple.codegen.*;
-import com.seaofnodes.simple.node.MachConcreteNode;
-import com.seaofnodes.simple.node.MachNode;
-import com.seaofnodes.simple.node.Node;
-import com.seaofnodes.simple.util.SB;
-
-public class AndARM extends MachConcreteNode implements MachNode {
- AndARM(Node and) { super(and); }
- @Override public String op() { return "and"; }
- @Override public String glabel() { return "&"; }
- @Override public RegMask regmap(int i) { return arm.RMASK; }
- @Override public RegMask outregmap() { return arm.WMASK; }
- // AND (shifted register)
- @Override public void encoding( Encoding enc ) { arm.r_reg(enc,this,arm.OP_AND); }
- // General form: #rd = rs1 & rs2
- @Override public void asm(CodeGen code, SB sb){
- sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" & ").p(code.reg(in(2)));
- }
-}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/AndIARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/AndIARM.java
deleted file mode 100644
index 5b739cd3..00000000
--- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/AndIARM.java
+++ /dev/null
@@ -1,24 +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 AndIARM extends MachConcreteNode implements MachNode {
- final int _imm;
- AndIARM(Node and, int imm) {
- super(and);
- _inputs.pop();
- _imm = imm;
- }
- @Override public String op() { return "andi"; }
- @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_n(enc,this, in(1), arm.OPI_AND,_imm);
- }
- // General form: "andi rd = rs1 & imm"
- @Override public void asm(CodeGen code, SB sb) {
- sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" & #").p(arm.decodeImm12(_imm));
- }
-} \ No newline at end of file
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/AsrARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/AsrARM.java
deleted file mode 100644
index b83fb3cb..00000000
--- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/AsrARM.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;
-
-// Arithmetic Shift Right (register)
-public class AsrARM extends MachConcreteNode implements MachNode {
- AsrARM(Node asr) {super(asr);}
- @Override public String op() { return "sar"; }
- @Override public RegMask regmap(int i) { return arm.RMASK; }
- @Override public RegMask outregmap() { return arm.DMASK; }
- @Override public void encoding( Encoding enc ) { arm.shift_reg(enc,this,arm.OP_ASR); }
- @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/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<nargs(); i++ )
- sb.p(code.reg(arg(i+2))).p(" ");
- sb.unchar(2);
- }
-}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/CallRRARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/CallRRARM.java
deleted file mode 100644
index a3e1cf19..00000000
--- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/CallRRARM.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;
-
-public class CallRRARM extends CallNode implements MachNode {
- CallRRARM(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
- ? arm.RMASK // Function call target
- : arm.callInMask(tfp(),i,fun()._maxArgSlot); // Normal argument
- }
- @Override public RegMask outregmap() { return null; }
-
- @Override public void encoding( Encoding enc ) {
- // Needs a register, typically a jump-and-link-register opcode
- // blr
- short self = enc.reg(this);
- enc.add4(arm.blr(arm.OP_CALLRARM, self));
- }
-
- @Override public void asm(CodeGen code, SB sb) {
- sb.p(code.reg(fptr())).p(" ");
- for( int i=0; i<nargs(); i++ )
- sb.p(code.reg(arg(i))).p(" ");
- sb.unchar(2);
- }
-
-}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/CmpARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/CmpARM.java
deleted file mode 100644
index 794ff0c9..00000000
--- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/CmpARM.java
+++ /dev/null
@@ -1,22 +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 CmpARM extends MachConcreteNode implements MachNode {
- CmpARM( Node cmp ) { super(cmp); }
- @Override public String op() { return "cmp"; }
- @Override public RegMask regmap(int i) { return arm.RMASK; }
- @Override public RegMask outregmap() { return arm.FLAGS_MASK; }
-
- // SUBS (shifted register)
- @Override public void encoding( Encoding enc ) { arm.r_reg_subs(enc,this,arm.OP_CMP); }
-
- // General form: "cmp rs1, rs2"
- @Override public void asm(CodeGen code, SB sb) {
- String dst = code.reg(this);
- if( dst!="flags" ) sb.p(dst).p(" = ");
- sb.p(code.reg(in(1))).p(", ").p(code.reg(in(2)));
- }
-}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/CmpFARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/CmpFARM.java
deleted file mode 100644
index 67ffc27b..00000000
--- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/CmpFARM.java
+++ /dev/null
@@ -1,22 +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;
-
-// compare instruction on float regs input(D-registers)
-public class CmpFARM extends MachConcreteNode implements MachNode{
- CmpFARM(Node cmp) {super(cmp);}
- @Override public String op() { return "cmpf"; }
- @Override public RegMask regmap(int i) { return arm.DMASK; }
- @Override public RegMask outregmap() { return arm.FLAGS_MASK; }
-
- // FCMP
- @Override public void encoding( Encoding enc ) { arm.f_cmp(enc,this); }
- // General form: "cmp d0, d1"
- @Override public void asm(CodeGen code, SB sb) {
- String dst = code.reg(this);
- if( dst!="flags" ) sb.p(dst).p(" = ");
- sb.p(code.reg(in(1))).p(", ").p(code.reg(in(2)));
- }
-}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/CmpIARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/CmpIARM.java
deleted file mode 100644
index f84a1649..00000000
--- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/CmpIARM.java
+++ /dev/null
@@ -1,40 +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;
-
-// Compare with immediate.
-// CMP (immediate) - SUBS
-public class CmpIARM extends MachConcreteNode implements MachNode {
- final int _imm;
- final String _bop;
- CmpIARM(BoolNode bool, int imm) {
- super(bool);
- _inputs.pop();
- _bop = bool.op();
- _imm = imm;
- }
- CmpIARM( CmpIARM cmp ) {
- super((Node)null);
- addDef(null);
- addDef(cmp.in(1));
- _bop = cmp._bop;
- _imm = cmp._imm;
- }
- @Override public String op() { return _imm==0 ? "test" : "cmp"; }
- @Override public RegMask regmap(int i) { return arm.RMASK; }
- @Override public RegMask outregmap() { return arm.FLAGS_MASK; }
- @Override public boolean isClone() { return true; }
- @Override public Node copy() { return new CmpIARM(this); }
-
- @Override public void encoding( Encoding enc ) { arm.imm_inst_subs(enc,in(1),in(1), arm.OPI_CMP,_imm); }
-
- // General form: "cmp rs1, 1"
- @Override public void asm(CodeGen code, SB sb) {
- String dst = code.reg(this);
- if( dst!="flags" ) sb.p(dst).p(" = ");
- sb.p(code.reg(in(1)));
- if( _imm != 0 ) sb.p(", #").p(_imm);
- }
- }
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/DivARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/DivARM.java
deleted file mode 100644
index de254eeb..00000000
--- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/DivARM.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 DivARM extends MachConcreteNode implements MachNode {
- DivARM( Node div ) { super(div); }
- @Override public String op() { return "div"; }
- @Override public RegMask regmap(int i) { return arm.RMASK; }
- @Override public RegMask outregmap() { return arm.WMASK; }
- // SDIV
- @Override public void encoding( Encoding enc ) { arm.madd(enc,this,arm.OP_DIV,3); }
- // General form: "div dst /= src"
- @Override public void asm(CodeGen code, SB sb) {
- sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" / ").p(code.reg(in(2)));
- }
-
-}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/DivFARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/DivFARM.java
deleted file mode 100644
index 813bebe4..00000000
--- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/DivFARM.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 DivFARM extends MachConcreteNode implements MachNode {
- DivFARM( Node divf) { super(divf); }
- @Override public String op() { return "divf"; }
- @Override public RegMask regmap(int i) { return arm.DMASK; }
- @Override public RegMask outregmap() { return arm.DMASK; }
-
- // FDIV (scalar)
- @Override public void encoding( Encoding enc ) { arm.f_scalar(enc,this,arm.OPF_DIV); }
- // General form: "VDIF = 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/arm/FloatARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/FloatARM.java
deleted file mode 100644
index b47b65ad..00000000
--- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/FloatARM.java
+++ /dev/null
@@ -1,43 +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.TypeFloat;
-import com.seaofnodes.simple.util.SB;
-import com.seaofnodes.simple.util.Utils;
-
-//FMOV (scalar, immediate)
-//Floating-point move immediate.
-public class FloatARM extends ConstantNode implements MachNode, RIPRelSize {
- FloatARM(ConstantNode con) { super(con); }
- @Override public String op() { return "ld8"; }
- @Override public RegMask regmap(int i) { return null; }
- @Override public RegMask outregmap() { return arm.DMASK; }
- @Override public boolean isClone() { return true; }
- @Override public FloatARM copy() { return new FloatARM(this); }
-
- @Override public void encoding( Encoding enc ) {
- enc.largeConstant(this,_con,0,-1/*TODO: ARM-style ELF patching*/);
- short dst = (short)(enc.reg(this) - arm.D_OFFSET);
- enc.add4(arm.load_pc(arm.OPF_ARM, 0, dst));
- }
-
- // Delta is from opcode start.
- @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 dst = (short)(enc.reg(this) - arm.D_OFFSET);
- enc.patch4(opStart, arm.load_pc(arm.OPF_ARM, delta, dst));
- }
-
- // Human-readable form appended to the SB. Things like the encoding,
- // indentation, leading address or block labels not printed here.
- // Just something like "ld4\tR17=[R18+12] // Load array base".
- // General form: "op\tdst=src+src"
- @Override public void asm(CodeGen code, SB sb) {
- _con.print(sb.p(code.reg(this)).p(" = #"));
- }
-
-}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/FunARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/FunARM.java
deleted file mode 100644
index 44a417f9..00000000
--- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/FunARM.java
+++ /dev/null
@@ -1,22 +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.MachNode;
-import com.seaofnodes.simple.util.SB;
-import com.seaofnodes.simple.util.Utils;
-
-public class FunARM extends FunNode implements MachNode {
- FunARM(FunNode fun) { super(fun); }
- @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(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 <Xd>, <Xn>, #(-<shift> MOD 64), #(63-<shift>)
- // immr must be (-<shift> 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<<logsize) unsigned
- final int _imm; // Limit ? bits
- final char _sz = (char)('0'+(1<<_declaredType.log_size()));
- MemOpARM(MemOpNode mop, Node ptr, Node idx, int off, int imm) {
- super(mop,mop);
- assert ptr._type instanceof TypeMemPtr && !(ptr instanceof AddNode);
- assert ptr() == ptr;
- _inputs.setX(2, ptr);
- _inputs.setX(3, idx);
- _off = off;
- _imm = imm;
- }
-
- // Store-based flavors have a value edge
- MemOpARM( MemOpNode mop, Node ptr, Node idx, int off, int imm, Node val ) {
- this(mop,ptr,idx,off,imm);
- _inputs.setX(4,val);
- }
-
- @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(); }
-
- int size() { return 1<<_declaredType.log_size(); }
-
- // Wider mask to store both GPRs and FPRs
- @Override public RegMask regmap(int i) {
- // 0 - ctrl
- if( i==1 ) return null; // memory
- if( i==2 ) return arm.RMASK; // ptr/base
- if( i==3 ) return arm.RMASK; // off/index
- if( i==4 ) return arm.RMASK; // value
- return null; // Anti-dependence
- }
-
- // Shared encoding for loads and stores(int and float)
- public void ldst_encode( Encoding enc, int opcode_imm, int opcode_reg, Node xval, int size) {
- short ptr = enc.reg(ptr());
- short off = enc.reg(off());
- short val = enc.reg(xval);
- int body = off() == null
- ? arm.load_str_imm(opcode_imm, _off, ptr, val, size)
- : arm.indr_adr(opcode_reg, off, arm.STORE_LOAD_OPTION.SXTX, 0, ptr, val);
- enc.add4(body);
- }
-
- SB asm_address(CodeGen code, SB sb) {
- sb.p("[").p(code.reg(ptr())).p("+");
- if( off() != null ) sb.p(code.reg(off()));
- else sb.p(_off);
- return sb.p("]");
- }
-}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/MulARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/MulARM.java
deleted file mode 100644
index a23ec75b..00000000
--- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/MulARM.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;
-
-// mulh signed multiply instruction(no-imm form)
-public class MulARM extends MachConcreteNode implements MachNode {
- MulARM(Node mul) {super(mul);}
- @Override public String op() { return "mul"; }
- @Override public RegMask regmap(int i) { return arm.RMASK; }
- @Override public RegMask outregmap() { return arm.WMASK; }
-
- @Override public void encoding( Encoding enc ) { arm.madd(enc,this,arm.OP_MUL,31); }
- // General form: "rd = rs1 * rs2"
- @Override public void asm(CodeGen code, SB sb) {
- sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" * ").p(code.reg(in(2)));
- }
-}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/MulFARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/MulFARM.java
deleted file mode 100644
index 73ebbf00..00000000
--- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/MulFARM.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 MulFARM extends MachConcreteNode implements MachNode{
- MulFARM(Node mulf) {super(mulf);}
- @Override public String op() { return "mulf"; }
- @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_MUL); }
- // Default on double precision for now(64 bits)
- // General form: "VMUL.f32 rd = src1 * src2
- @Override public void asm(CodeGen code, SB sb) {
- sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" * ").p(code.reg(in(2)));
- }
-}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/NegARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/NegARM.java
deleted file mode 100644
index 881b41f9..00000000
--- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/NegARM.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.node.MachConcreteNode;
-import com.seaofnodes.simple.type.TypeInteger;
-import com.seaofnodes.simple.util.SB;
-
-public class NegARM extends MachConcreteNode implements MachNode {
- NegARM(Node sub) {
- super(sub);
- //_inputs.pop();
- }
- @Override public String op() { return "neg"; }
- @Override public RegMask regmap(int i) { return arm.RMASK; }
- @Override public RegMask outregmap() { return arm.WMASK; }
- @Override public void encoding( Encoding enc ) {
- // reverse subtract with immediate 0
- arm.imm_inst(enc,this, in(1), 0b00100110, 0);
- }
- // 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)));
- }
-}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/NewARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/NewARM.java
deleted file mode 100644
index c1aad15f..00000000
--- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/NewARM.java
+++ /dev/null
@@ -1,37 +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;
-
-public class NewARM extends NewNode implements MachNode, RIPRelSize {
- // A pre-zeroed chunk of memory.
- NewARM(NewNode nnn) { super(nnn); }
- @Override public void encoding( Encoding enc ) {
- // BL(branch with link)
- enc.external(this,"calloc").
- add4(arm.mov(arm.OP_MOVZ, 0, 1, 0)). // movz x0,#1
- add4(arm.b(arm.OP_CALL, 0));
- }
-
- // Patch is for running "new" in a JIT.
- // Delta is from opcode start
- @Override public byte encSize(int delta) { return 4*2; }
-
- // 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 ) {
- enc.patch4(opStart+4, arm.b_calloc(arm.OP_CALL, delta-4));
- } else {
- throw Utils.TODO();
- }
- }
- // General form: "alloc #bytes PC"
- @Override public void asm(CodeGen code, SB sb) {
- sb.p("ldi x0=#1\n");
- sb.p("call #calloc");
- }
-}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/NotARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/NotARM.java
deleted file mode 100644
index 027a8128..00000000
--- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/NotARM.java
+++ /dev/null
@@ -1,27 +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.util.SB;
-
-public class NotARM extends MachConcreteNode implements MachNode{
- NotARM(NotNode not) {super(not);}
- @Override public String op() { return "not"; }
- @Override public RegMask regmap(int i) { return arm.RMASK; }
- @Override public RegMask outregmap() { return arm.WMASK; }
- @Override public RegMask killmap() { return arm.FLAGS_MASK; }
- @Override public void encoding( Encoding enc ) {
- // subs xzr, rs, #0
- // cset rd, eq // Set rd to 1 if rs == 0 (equal), else 0
- // subtracting zero from rs will just yield rs, it sets the zero flag and then it's used in cset
- short self = enc.reg(this );
- short reg1 = enc.reg(in(1));
- int subs = arm.imm_inst(arm.OP_SUBS, 0, reg1, self);
- enc.add4(subs);
- int cset = arm.cond_set(arm.OP_CSET, 31, arm.COND.EQ, 63, reg1);
- enc.add4(cset);
- }
-
- @Override public void asm(CodeGen code, SB sb) { sb.p(code.reg(this)); }
-}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/OrARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/OrARM.java
deleted file mode 100644
index e8245749..00000000
--- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/OrARM.java
+++ /dev/null
@@ -1,20 +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;
-
-// Not using the ORRS variant.
-public class OrARM extends MachConcreteNode implements MachNode {
- OrARM(Node and) { super(and); }
- @Override public String op() { return "or"; }
- @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_OR); }
- // General form: #rd = rs1 & rs2
- @Override public void asm(CodeGen code, SB sb) {
- sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" | ").p(code.reg(in(2)));
- }
-
-}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/OrIARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/OrIARM.java
deleted file mode 100644
index a48caa51..00000000
--- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/OrIARM.java
+++ /dev/null
@@ -1,24 +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 OrIARM extends MachConcreteNode implements MachNode {
- final int _imm;
- OrIARM(Node or, int imm) {
- super(or);
- _inputs.pop();
- _imm = imm;
- }
- @Override public String op() { return "ori"; }
- @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_n(enc, this, in(1), arm.OPI_OR, _imm);
- }
- // General form: "ori 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/ParmARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/ParmARM.java
deleted file mode 100644
index b419fe0a..00000000
--- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/ParmARM.java
+++ /dev/null
@@ -1,17 +0,0 @@
-package com.seaofnodes.simple.node.cpus.arm;
-
-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 ParmARM extends ParmNode implements MachNode {
- final RegMask _rmask;
- ParmARM(ParmNode parm) {
- super(parm);
- _rmask = arm.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/arm/ProjARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/ProjARM.java
deleted file mode 100644
index 4b7d6f13..00000000
--- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/ProjARM.java
+++ /dev/null
@@ -1,15 +0,0 @@
-package com.seaofnodes.simple.node.cpus.arm;
-
-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 ProjARM extends ProjNode implements MachNode {
- ProjARM( 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/arm/RetARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/RetARM.java
deleted file mode 100644
index 2136ebf5..00000000
--- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/RetARM.java
+++ /dev/null
@@ -1,15 +0,0 @@
-package com.seaofnodes.simple.node.cpus.arm;
-
-import com.seaofnodes.simple.*;
-import com.seaofnodes.simple.codegen.*;
-import com.seaofnodes.simple.node.*;
-
-public class RetARM extends ReturnNode implements MachNode {
- RetARM(ReturnNode ret, FunNode fun) { super(ret, fun); fun.setRet(this); }
- @Override public void encoding( Encoding enc ) {
- int frameAdjust = ((FunARM)fun())._frameAdjust;
- if( frameAdjust > 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<<D0;
- static final RegMask DMASK = new RegMask(FP_BITS);
-
- // Load/store mask; both GPR and FPR
- static final RegMask MEM_MASK = new RegMask(WR_BITS | FP_BITS);
-
- static final RegMask SPLIT_MASK = new RegMask(WR_BITS | FP_BITS, -2L/*skip flags*/);
-
- static final RegMask FLAGS_MASK = new RegMask(FLAGS);
-
- // Arguments masks
- static final RegMask X0_MASK = new RegMask(X0);
- static final RegMask X1_MASK = new RegMask(X1);
- static final RegMask X2_MASK = new RegMask(X2);
- static final RegMask X3_MASK = new RegMask(X3);
- static final RegMask X4_MASK = new RegMask(X4);
- static final RegMask X5_MASK = new RegMask(X5);
- static final RegMask X6_MASK = new RegMask(X6);
- static final RegMask X7_MASK = new RegMask(X7);
-
- // Arguments(float) masks
- static final RegMask D0_MASK = new RegMask(D0);
- static final RegMask D1_MASK = new RegMask(D1);
- static final RegMask D2_MASK = new RegMask(D2);
- static final RegMask D3_MASK = new RegMask(D3);
- static final RegMask D4_MASK = new RegMask(D4);
- static final RegMask D5_MASK = new RegMask(D5);
- static final RegMask D6_MASK = new RegMask(D6);
- static final RegMask D7_MASK = new RegMask(D7);
-
- // major opcode: OP
- public static int OP_ADD = 0b10_001_011;
- public static int OPF_ADD = 0b00_011_110;
- public static int OPF_OP_ADD = 0b00_101_0;
- public static int OPI_ADD = 0b10_010_00100;
-
- public static int OPI_SUB = 0b11_010_00100;
- public static int OP_UJMP = 0b000101;
-
- public static int OP_ADRP = 0b10000;
-
- public static int OP_ASR = 0b1010;
- public static int OPI_ASR = 0b10_0100_1101;
-
- public static int OP_LSL = 0b1000;
- public static int OPI_LSL =0b1101001101;
-
- public static int OP_LSR = 0b1001;
- public static int OPI_LSR = 0b1101001101;
-
- public static int OP_BRANCH = 0b01_0101_00;
- public static int OP_CALL = 0b10_010_1;
-
- public static int OP_CALLRARM = 0b1101011000111111000000;
-
- public static int OP_SUBS = 0b1111000100;
- public static int OP_CSET = 0b10011010100;
- public static int OP_CMP = 0b11101011;
- public static int OPI_CMP = 0b1111000100;
-
- public static int OP_DIV = 0b10011010110;
- public static int OPF_DIV = 0b000110;
-
- public static int OP_MUL = 0b10011011000;
- public static int OPF_MUL = 0b10;
-
- public static int OPF_ARM = 0b01011100;
-
- public static int OP_FLOAT_C = 0b10011110;
-
- public static int OP_XOR = 0b11001010;
- public static int OPI_XOR = 0b110100100;
-
- public static int OP_AND = 0b10_0010_10;
- public static int OPI_AND = 0b10_0100_100;
-
- public static int OP_OR = 0b10101010;
- public static int OPI_OR = 0b101100100;
-
- public static int OP_SUB = 0b11001011;
- public static int OPF_SUB = 0b1110;
-
- public static int OP_MOVK = 0b111100101;
- public static int OP_MOVN = 0b100100101;
- public static int OP_MOVZ = 0b110100101;
-
- public static int OP_RET = 0b1101011001011111000000;
- // Store/load opcodes
- public static int OP_LOAD_R_64 = 0b11111000011;
- public static int OP_LOAD_R_32 = 0b10111000011;
- public static int OP_LOAD_R_16 = 0b01111000010;
- public static int OP_LOAD_R_8 = 0b00111000011;
-
- public static int OP_LOAD_IMM_64 = 0b1111100101;
- public static int OP_LOAD_IMM_32 = 0b1011100101;
- public static int OP_LOAD_IMM_16 = 0b0111100101;
- public static int OP_LOAD_IMM_8 = 0b0011100101;
-
- // load
- public static int OPF_LOAD_R_64 = 0b11111100011;
- public static int OPF_LOAD_R_32 = 0b10111100011;
-
- public static int OPF_LOAD_IMM_64 = 0b1111110101;
- public static int OPF_LOAD_IMM_32 = 0b1011110101;
-
- public static int OP_STORE_R_64 = 0b11111000001;
- public static int OP_STORE_R_32 = 0b10111000001;
- public static int OP_STORE_R_16 = 0b01111000001;
- public static int OP_STORE_R_8 = 0b00111000001;
-
- public static int OP_STORE_IMM_64 = 0b1111100100;
- public static int OP_STORE_IMM_32 = 0b1011100100;
- public static int OP_STORE_IMM_16 = 0b0111100100;
- public static int OP_STORE_IMM_8 = 0b0011100100;
-
- public static int OPF_STORE_R_64 = 0b11111100001;
- public static int OPF_STORE_R_32 = 0b10111100001;
-
- public static int OPF_STORE_IMM_32 = 0b1111110100;
- public static int OPF_STORE_IMM_64 = 0b1111110100;
-
-
- public static int OP_FMOV = 0b10011110;
- public static int OP_FMOV_REG = 0b00011110;
- // https://docsmirror.github.io/A64/2023-06/mov_orr_log_shift.html
- public static int OP_MOV = 0b10101010000;
- // Calling convention; returns a machine-specific register
- // for incoming argument idx.
- // index 0 for control, 1 for memory, real args start at index 2
- static final RegMask[] CALLINMASK = new RegMask[] {
- X0_MASK,
- X1_MASK,
- X2_MASK,
- X3_MASK,
- X4_MASK,
- X5_MASK,
- X6_MASK,
- X7_MASK,
- };
- static final RegMask[] XMMS = new RegMask[] {
- D0_MASK,
- D1_MASK,
- D2_MASK,
- D3_MASK,
- D4_MASK,
- D5_MASK,
- D6_MASK,
- D7_MASK,
- };
-
- // ARM ENCODING
- public enum OPTION {
- UXTB,
- UXTH,
- UXTX,
- SXTB,
- SXTH,
- SXTW,
- SXTX,
- }
-
- static public int cset(int opcode, int rm, COND cond, int rn, int rd) {
- assert 0 <= rm && rm < 32;
- assert 0 <= rn && rn < 32;
- assert 0 <= rd && rd < 32;
- return (opcode << 21) | (rm << 15) | (cond.ordinal() << 12) | (rn << 5) | rd;
- }
-
- static public int cset(int opcode, COND cond, int rn, int rd) {
- assert 0 <= rn && rn < 32;
- assert 0 <= rd && rd < 32;
- return (opcode << 16) | (cond.ordinal() << 12) | (rn << 5) | rd;
- }
-
- public enum STORE_LOAD_OPTION {
- UXTW, // base+ u32 index [<< logsize]
- UXTX, // base+ u64 index [<< logsize]
- SXTW, // base+ s32 index [<< logsize]
- SXTX, // base+ s64 index [<< logsize]
- }
-
- public enum COND {
- EQ,
- NE,
- CS,
- CC,
- MI,
- PL,
- VS,
- VC,
- HI,
- LS,
- GE,
- LT,
- GT,
- LE,
- AL,
- NV
- }
-
- // True if signed 9-bit immediate
- private static boolean imm9(TypeInteger ti) {
- // 55 = 64-9
- return ti.isConstant() && ((ti.value()<<55)>>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)-1)) == (pattern >> 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<<imms)-1) return -1;
- imms--;
- if (size == 64) return 0x1000 | immr << 6 | imms;
- return (32-size)<<1 | immr << 6 | imms;
- }
-
- // sh is encoded in opcode
- public static int imm_inst(int opcode, int imm12, int rn, int rd) {
- assert 0 <= rn && rn < 32;
- assert 0 <= rd && rd < 32;
- assert opcode >=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
- // <Qd>, <Qm>
- // F32.S32
- //encoded as op = 0b00, size = 0b10.
- // VCVT<c>.<Td>.<Tm> <Dd>, <Dm>
- // 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<idx; i++ )
- if( tfp.arg(i-2) instanceof TypeFloat)
- fcnt++;
- // Floats up to XMMS in XMM registers
- if( tfp.arg(idx-2) instanceof TypeFloat ) {
- if( fcnt < XMMS.length )
- return XMMS[fcnt];
- } else {
- RegMask[] cargs = CALLINMASK;
- if( idx-2-fcnt < cargs.length )
- return cargs[idx-2-fcnt];
- }
- throw Utils.TODO(); // Pass on stack slot
- }
-
- public static long decodeImm12(int imm12) {
- int immr = (imm12 >> 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<tfp.nargs(); i++ ) {
- if( tfp.arg(i) instanceof TypeFloat ) fcnt++;
- else icnt++;
- }
- int nstk = Math.max(icnt-8,0)+Math.max(fcnt-8,0);
- return (short)nstk;
- }
-
- private static final long CALLEE_SAVE =
- 1L<<X19 |
- 1L<<X20 | 1L<<X21 | 1L<<X22 | 1L<<X23 |
- 1L<<X24 | 1L<<X25 | 1L<<X26 | 1L<<X27 |
- 1L<<X28 |
- 1L<<D9 | 1L<<D10 | 1L<<D11 |
- 1L<<D12 | 1L<<D13 | 1L<<D14 | 1L<<D15;
- static final long CALLER_SAVE = ~CALLEE_SAVE & ~(1L<<RSP);
- @Override public long callerSave() { return CALLER_SAVE; }
- @Override public long neverSave() { return 1L<<RSP; }
- @Override public RegMask retMask( TypeFunPtr tfp ) {
- return tfp.ret() instanceof TypeFloat ? D0_MASK : X0_MASK;
- }
- @Override public int rpc() { return X30; }
-
- // Create a split op; any register to any register, including stack slots
- @Override public SplitNode split(String kind, byte round, LRG lrg) { return new SplitARM(kind,round); }
-
- // Return a MachNode unconditional branch
- @Override public CFGNode jump() {
- return new UJmpARM();
- }
-
- // Instruction selection
- @Override public Node instSelect(Node n ) {
- return switch( n ) {
- case AddFNode addf -> 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 extends MemOpNode> 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<nargs(); i++ )
- sb.p(code.reg(arg(i+2))).p(", ");
- sb.unchar(2).p(" ").p(code.reg(fptr()));
-
- }
-}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CallRRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CallRRISC.java
deleted file mode 100644
index ac36ed12..00000000
--- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CallRRISC.java
+++ /dev/null
@@ -1,31 +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 CallRRISC extends CallNode implements MachNode {
- CallRRISC( 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-1
- ? riscv.RMASK // Function call target
- : riscv.callInMask(tfp(),i,fun()._maxArgSlot); // Normal argument
- }
- @Override public RegMask outregmap() { return null; }
-
- @Override public void encoding( Encoding enc ) {
- short rpc = enc.reg(this);
- short src = enc.reg(in(_inputs._len-1));
- int body = riscv.i_type(riscv.OP_JALR, rpc, 0, src, 0);
- enc.add4(body);
- }
-
- @Override public void asm(CodeGen code, SB sb) {
- sb.p(code.reg(fptr())).p(" ");
- for( int i=0; i<nargs(); i++ )
- sb.p(code.reg(arg(i))).p(" ");
- sb.unchar(2);
- }
-}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/DivFRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/DivFRISC.java
deleted file mode 100644
index 68ed6f6c..00000000
--- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/DivFRISC.java
+++ /dev/null
@@ -1,19 +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;
-
-// fdiv.d
-public class DivFRISC extends MachConcreteNode implements MachNode{
- DivFRISC(Node divf) {super(divf);}
- @Override public String op() { return "divf"; }
- @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.RUP,13); }
- // Default on double precision for now(64 bits)
- // General form: "fdiv.d rd = src1 / src2
- @Override public void asm(CodeGen code, SB sb) {
- sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" / ").p(code.reg(in(2)));
- }
-}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/DivRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/DivRISC.java
deleted file mode 100644
index 515ffa47..00000000
--- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/DivRISC.java
+++ /dev/null
@@ -1,17 +0,0 @@
-package com.seaofnodes.simple.node.cpus.riscv;
-
-import com.seaofnodes.simple.util.SB;
-import com.seaofnodes.simple.codegen.*;
-import com.seaofnodes.simple.node.*;
-
-public class DivRISC extends MachConcreteNode implements MachNode{
- public DivRISC(Node div) {super(div);}
- @Override public String op() { return "div"; }
- @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,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/FltRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/FltRISC.java
deleted file mode 100644
index 81726200..00000000
--- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/FltRISC.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.type.TypeFloat;
-import com.seaofnodes.simple.util.SB;
-import com.seaofnodes.simple.util.Utils;
-
-public class FltRISC extends ConstantNode implements MachNode, RIPRelSize {
- FltRISC(ConstantNode con) { super(con); }
- @Override public String op() { return "ld8"; }
- @Override public RegMask regmap(int i) { return null; }
- @Override public RegMask outregmap() { return riscv.FMASK; }
- @Override public boolean isClone() { return true; }
- @Override public FltRISC copy() { return new FltRISC(this); }
-
- @Override public void encoding( Encoding enc ) {
- enc.largeConstant(this,_con, 0, -1);
- short dst = (short)(enc.reg(this) - riscv.F_OFFSET);
- short tmp = (short)riscv.T6;
- // 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_LOADFP, dst, 0b11, tmp, 0));
- }
- @Override public RegMask killmap() { return new RegMask(riscv.T6); }
-
- // Delta is from opcode start.
- @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 = (short)(enc.reg(this) - riscv.F_OFFSET);
- 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_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<<F0;
- static final RegMask FMASK = new RegMask(FP_BITS);
-
- // Load/store mask; both GPR and FPR
- static final RegMask MEM_MASK = new RegMask(WR_BITS | FP_BITS);
-
- static final RegMask SPLIT_MASK = new RegMask( WR_BITS | FP_BITS, -1L );
-
-
- // Arguments masks
- public static RegMask A0_MASK = new RegMask(A0);
- public static RegMask A1_MASK = new RegMask(A1);
- public static RegMask A2_MASK = new RegMask(A2);
- public static RegMask A3_MASK = new RegMask(A3);
- public static RegMask A4_MASK = new RegMask(A4);
- public static RegMask A5_MASK = new RegMask(A5);
- public static RegMask A6_MASK = new RegMask(A6);
- public static RegMask A7_MASK = new RegMask(A7);
-
- public static final RegMask RPC_MASK = new RegMask(RPC);
-
- // Float arguments masks
- public static RegMask FA0_MASK = new RegMask(FA0);
- public static RegMask FA1_MASK = new RegMask(FA1);
- public static RegMask FA2_MASK = new RegMask(FA2);
- public static RegMask FA3_MASK = new RegMask(FA3);
- public static RegMask FA4_MASK = new RegMask(FA4);
- public static RegMask FA5_MASK = new RegMask(FA5);
- public static RegMask FA6_MASK = new RegMask(FA6);
- public static RegMask FA7_MASK = new RegMask(FA7);
-
- // Int arguments calling conv
- static RegMask[] CALLINMASK = new RegMask[] {
- A0_MASK,
- A1_MASK,
- A2_MASK,
- A3_MASK,
- A4_MASK,
- A5_MASK,
- A6_MASK,
- A7_MASK
- };
-
- static RegMask[] XMMS = new RegMask[] {
- FA0_MASK,
- FA1_MASK,
- FA2_MASK,
- FA3_MASK,
- FA4_MASK,
- FA5_MASK,
- FA6_MASK,
- FA7_MASK
- };
-
- // major opcode: OP
- public static int OP_LOAD = 0b00_000_11;
- public static int OP_LOADFP = 0b00_001_11;
- public static int OP_CUSTOM0 = 0b00_010_11;
- public static int OP_IMM = 0b00_100_11;
- public static int OP_AUIPC = 0b00_101_11;
-
- public static int OP_STORE = 0b01_000_11;
- public static int OP_STOREFP = 0b01_001_11;
- public static int OP_CUSTOM1 = 0b01_010_11;
- public static int OP = 0b01_100_11;
- public static int OP_LUI = 0b01_101_11;
-
- public static int OP_CUSTOM2 = 0b10_010_11;
- public static int OP_FP = 0b10_100_11;
-
- public static int OP_BRANCH = 0b11_000_11;
- public static int OP_JALR = 0b11_001_11;
- public static int OP_RESERVED= 0b11_010_11;
- public static int OP_JAL = 0b11_011_11;
-
-
- // Since riscv instructions are fixed we can just or them together
- public static int r_type(int opcode, int rd, int func3, int rs1, int rs2, int func7) {
- assert 0 <= rs1 && rs1 < 32;
- assert 0 <= rs2 && rs2 < 32;
- assert 0 <= rd && rd < 32;
- return (func7 << 25) | (rs2 << 20) | (rs1 << 15) | (func3 << 12) | (rd << 7) | opcode;
- }
- public static void r_type(Encoding enc, Node n, int func3, int func7) {
- short dst = enc.reg(n);
- short src1 = enc.reg(n.in(1));
- short src2 = enc.reg(n.in(2));
- int body = r_type(OP,dst,func3,src1,src2,func7);
- enc.add4(body);
- }
-
- public static void rf_type(Encoding enc, Node n, RM func3, int func7) {
- short dst = (short)(enc.reg(n )-F_OFFSET);
- short src1 = (short)(enc.reg(n.in(1))-F_OFFSET);
- short src2 = (short)(enc.reg(n.in(2))-F_OFFSET);
- int body = r_type(OP_FP,dst,func3.ordinal(),src1,src2,func7);
- enc.add4(body);
- }
-
-
- public static int u_type(int opcode, int rd, int imm20) {
- assert 0 <= rd && rd < 32;
- return (imm20 << 12) | (rd << 7) | opcode;
- }
-
- public static int j_type(int opcode, int rd, int delta) {
- assert -(1L<<20) <= delta && delta < (1L<<20);
- assert 0 <= rd && rd < 32;
- // Messy branch offset encoding
- // 31 30-21 20 19-12 11-7 6-0
- // 20 10- 1 11 19-12 rpc JAL
- assert (delta&1)==0; // Low bit is always zero, not encoded
- int imm10_01 = (delta>> 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; i<idx; i++ )
- if( tfp.arg(i-2) instanceof TypeFloat )
- fcnt++;
- // Floats up to XMMS in XMM registers
- if( tfp.arg(idx-2) instanceof TypeFloat ) {
- if( fcnt < XMMS.length )
- return XMMS[fcnt];
- } else {
- RegMask[] cargs = CALLINMASK;
- if( idx-2-fcnt < cargs.length )
- return cargs[idx-2-fcnt];
- }
- // Pass on stack slot(8 and higher)
- if( maxArgSlot>0 ) 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<tfp.nargs(); i++ ) {
- if( tfp.arg(i) instanceof TypeFloat ) fcnt++;
- else icnt++;
- }
- int nstk = Math.max(icnt-8,0)+Math.max(fcnt-8,0);
- return (short)nstk;
- }
-
- static final long CALLER_SAVE =
- (1L<<RPC) |
- (1L<<A0 ) | (1L<<A1 ) | (1L<<A2 ) | (1L<<A3 ) | (1L<<A4 ) | (1L<<A5 ) | (1L<<A6 ) | (1L<<A7 ) |
- (1L<<T0 ) | (1L<<T1 ) | (1L<<T2 ) | (1L<<T3 ) | (1L<<T4 ) | (1L<<T5 ) | (1L<<T6 ) |
- (1L<<FA0) | (1L<<FA1) | (1L<<FA2) | (1L<<FA3) | (1L<<FA4) | (1L<<FA5) | (1L<<FA6) | (1L<<FA7) |
- (1L<<F0 ) | (1L<<F1 ) | (1L<<F2 ) | (1L<<F3 ) | (1L<<F4 ) | (1L<<F5 ) | (1L<<F6 ) | (1L<<F7 ) |
- (1L<<FT8) | (1L<<FT9) | (1L<<FT10)| (1L<<FT11);
-
- @Override public long callerSave() { return CALLER_SAVE; }
- @Override public long neverSave() { return (1L<<RSP) | (1L<<ZERO); }
- @Override public RegMask retMask( TypeFunPtr tfp ) {
- return tfp.ret() instanceof TypeFloat ? FA0_MASK : A0_MASK;
- }
- @Override public int rpc() { return RPC; }
-
-
- // Return a MachNode unconditional branch
- @Override public CFGNode jump() { return new UJmpRISC(); }
-
- // Create a split op; any register to any register, including stack slots
- @Override public SplitNode split(String kind, byte round, LRG lrg) { return new SplitRISC(kind,round); }
-
- // True if signed 12-bit immediate
- public static boolean imm12(TypeInteger ti) {
- // 52 = 64-12
- return ti.isConstant() && ((ti.value()<<52)>>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 <u are implemented in hardware.
- // x < y - as-is
- // x <= y - flip and negate; !(y < x); `slt tmp=y,x; xori dst=tmp,#1`
- // x == y - sub and vs0 == `sub tmp=x-y; sltu dst=tmp,#1`
-
- // x > 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<nargs(); i++ )
- sb.p(code.reg(arg(i))).p(" ");
- sb.unchar(2);
- }
-
-}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CallX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CallX86.java
deleted file mode 100644
index ffdce7df..00000000
--- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CallX86.java
+++ /dev/null
@@ -1,48 +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 CallX86 extends CallNode implements MachNode, RIPRelSize {
- final TypeFunPtr _tfp;
- final String _name;
- CallX86( 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 x86_64_v2.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);
- enc.add1(0xe8).add4(0);
- }
-
- // Delta is from opcode start, but X86 measures from the end of the 5-byte encoding
- @Override public byte encSize(int delta) { return 5; }
-
- // Delta is from opcode start
- @Override public void patch( Encoding enc, int opStart, int opLen, int delta ) {
- enc.patch4(opStart+1, delta-5);
- }
-
- @Override public void asm(CodeGen code, SB sb) {
- sb.p(_name).p(" ");
- for( int i=0; i<nargs(); i++ )
- sb.p(code.reg(in(i+2))).p(" ");
- sb.unchar(2);
- }
-}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CmpFX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CmpFX86.java
deleted file mode 100644
index 5b97ef63..00000000
--- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CmpFX86.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 CmpFX86 extends MachConcreteNode implements MachNode {
- CmpFX86( Node cmp ) { super(cmp); }
- @Override public String op() { return "cmpf"; }
- @Override public RegMask regmap(int i) { return x86_64_v2.XMASK; }
- @Override public RegMask outregmap() { return x86_64_v2.FLAGS_MASK; }
-
- @Override public void encoding( Encoding enc ) {
- // UCOMISD
- short src1 = (short)(enc.reg(in(1)) - x86_64_v2.XMM_OFFSET);
- short src2 = (short)(enc.reg(in(2)) - x86_64_v2.XMM_OFFSET);
- // float opcode
- enc.add1(0x66);
- // rex prefix must come next (REX.W is not set)
- x86_64_v2.rexF(src1, src2, 0, false, enc);
-
- enc.add1(0x0F).add1(0x2E);
- enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, src1, src2));
- }
-
- // General form: "cmp src1,src2"
- @Override public void asm(CodeGen code, SB sb) {
- String dst = code.reg(this);
- if( dst!="flags" ) sb.p(dst).p(" = ");
- sb.p(code.reg(in(1))).p(", ").p(code.reg(in(2)));
- }
-}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CmpIX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CmpIX86.java
deleted file mode 100644
index 8b8318eb..00000000
--- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CmpIX86.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;
-
-// Compare immediate. Sets flags.
-public class CmpIX86 extends MachConcreteNode implements MachNode {
- final int _imm;
- CmpIX86( Node cmp, int imm, boolean swap ) {
- super(cmp);
- _inputs.del(swap ? 1 : 2);
- _imm = imm;
- }
- CmpIX86( CmpIX86 cmp ) {
- super((Node)null);
- addDef(null);
- addDef(cmp.in(1));
- _imm = cmp._imm;
- }
-
- @Override public String op() { return _imm==0 ? "test" : "cmp"; }
- @Override public RegMask regmap(int i) { return x86_64_v2.RMASK; }
- @Override public RegMask outregmap() { return x86_64_v2.FLAGS_MASK; }
- @Override public boolean isClone() { return true; }
- @Override public Node copy() { return new CmpIX86(this); }
-
- @Override public final void encoding( Encoding enc ) {
- short dst = enc.reg(in(1)); // Also src1
- enc.add1(x86_64_v2.rex(0, dst, 0));
- // opcode; 0x81 or 0x83; 0x69 or 0x6B
- enc.add1( 0x81 + (x86_64_v2.imm8(_imm) ? 2 : 0) );
- enc.add1( x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, 7, 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) {
- String dst = code.reg(this);
- if( dst!="flags" ) sb.p(dst).p(" = ");
- sb.p(code.reg(in(1)));
- if( _imm != 0 ) sb.p(", #").p(_imm);
- }
-}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CmpMemX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CmpMemX86.java
deleted file mode 100644
index 11f900ed..00000000
--- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CmpMemX86.java
+++ /dev/null
@@ -1,78 +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.type.TypeFloat;
-import com.seaofnodes.simple.util.SB;
-
-public class CmpMemX86 extends MemOpX86 {
- final boolean _swap; // Op switched LHS, RHS
- CmpMemX86( BoolNode bool, LoadNode ld, Node base, Node idx, int off, int scale, int imm, Node val, boolean swap ) {
- super(bool,ld, base, idx, off, scale, imm, val );
- _swap = swap;
- assert !_swap || val() != null || bool.op()=="==" || bool.op()=="!="; // Cannot swap with immediate
- }
- @Override public String op() { return ((val()==null && _imm==0) ? "test" : "cmp") + _sz; }
- @Override public RegMask outregmap() { return x86_64_v2.FLAGS_MASK; }
- @Override public void encoding( Encoding enc ) {
- // REX.W + 81 /7 id CMP r/m64, imm32 | REX.W + 39 /r CMP r/m64,r64
- // CMP [mem], imm32
-
- short ptr = enc.reg(ptr());
- short idx = enc.reg(idx());
- short src = enc.reg(val());
-
- if(src == -1) {
- assert !_swap;
- int log = _declaredType.log_size();
- x86_64_v2.rexF(-1, ptr, idx, log==3, enc);
- switch(log) {
- case 0: enc.add1(0x80); break;
- case 1: enc.add1(0x66).add1(0x81); break;
- case 2: enc.add1(0x81); break;
- case 3: enc.add1(0x81); break;
- }
- x86_64_v2.indirectAdr(_scale, idx, ptr, _off, 7, enc);
- switch(log) {
- case 0: enc.add1(_imm); break;
- case 1: enc.add2(_imm); break;
- case 2: enc.add4(_imm); break;
- case 3: enc.add8(_imm); break;
- }
- } else {
- encVal(enc, _declaredType, ptr, idx, src, _off, _scale, _swap);
- }
- }
-
- static void encVal(Encoding enc, Type decl, short ptr, short idx, short src, int off, int scale, boolean _swap) {
- if(decl instanceof TypeFloat) {
- src -= (short)x86_64_v2.XMM_OFFSET;
- enc.add1(decl==TypeFloat.F32 ? 0xF3 : 0xF2).add1(0x0F).add1(0xC2);
- } else {
- int log = decl.log_size();
- x86_64_v2.rexF(src, ptr, idx, log == 3, enc);
- int swap = _swap ? 2 : 0;
- switch(log) {
- case 0: enc.add1(0x38+swap); break;
- case 1: enc.add1(0x66).add1(0x39+swap); break;
- case 2: enc.add1(0x39+swap); break;
- case 3: enc.add1(0x39+swap); break;
- }
- }
- x86_64_v2.indirectAdr(scale, idx, ptr, off, src, enc);
- }
-
- // General form: "cmp dst = src, [base + idx<<2 + 12]"
- @Override public void asm(CodeGen code, SB sb) {
- String dst = code.reg(this);
- if( dst!="flags" ) sb.p(dst).p(" = ");
- if( _swap ) asm_address(code,sb).p(",");
- if( val()==null ) {
- if( _imm!=0 ) sb.p("#").p(_imm);
- } else {
- sb.p(code.reg(val()));
- }
- if( !_swap ) asm_address(code,sb.p(", "));
- }
-}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CmpX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CmpX86.java
deleted file mode 100644
index e7305cdf..00000000
--- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CmpX86.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 CmpX86 extends MachConcreteNode implements MachNode {
- CmpX86( Node add ) { super(add); }
- @Override public String op() { return "cmp"; }
- @Override public RegMask regmap(int i) { return x86_64_v2.RMASK; }
- @Override public RegMask outregmap() { return x86_64_v2.FLAGS_MASK; }
- // This one is marginal: cloning a Cmp[reg,reg] means stretching the
- // lifetimes of 2 normal registers in exchange for shortening a flags
- // register lifetime. Spilling flags is (probably) relatively expensive
- // compared to some normal registers - and those normal registers might not
- // be spilling!
- @Override public boolean isClone() { return true; }
-
- @Override public void encoding( Encoding enc ) {
- short dst = enc.reg(in(1));
- short src = enc.reg(in(2));
-
- enc.add1(x86_64_v2.rex(dst, src, 0));
- enc.add1(0x3B);
- enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, dst, src));
- }
- @Override public void asm(CodeGen code, SB sb) {
- String dst = code.reg(this);
- if( dst!="flags" ) sb.p(dst).p(" = ");
- sb.p(code.reg(in(1))).p(", ").p(code.reg(in(2)));
- }
-}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/DivFX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/DivFX86.java
deleted file mode 100644
index 3427aea5..00000000
--- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/DivFX86.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 DivFX86 extends MachConcreteNode implements MachNode {
- DivFX86( Node divf) { super(divf); }
- @Override public String op() { return "divf"; }
- @Override public RegMask regmap(int i) { 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 5E /r DIVSD 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(0x5E);
-
- enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, dst, src));
- }
-
-
- // General form: "divf 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/DivX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/DivX86.java
deleted file mode 100644
index b5904a7f..00000000
--- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/DivX86.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 DivX86 extends MachConcreteNode implements MachNode {
- DivX86( Node div ) { super(div); }
- @Override public String op() { return "div"; }
- @Override public RegMask regmap(int i) {
- return (i==1) ? x86_64_v2.RAX_MASK : x86_64_v2.DIV_MASK;
- }
- @Override public RegMask outregmap() { return x86_64_v2.RAX_MASK; }
- @Override public RegMask killmap() { return x86_64_v2.RDX_MASK; }
-
- @Override public void encoding( Encoding enc ) {
- // REX.W + F7 /7 IDIV r/m64
- short src = enc.reg(in(2));
- // sign extend rax before 128/64 bits division
- // sign extend rax to rdx:rax
- enc.add1(x86_64_v2.REX_W);
- enc.add1(0x99);
- enc.add1(x86_64_v2.rex(0, src, 0));
- // divide
- enc.add1(0xF7); // opcode
- enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, 0x07, src));
- }
- // General form: "div dst /= src"
- @Override public void asm(CodeGen code, SB sb) {
- sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" / ").p(code.reg(in(2)));
- }
- @Override public String comment() { return "kill rdx"; }
-}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/FltX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/FltX86.java
deleted file mode 100644
index 9bcbac8f..00000000
--- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/FltX86.java
+++ /dev/null
@@ -1,47 +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 FltX86 extends ConstantNode implements MachNode, RIPRelSize{
- private byte _encSize;
- FltX86(ConstantNode con ) { super(con); }
- @Override public String op() { return "ld8"; }
- @Override public RegMask regmap(int i) { return null; }
- @Override public RegMask outregmap() { return x86_64_v2.XMASK; }
- @Override public boolean isClone() { return true; }
- @Override public Node copy() { return new FltX86(this); }
-
- @Override public void encoding( Encoding enc ) {
- // Load the constant into a FPR
- // movsd xmm, [rip + 0]
- // F2 0F 10 /r MOVSD xmm1, m64
- _encSize = 1/*Fop*/+0/*REX*/+2/*op*/+1/*MODRM*/+4/*offset*/;
- short dst = (short)(enc.reg(this ) - x86_64_v2.XMM_OFFSET);
- // Fopcode
- enc.add1(0xF2);
- // rex prefix must come next (REX.W is not set)
- _encSize += x86_64_v2.rexF(dst, 0, 0, false, enc);
- enc.add1(0x0F).add1(0x10);
- // hard-code rip here
- enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.INDIRECT, dst, 0x05));
- enc.add4(0);
- enc.largeConstant(this,_con,_encSize-4,2/*ELF encoding PC32*/);
- }
-
- // Delta is from opcode start
- @Override public byte encSize(int delta) {
- // TODO: 1-byte RIP offset?
- return (byte)_encSize;
- }
-
- // 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(" = #"));
- }
-}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/FunX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/FunX86.java
deleted file mode 100644
index cbd5fdbf..00000000
--- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/FunX86.java
+++ /dev/null
@@ -1,34 +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.node.FunNode;
-import com.seaofnodes.simple.node.MachNode;
-import com.seaofnodes.simple.util.SB;
-
-public class FunX86 extends FunNode implements MachNode {
- FunX86( FunNode fun ) { super(fun); }
- @Override public void postSelect(CodeGen code) {
- super.postSelect(code);
- // One more for RPC pre-pushed; X86 special
- _maxArgSlot++;
- }
-
- @Override public void computeFrameAdjust(CodeGen code, int maxReg) {
- super.computeFrameAdjust(code,maxReg);
- // Alignment, but off by RPC
- if( _hasCalls ) // If non-leaf, pad to 16b
- _frameAdjust = ((_frameAdjust+8+8) & -16)-8;
- }
-
- @Override public void encoding( Encoding enc ) {
- int sz = _frameAdjust;
- if( sz == 0 ) return; // Skip if no frame adjust
- // opcode: 0x83, addi rsp with immediate 8
- 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, 0b101, x86_64_v2.RSP) );
- if( x86_64_v2.imm8(sz) ) enc.add1(sz);
- else enc.add4(sz);
- }
-
-}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/I2f8X86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/I2f8X86.java
deleted file mode 100644
index 9f3d7905..00000000
--- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/I2f8X86.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 I2f8X86 extends MachConcreteNode implements MachNode {
- I2f8X86(Node i2f8 ) { super(i2f8); }
- @Override public String op() { return "cvtf"; }
- @Override public RegMask regmap(int i) { assert i==1; return x86_64_v2.WMASK; }
- @Override public RegMask outregmap() { return x86_64_v2.XMASK; }
-
- @Override public void encoding( Encoding enc ) {
- // F2 0F 2A /r CVTSI2SD xmm1, r32/m32
- short dst = (short)(enc.reg(this ) - x86_64_v2.XMM_OFFSET);
- short src = enc.reg(in(1));
-
- // Fopcode
- enc.add1(0xF2);
- // rex prefix must come next (REX.W is not set)
- x86_64_v2.rexF(dst, src, 0, true, enc);
-
- enc.add1(0x0F).add1(0x2A);
-
- enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, dst, src));
- }
-
- @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/x86_64_v2/ImmX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ImmX86.java
deleted file mode 100644
index 5e406911..00000000
--- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ImmX86.java
+++ /dev/null
@@ -1,38 +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.TypeInteger;
-import com.seaofnodes.simple.util.SB;
-
-public abstract class ImmX86 extends MachConcreteNode implements MachNode {
- final int _imm;
- ImmX86( Node add, int imm ) {
- super(add);
- _inputs.pop(); // Pop constant input off
- _imm = imm;
- }
- @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 RegMask killmap() { return x86_64_v2.FLAGS_MASK; }
-
- abstract int opcode();
- abstract int mod();
-
- @Override public void encoding( Encoding enc ) {
- short dst = enc.reg(this); // Also src1
- enc.add1(x86_64_v2.rex(0, dst, 0));
- // opcode; 0x81 or 0x83; 0x69 or 0x6B
- enc.add1( opcode() + (x86_64_v2.imm8(_imm) ? 2 : 0) );
- 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);
- }
-
- // General form: "addi dst += #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/IntX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/IntX86.java
deleted file mode 100644
index 3f2f696c..00000000
--- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/IntX86.java
+++ /dev/null
@@ -1,76 +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.type.TypeInteger;
-import com.seaofnodes.simple.util.SB;
-
-// Integer constants
-public class IntX86 extends ConstantNode implements MachNode {
- IntX86( ConstantNode con ) { super(con); }
- @Override public String op() {
- return _con == Type.NIL || _con == TypeInteger.ZERO ? "xor" : "ldi";
- }
- @Override public boolean isClone() { return true; }
- @Override public Node copy() { return new IntX86(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 || _con == TypeInteger.ZERO ? 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 || _con==TypeInteger.ZERO ) {
- // 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;
- }
-
- // 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<<RAX) & ~(1L<<RDX));
-
- static final long SPILLS = -(1L << MAX_REG);
- static final RegMask SPLIT_MASK = new RegMask(WR_BITS | FP_BITS /* | (1L<<FLAGS)*/ | SPILLS, -1L );
-
- // Load/store mask; both GPR and FPR
- static RegMask MEM_MASK = new RegMask(WR_BITS | FP_BITS);
-
- public static RegMask RAX_MASK = new RegMask(RAX);
- public static RegMask RCX_MASK = new RegMask(RCX);
- public static RegMask RDX_MASK = new RegMask(RDX);
- public static RegMask RDI_MASK = new RegMask(RDI);
- public static RegMask R08_MASK = new RegMask(R08);
- public static RegMask R09_MASK = new RegMask(R09);
- public static RegMask RSI_MASK = new RegMask(RSI);
-
- public static RegMask XMM0_MASK = new RegMask(XMM0);
-
- // Encoding
- public static int REX = 0x40;
- public static int REX_W = 0x48;
- public static int REX_WR = 0x4C;
- public static int REX_WRB= 0x4D;
- public static int REX_WB = 0x49;
-
- public enum MOD {
- INDIRECT, // [mem]
- INDIRECT_disp8, // [mem + 0x12]
- INDIRECT_disp32, // [mem + 0x12345678]
- DIRECT, // mem
- };
-
- // opcode included here
- // 0F 84 cd JE rel32
- // 0F 85 cd JNE rel32
- // 0F 8F cd JG rel32
- // 0F 8C cd JL rel32
- // 0F 8E cd JLE rel32
- // 0F 8D cd JGE rel32
- static public int jumpop(String op) {
- return switch (op) {
- case "==" -> 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<idx; i++ ) {
- if( tfp.arg(i-2) instanceof TypeFloat ) fcnt++;
- else icnt++;
- }
- int nstk = Math.max(icnt-6,0)+Math.max(fcnt-8,0);
- return tfp.arg(idx-2) instanceof TypeFloat
- ? fcnt<8 ? XMMS8 [fcnt] : new RegMask(MAX_REG+maxArgSlot+nstk)
- : icnt<6 ? SYS5_CALL[icnt] : new RegMask(MAX_REG+maxArgSlot+nstk);
- }
- static short maxArgSlotSys5(TypeFunPtr tfp) {
- int icnt=0, fcnt=0; // Count of ints, floats
- for( int i=0; i<tfp.nargs(); i++ ) {
- if( tfp.arg(i) instanceof TypeFloat ) fcnt++;
- else icnt++;
- }
- int nstk = Math.max(icnt-6,0)+Math.max(fcnt-8,0);
- return (short)nstk;
- }
-
- static final long SYSTEM5_CALLER_SAVE =
- (1L << RAX) | (1L << RCX) | (1L << RDX) |
- (1L << RDI) | (1L << RSI) |
- (1L << R08) | (1L << R09) | (1L << R10) | (1L << R11) |
- (1L << FLAGS) | // Flags are killed
- // All FP regs are killed
- FP_BITS;
-
- static final long WIN64_CALLER_SAVE =
- (1L<< RAX) | (1L<< RCX) | (1L<< RDX) |
- (1L<< R08) | (1L<< R09) | (1L<< R10) | (1L<< R11) |
- (1L<<FLAGS)| // Flags are killed
- // Only XMM0-XMM5 are killed; XMM6-XMM15 are preserved
- (1L<<XMM0) | (1L<<XMM1) | (1L<<XMM2) | (1L<<XMM3) |
- (1L<<XMM4) | (1L<<XMM5) ;
-
- @Override public long callerSave() {
- return switch (CodeGen.CODE._callingConv) {
- case "SystemV" -> SYSTEM5_CALLER_SAVE;
- case "Win64" -> WIN64_CALLER_SAVE;
- default -> throw new IllegalArgumentException("Unknown calling convention: " + CodeGen.CODE._callingConv);
- };
- }
- @Override public long neverSave() { return 1L<<RSP; }
- @Override public RegMask retMask( TypeFunPtr tfp ) {
- return tfp.ret() instanceof TypeFloat ? XMM0_MASK : RAX_MASK;
- }
- @Override public int rpc() { return RPC; }
-
-
- // Create a split op; any register to any register, including stack slots
- @Override public SplitNode split(String kind, byte round, LRG lrg) {
- return new SplitX86(kind, round);
- }
-
- // Return a MachNode unconditional branch
- @Override public CFGNode jump() {
- return new UJmpX86();
- }
-
- // Instruction selection
- @Override
- public Node instSelect(Node n) {
- return switch (n) {
- case AddFNode addf -> addf(addf);
- case AddNode add -> add(add);
- case AndNode and -> and(and);
- case BoolNode bool -> cmp(bool);
- case 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 extends MemOpNode > 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<code._cfg._len; i++ )
- if( code._cfg.at(i) instanceof FunNode fun )
- iadr = print(iadr,sb,code,fun,i);
-
- // constant pool
- iadr = (iadr+15)&-16; // pad to 16
- Encoding enc = code._encoding;
- if( enc!=null && !enc._bigCons.isEmpty() && iadr < enc._bits.size() ) {
- // radix sort the big constants by alignment
- Ary<Relo>[] raligns = new Ary[5];
- for( Node op : enc._bigCons.keySet() ) {
- Relo relo = enc._bigCons.get(op);
- int align = relo._t.alignment();
- Ary<Relo> relos = raligns[align]==null ? (raligns[align]=new Ary<>( Relo.class)) : raligns[align];
- relos.add(relo);
- }
-
- HashSet<Type> targets = new HashSet<>();
- sb.p("--- Constant Pool ------").nl();
-
- // By alignment
- for( int align = 4; align >= 0; align-- ) {
- Ary<Relo> 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<<align);
- }
- }
-
- case TypeStruct ts -> {
- int sz = ts.size();
- int log = 1<<align;
- sz = (sz + (log -1)) & -log; // Round up final padding
- for( int i=0; i<sz; i++ )
- sb.hex1(enc.read1(iadr++));
- }
-
- // Simple primitive (e.g. larger int, float)
- default -> {
- pN(enc,sb,iadr,align).fix(9-(1<<align),"");
- iadr += (1<<align);
- }
- }
- relo._t.print(sb).nl();
- }
- }
- }
-
- return sb;
- }
-
- private static SB pN( Encoding enc, SB sb, int iadr, int log ) {
- switch( log ) {
- case 0: sb.hex1(enc.read1(iadr)); break;
- case 1: sb.hex2(enc.read2(iadr)); break;
- case 2: sb.hex4(enc.read4(iadr)); break;
- case 3: sb.hex8(enc.read8(iadr)); break;
- }
- return sb;
- }
-
-
- private static int print(int iadr, SB sb, CodeGen code, FunNode fun, int cfgidx) {
- FunNode old=null;
- if( code._encoding!=null ) {
- old = code._encoding._fun;
- code._encoding._fun = fun; // Useful printing after RA
- }
- // Function header
- sb.nl().p("---");
- if( fun._name != null ) sb.p(fun._name).p(" ");
- fun.sig().print(sb);
- sb.p("---------------------------").nl();
- if( code._encoding!=null && code._encoding._padFunHeads )
- iadr = (iadr + 15)&-16; // All function entries padded to 16 align
-
- if( fun._frameAdjust != 0 )
- iadr = doInst(iadr,sb,code,fun,cfgidx,fun,true,true);
- while( !(code._cfg.at(cfgidx) instanceof ReturnNode) )
- iadr = doBlock(iadr,sb,code,fun,cfgidx++);
-
- // Function separator
- sb.p("---");
- fun.sig().print(sb);
- sb.p("---------------------------").nl();
- if( code._encoding != null )
- code._encoding._fun = old;
- return iadr;
- }
-
- static private final int opWidth = 5;
- static private final int argWidth = 30;
- static int doBlock(int iadr, SB sb, CodeGen code, FunNode fun, int cfgidx) {
- final int encWidth = code._mach==null ? 2 : code._mach.defaultOpSize()*2;
- CFGNode bb = code._cfg.at(cfgidx);
- if( bb != fun && !(bb instanceof IfNode) && !(bb instanceof CallEndNode) && !(bb instanceof CallNode) && !(bb instanceof CProjNode && bb.in(0) instanceof CallEndNode ))
- sb.p(label(bb)).p(":").nl();
- if( bb instanceof CallNode ) return iadr;
- final boolean postAlloc = code._phase.ordinal() > 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; i<phi.nIns(); i++ )
- sb.p("N").p(phi.in(i)._nid).p(",");
- sb.unchar().p(" )");
- }
- sb.nl();
- }
- }
- if( once ) sb.unchar().nl();
-
- // All the non-phis
- for( int i=0; i<bb.nOuts(); i++ )
- if( !(bb.out(i) instanceof PhiNode) )
- iadr = doInst(iadr, sb,code, fun, cfgidx, bb.out(i),postAlloc, postEncode );
-
- return iadr;
- }
-
- static int doInst( int iadr, SB sb, CodeGen code, FunNode fun, int cfgidx, Node n, boolean postAlloc, boolean postEncode ) {
- if( n==null || n instanceof CProjNode ) return iadr;
- if( postAlloc && n instanceof CalleeSaveNode ) return iadr;
- if( postEncode && n instanceof ProjNode ) return iadr;
- if( n instanceof MemMergeNode ) return iadr;
- if( n.getClass() == ConstantNode.class ) return iadr; // Default placeholders
- final int dopz = code._mach==null ? 2 : code._mach.defaultOpSize();
- final int encWidth = dopz*2;
-
- // All blocks ending in a Region will need to either fall into or jump
- // to this block. Until the post-reg-alloc block layout cleanup, we
- // need to assume a jump. There's no real hardware op here, yet.
- if( n instanceof RegionNode cfg && !(n instanceof FunNode) ) {
- if( postEncode ) return iadr; // All jumps inserted already
- while( cfgidx < code._cfg._len-1 ) {
- CFGNode next = code._cfg.at(++cfgidx);
- if( next == n ) return iadr; // Fall-through, no branch
- if( next.nOuts()>1 )
- 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<Math.min(size,dopz); i++ )
- sb.hex1(code._encoding._bits.buf()[iadr++]);
- else {
- iadr += Math.min(size,dopz);
- for( int i=0; i<Math.min(size,dopz); i++ )
- sb.hex1(code._encoding._bits.buf()[iadr-i-1]);
- }
- for( int i=size*2; i<encWidth; i++ )
- sb.p(" ");
- fatEncoding = size - (encWidth>>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<n.nIns(); i++ ) {
- sb.fix(5, n.in(i) == null ? "___" : String.valueOf(n.in(i)._nid));
- off += 5;
- if( off >= 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<len; i++ )
- sb.hex1(code._encoding._bits.buf()[iadr++]);
- off += len;
- for( int i=len; i<dopz; i++ ) sb.p(" ");
- sb.p(" ");
- int x = isMultiOp.indexOf('\n');
- if( x== -1 ) { // Last line
- sb.p(isMultiOp).nl();
- isMultiOp = null;
- } else {
- sb.p(isMultiOp.substring(0,x+1)); // Includes the newline
- isMultiOp = isMultiOp.substring(x+1);
- }
-
- }
-
- } else if( fatEncoding > 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<fatEncoding; i++ )
- sb.hex1(code._encoding._bits.buf()[iadr++]);
- sb.nl();
- }
-
-
- // MultiNodes are immediately followed by projection(s)
- if( !(n instanceof CFGNode) && n instanceof MultiNode ) {
- for( Node proj : n._outputs ) {
- if( proj instanceof ProjNode ) // it could also be an ante-dep
- doInst(iadr,sb,code,fun, cfgidx, proj,postAlloc,postEncode);
- }
- }
-
- return iadr;
- }
-
- private static String label( CFGNode bb ) {
- return (bb instanceof LoopNode ? "LOOP" : "L")+bb._nid;
- }
-
- private static String isMultiOp(SB sb, int old, int len) {
- for( int i=old; i<len; i++ )
- if( sb.at(i)=='\n' ) {
- String s = sb.subString(i+1,len);
- sb.setLen(i);
- return s;
- }
- return null;
- }
-
-}
diff --git a/src/main/java/com/seaofnodes/simple/print/GraphVisualizer.java b/src/main/java/com/seaofnodes/simple/print/GraphVisualizer.java
deleted file mode 100644
index 20c8439a..00000000
--- a/src/main/java/com/seaofnodes/simple/print/GraphVisualizer.java
+++ /dev/null
@@ -1,287 +0,0 @@
-package com.seaofnodes.simple.print;
-
-import com.seaofnodes.simple.Parser;
-import com.seaofnodes.simple.node.*;
-import java.util.*;
-import static com.seaofnodes.simple.util.Utils.TODO;
-
-/**
- * Simple visualizer that outputs GraphViz dot format.
- * The dot output must be saved to a file and run manually via dot to generate the SVG output.
- * Currently, this is done manually.
- */
-public class GraphVisualizer {
-
- /**
- * If set to true we put the control nodes in a separate cluster from
- * data nodes.
- */
- boolean _separateControlCluster = false;
-
- public GraphVisualizer(boolean separateControlCluster) { this._separateControlCluster = separateControlCluster; }
- public GraphVisualizer() { this(false); }
-
- public String generateDotOutput(Parser parse) { return generateDotOutput(parse._code._stop,parse._scope,parse._xScopes); }
- public String generateDotOutput(StopNode stop, Node scope, Stack<ScopeNode> xScopes) {
-
- // Since the graph has cycles, we need to create a flat list of all the
- // nodes in the graph.
- Collection<Node> 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<Node> 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<TABLE BORDER=\"0\" CELLBORDER=\"1\" CELLSPACING=\"0\" CELLPADDING=\"4\">\n");
- sb.append("\t\t\t<TR><TD BGCOLOR=\"yellow\">").append(lab).append("</TD></TR>\n");
- sb.append("\t\t\t<TR>");
- 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("<TD>").append("\n");
- sb.append("\t\t\t\t<TABLE BORDER=\"0\" CELLBORDER=\"1\" CELLSPACING=\"0\">").append("\n");
- sb.append("\t\t\t\t<TR>");
- }
- sb.append("<TD PORT=\"p").append(proj._idx).append("\">").append(proj.glabel()).append("</TD>");
- }
- if (use instanceof CProjNode proj) {
- if (!doProjTable) {
- doProjTable = true;
- sb.append("<TD>").append("\n");
- sb.append("\t\t\t\t<TABLE BORDER=\"0\" CELLBORDER=\"1\" CELLSPACING=\"0\">").append("\n");
- sb.append("\t\t\t\t<TR>");
- }
- sb.append("<TD PORT=\"p").append(proj._idx).append("\" BGCOLOR=\"yellow\">").append(proj.glabel()).append("</TD>");
- }
- }
- if (doProjTable) {
- sb.append("</TR>").append("\n");
- sb.append("\t\t\t\t</TABLE>").append("\n");
- sb.append("\t\t\t</TD>");
- }
- sb.append("</TR>\n");
- sb.append("\t\t\t</TABLE>>\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<Node> 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<TABLE BORDER=\"0\" CELLBORDER=\"1\" CELLSPACING=\"0\">\n");
- // Add the scope level
- sb.append("\t\t\t<TR><TD BGCOLOR=\"cyan\">").append(level).append("</TD>");
- int lexStart = scope._kinds.at(level)._lexSize;
- for( int j=lexStart; j<last; j++ ) {
- var v = scope._vars.at(j);
- sb.append("<TD PORT=\"").append(makePortName(scopeName, v._name)).append("\">").append(v._name).append("</TD>");
- }
- last = lexStart;
- sb.append("</TR>\n");
- sb.append("\t\t\t</TABLE>>];\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<Node> 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; i<n.nIns(); i++ ) {
- Node def = n.in(i);
- if( n instanceof PhiNode && def instanceof RegionNode ) {
- // Draw a dotted use->def 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<Node> findAll(Stack<ScopeNode> ss, Node... ns) {
- final HashMap<Integer, Node> 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<Integer,Node> 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<lim; i++ )
- sb.p(" ");
- sb.p(" ]] ");
- if( n._type!= null ) sb.p(n._type.str());
- return sb.p("\n");
- }
-
-
- public static String prettyPrint(Node node, int depth) {
- return CodeGen.CODE._phase.ordinal() > 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<Node> 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<Node> 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<Node> _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<code._cfg._len-1; i++ ) {
- CFGNode blk = code._cfg.at(i);
- if( blk.blockHead() && (!(blk instanceof CProjNode) || (blk.cfg0() instanceof IfNode )) ) {
- if( blk instanceof FunNode fun )
- sb.p("--- ").p(fun.label()).p(" ---------------------------").nl();
- // Print block header
- sb.p("%-13.13s".formatted(label(blk)+":"));
- sb.p( " ".repeat(4) ).p(" [[ ");
- if( blk instanceof RegionNode )
- for( int j=1; j<blk.nIns(); j++ )
- label(sb,blk.cfg(j));
- else
- label(sb,blk.cfg(0));
- sb.p(" ]] \n");
- }
- printLine(blk,sb);
- if( blk instanceof ReturnNode ret )
- sb.p("--- ").p(ret.fun().label()).p(" ---------------------------").nl();
-
- // Block contents
- for( Node n : blk._outputs ) {
- if( n instanceof CFGNode cfg ) continue;
- printLine(n,sb);
- if( !(n instanceof CFGNode) && n instanceof MultiNode )
- for( Node use : n._outputs )
- if( use instanceof ProjNode )
- printLine(use,sb);
- }
- }
-
- printLine(code._stop,sb);
- return sb.toString();
- }
-
- // Bulk pretty printer, knowing scheduling information is available
- private static String _prettyPrintScheduled( Node node, int depth ) {
- // Backwards DFS walk to depth.
- HashMap<Integer,Integer> ds = new HashMap<>();
- Ary<Node> 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<ns.size(); i++ ) {
- if( ns.get(i) instanceof ProjNode proj && !(proj.in(0) instanceof CFGNode) ) {
- ns.del(i--);
- ds.remove(proj._nid);
- }
- }
- // Print by block with least idepth
- SB sb = new SB();
- Ary<Node> 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<blk.nIns(); i++ )
- label(sb,blk.cfg(i));
- else
- label(sb,blk.cfg(0));
- sb.p(" ]] \n");
- printLine(blk,sb);
-
- // Collect block contents that are in the depth limit
- bns.clear();
- int xd = Integer.MAX_VALUE;
- for( Node use : blk._outputs ) {
- Integer i = ds.get(use._nid);
- if( i!=null && !(use instanceof CFGNode cfg && cfg.blockHead()) ) {
- if( bns.find(use)==-1 )
- bns.add(use);
- xd = Math.min(xd,i);
- }
- }
- // Print Phis up front, if any
- for( int i=0; i<bns.size(); i++ )
- if( bns.get(i) instanceof PhiNode phi )
- printLine( phi, sb,bns,i--,ds,ns);
-
- // Print block contents in depth order, bumping depth until whole block printed
- for( ; !bns.isEmpty(); xd++ )
- for( int i=0; i<bns.size(); i++ ) {
- Node n = bns.get(i);
- if( ds.get(n._nid)==xd ) {
- printLine( n, sb, bns, i--, ds,ns );
- if( n instanceof MultiNode && !(n instanceof CFGNode) ) {
- for( Node use : n._outputs ) {
- if( use instanceof ProjNode )
- printLine(use,sb,bns,bns.indexOf(use),ds,ns);
- }
- }
- }
- }
- sb.p("\n");
- }
- return sb.toString();
- }
-
- private static void _walk( HashMap<Integer,Integer> ds, Ary<Node> 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<Node> bns, int i, HashMap<Integer,Integer> ds, Ary<Node> 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<ScopeNode> xScopes = midParse ? CODE.P._xScopes : null;
-
- Collection<Node> 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<Node> 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("<TABLE BORDER=\"0\" CELLBORDER=\"1\" CELLSPACING=\"0\" CELLPADDING=\"4\">\n");
- // Row over single cell for node label
- cell(sb.i().p("<TR>"), n.glabel(), n, null).p("</TR>\n");
- // Row over single cell, for nested table
- sb.i().p("<TR><TD>\n").ii();
- sb.i().p("<TABLE BORDER=\"0\" CELLBORDER=\"1\" CELLSPACING=\"0\">").p("\n");
- sb.i().p("<TR>");
- 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("<TD></TD>");
- sb. p("</TR>").p("\n");
- sb.i().p("</TABLE>").p("\n").di();
- sb.i().p("</TD></TR>\n");
- sb.i().p("</TABLE>>\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("<TABLE BORDER=\"0\" CELLBORDER=\"1\" CELLSPACING=\"0\">\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("<TR>");
- for( int m=2; m<n.nIns(); m++ )
- cell(sb,"#"+m,n.alias(m),"m"+m);
- sb.p("</TR>\n"); // End scope level
- }
-
- // Scope variables, empty if none
- if( lexStart<last ) {
- sb.i().p("<TR>\n");
- for( int j=lexStart; j<last; j++ ) {
- if( scope.in(j)==null ) continue;
- var v = scope._vars.at(j);
- cell(sb.i(),v._name,scope.in(j),makePortName(scopeName, v._name)).nl();
- }
- last = lexStart;
- sb.i().p("</TR>\n");
- }
- sb.i().p("</TABLE>>];\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<max; i++ )
- sb.di().i().p("}\n");
- }
-
- // Append a cell, with color
- private static SB cell(SB sb, String text, Node n, String port) {
- sb.p("<TD");
- String color = color(n);
- sb.p(" BGCOLOR=\"").p(color).p("\"");
- if( port!=null ) sb.p(" PORT=\"").p(port).p("\"");
- return colorcell(sb.p(">"),text,n,dark(color)).p("</TD>");
- }
- 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("<font color=\"white\">");
- sb.p(text);
- if( n._type!=null ) {
- sb.p("<br /><font point-size=\"10\">");
- n._type.gprint(sb);
- sb.p("</font>");
- }
- if( dark ) sb.p("</font>");
- 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<Node> 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<n.nIns(); i++ ) {
- Node def = n.in(i);
- if( def==null ) continue;
- sb.i().p(n.uniqueName()).p(" -> ");
- 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<n.nIns(); i++ ) {
- Node def = n.in(i);
- while( def instanceof ScopeNode lazy )
- def = lazy.in(i);
- if( def==null ) continue;
- String scopeName = makeScopeName(scope, 0);
- sb.i().p(scopeName).p(":m").p(i).p(" -> ");
- 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<len; i++ )
- _str[i] = (byte)(rawget() ^ _key[i & 3]);
-
- return new String(_str,0,len);
- }
- // Client closes
- case 8: return null;
- // Ping
- case 9:
- // Send a PONG back. TODO: echo PING data in the PONG
- _out.write(0x8A); // FIN + PONG
- _out.write(0x00); // LENGTH==0
- return get();
- // Not (yet) implemented: continuation frames, ping/pong
- default:
- throw Utils.TODO("Unexpected op from client: "+op); // Multi-part message
- }
- }
-
- // Classic put to browser client. No encoding.
- public void put(String msg) throws IOException {
- int len = msg.length();
- if( len <= 125 ) {
- _out.write(129); // FIN, opcode 1 - whole text message
- _out.write(len);
- for( int i=0; i<msg.length(); i++ )
- _out.write(msg.charAt(i));
- return;
- }
- if( len > 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<msg.length(); i++ )
- _out.write(msg.charAt(i));
- }
-
- @Override public void close() throws IOException {
- if( _out != null ) {
- _out.write(0b10001000);
- _out.flush();
- }
- super.close();
- }
-}
diff --git a/src/main/java/com/seaofnodes/simple/sys.java b/src/main/java/com/seaofnodes/simple/sys.java
deleted file mode 100644
index 45106770..00000000
--- a/src/main/java/com/seaofnodes/simple/sys.java
+++ /dev/null
@@ -1,25 +0,0 @@
-package com.seaofnodes.simple;
-
-import java.io.IOException;
-import java.nio.file.Files;
-import java.nio.file.Path;
-
-
-/** Default included namespaces for Simple.
-
- You can think of this as {@code #include <stdio.h>} 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<Field> FREE = new Ary<>(Field.class);
- private Field(String fname, Type type, int alias, boolean xfinal, boolean one ) { super(TFLD); init(fname,type,alias,xfinal,one); }
- private Field init(String fname, Type type, int alias, boolean xfinal, boolean one ) {
- _fname = fname;
- _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<Type> 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 <a href="https://www.cse.psu.edu/~gxt29/teaching/cse597s21/slides/08monotoneFramework.pdf">see for example this set of slides</a>.
- * <p>
- * The types form a lattice; @see <a href="https://en.wikipedia.org/wiki/Lattice_(order)">a symmetric complete bounded (ranked) lattice.</a>
- * <p>
- * 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.
- * <p>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<Type,Type> INTERN = new HashMap<>();
- // Starting intern table; used to rapidly reset the main table between tests
- private static final HashMap<Type,Type> INTERN0= new HashMap<>();
- // Lifetime management during cyclic installs is complex; this is used to
- // track created-but-already-interned objects
- private static final Ary<Type> FREES = new Ary<>(Type.class);
- // "visit" bit management, based solely on _uid
- static final BitSet BITS = new BitSet();
-
- // "visit" bit management. Key varies by kind of visit:
-
- // - "close" an open cyclic type. Used by the Parser, it will hit on
- // same-named structs, even if the structs have different states of
- // completion/fields.
- // - "uid/name" - Either the UID or name works (unlike "close" which must
- // use the name). Used to make cyclic copies with changes, like make
- // read-only where there should be only 1 copy of a name in a cycle.
- // - "pair of uids" - For recursive MEET, where we need to key off two types
- static final HashMap<Object,Type> VISIT = new HashMap<>();
-
- // Every Type has a UID to allow visit bits on cyclic visits
- private static int UID=1;
- public char _uid; // Unique ID for every type
-
- // ----------------------------------------------------------
- // Simple types are implemented fully here. "Simple" means: the code and
- // 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<Type> 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 extends Type> T intern() {
- //assert check();
- assert !_terned; // Do not ask for already-interned
- T t2 = (T)INTERN.get(this);
- if( t2!=null ) {
- assert t2._dual != null; // Prior is complete with dual
- assert this != t2; // Do not hashcons twice, should not get self back
- return t2; // Return prior
- }
- // Mid recursive type construction
- if( !VISIT.isEmpty() && _type >= TCYCLIC ) {
- // No intern attempt at all
- return (T)this;
- }
- // Not in type table
- _dual = null; // No dual yet
- INTERN.put(this,this); // Put in table without dual
- _terned = true;
- //Util.hash_quality_check_per(INTERN,"INTERN");
- T d = xdual(); // Compute dual without requiring table lookup, and not setting name
- _dual = d;
- if( this==d ) return d; // Self-symmetric? Dual is self
- assert !equals(d); // Self-symmetric is handled by caller
- assert d._dual==null; // Else dual-dual not computed yet
- assert INTERN.get(d)==null;
- d._dual = (T)this;
- INTERN.put(d,d); // Install dual also
- d._terned = true;
- //assert check();
- return (T)this;
- }
-
- // Recursive visit elsewhere, interns with dual already
- <T extends Type> T _intern() {
- //assert check();
- assert !_terned;
- Type t = INTERN.get(this);
- if( t!=null ) return (T)t.delayFree(this).delayFree(_dual);
- assert !INTERN.containsKey( _dual );
- INTERN.put(this,this);
- INTERN.put(_dual,_dual);
- _terned = _dual._terned = true;
- //assert check();
- return (T)this;
- }
-
- private 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 <clinit> and collect basic types
- Type t = TypeRPC .BOT; // Force class load, intern
- Type u = TypeTuple.BOT; // Force class load, intern
- Type w = TypePtr .PTR; // Force class load, intern
- INTERN0.putAll(INTERN);
- } else {
- // Later times, reset back to first time
- VISIT.put(0,CONTROL); // Defeat assert
- for( Iterator<Type> it = INTERN.keySet().iterator(); it.hasNext(); ) {
- Type t = it.next();
- if( !INTERN0.containsKey(t) )
- { t._terned=false; t.free(t); }
- it.remove();
- }
- VISIT.clear();
- INTERN.putAll(INTERN0);
- }
- }
-
- // ----------------------------------------------------------
- public final Type meet(Type t) {
- // 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 extends Type> T dual() { return (T)_dual; }
-
- <T extends Type> T xdual() {
- return (T)new Type(switch( _type ) {
- case TBOT -> TTOP;
- case TTOP -> TBOT;
- case TCTRL -> TXCTRL;
- case TXCTRL-> TCTRL;
- case TNIL -> TXNIL;
- case TXNIL -> TNIL;
- default -> throw Utils.TODO(); // Should not reach here
- });
- }
-
-
- // ----------------------------------------------------------
- // Our lattice is defined with a MEET and a DUAL.
- // JOIN is dual of meet of both duals.
- 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<Type> ts = new Ary<>(Type.class);
- ts.addAll(VISIT.values());
- if( ts.find(this)== -1 ) ts.add(this);
-
- // Pass #1: replace already interned fields
- assert BITS.isEmpty();
- for( int i=0; i<ts._len; i++ )
- if( ts.at(i).tern()._terned )
- ts.del(i--);
- BITS.clear();
-
- // Pass#2: compute recursive duals
- for( Type t : ts )
- t.rdual();
-
- // Pass#3: Recursive install
- for( Type t : ts )
- if( !t._terned && INTERN.get(t)==null )
- t.install();
- // Upgrade the result
- Type rez = INTERN.get(this);
- assert rez!=null;
-
- // Pass#4?: Free up any created-but-already interned
- for( Type t : FREES )
- t.rfree();
- FREES.clear();
- VISIT.clear();
- return rez;
- }
-
- // Read-only recursive visits do not *make* cyclic types and so the cleanup
- // is much easier.
- static boolean recurClose(boolean rez) { VISIT.clear(); return rez; }
-
- final Type tern() {
- if( _terned ) return this;
- if( !BITS.get(_uid) ) {
- BITS.set(_uid);
- int nkids = nkids();
- for( int i=0; i<nkids; i++ )
- set(i,at(i).tern());
- }
- Type told = INTERN.get(this);
- return told==null ? this : told.delayFree(this);
- }
-
- // Recursive dual. Visit all types recursively. Each visited type is
- // *new*, and hits in the intern table, or not. If not, we recursively
- // call rdual to get its dual, and cross-link the duals. If hitting, we
- // use the intern type and mark the prior type for freeing.
- Type rdual() { assert !_terned; return xdual(); }
-
- // Recursively free type and children
- final void rfree() {
- if( isFree() ) return;
- int nkids = nkids();
- for( int i=0; i<nkids; i++ )
- if( !at(i)._terned )
- at(i).rfree();
- free(this);
- }
-
- Type free(Type free) { return this; }
- boolean isFree() { return false; }
-
- <T extends Type> T delayFree(Type free) {
- assert !free._terned;
- FREES.push(free);
- return (T)this;
- }
-
- final Type install() {
- if( this instanceof TypeStruct ) {
- Type x = _intern(); // Stop the recursion
- assert x==this;
- }
- int nkids = nkids();
- for( int i=0; i<nkids; i++ ) {
- Type kid = at(i);
- if( !kid._terned ) {
- Type ikid = kid.install();
- if( ikid != kid ) {
- set(i,ikid);
- _dual.set(i,ikid.dual());
- }
- }
- }
- return this instanceof TypeStruct ? this : _intern();
- }
-
- // Strict constant values, things on the lattice centerline.
- // Excludes both high and low values
- public final boolean isConstant() { return recurClose(recurOpen()._isConstant()); }
- boolean _isConstant() { return _type==TNIL; }
-
- // Are all reachable struct Fields are final?
- public final boolean isFinal() { return recurClose(recurOpen()._isFinal()); }
- boolean _isFinal() { assert _type < TCYCLIC; return true; }
-
- public final Type makeRO() {
- if( isFinal() ) return this;
- return recurOpen()._makeRO().recurClose();
- }
- Type _makeRO() { return this; }
-
- // Compute greatest lower bound in the lattice. If values are in memory,
- // ints and floats cannot widen.
- public final boolean isGLB(boolean mem) { return recurClose(recurOpen()._isGLB(mem)); };
- boolean _isGLB(boolean mem) {
- return switch(_type) {
- case TBOT -> false;
- case TNIL -> false;
- case TCTRL -> true;
- case TXCTRL -> false;
- case TXNIL -> false;
- case TTOP -> false;
- default -> throw Utils.TODO();
- };
- }
- public final Type glb(boolean mem) {
- if( isGLB(mem) ) return this;
- Type glb = recurOpen()._glb(mem).recurClose();
- assert this.isa(glb);
- return glb;
- }
- Type _glb(boolean mem) { assert is_simple(); return Type.BOTTOM; }
-
- Type _close() { return this; }
-
- // ----------------------------------------------------------
-
- // 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<<log_size(); }
- public int alignment() { return log_size(); } // alignment; for structs, max align of Fields
-
- // ----------------------------------------------------------
- // Useful in the debugger, which calls toString everywhere.
- // This is a more verbose dev-friendly print.
- @Override
- public final String toString() {
- return print(new SB(), new BitSet(), false).toString();
- }
- public final String gprint() {
- return print(new SB(), new BitSet(), true).toString();
- }
- public final SB print(SB sb) { return print(sb, new BitSet(), false); }
- public final SB gprint(SB sb) { return print(sb, new BitSet(), true ); }
-
- // Used during recursive printing, or combined with SB Node printing
- public final SB print(SB sb, BitSet visit, boolean html ) {
- if( visit.get(_uid) ) return sb.p(str());
- visit.set(_uid);
- return _print(sb,visit,html);
- }
- SB _print(SB sb, BitSet visit, boolean html ) { return sb.p(str()); }
-
- // This is used by error messages, and is a shorted print.
- public String str() { return STRS[_type]; }
-}
diff --git a/src/main/java/com/seaofnodes/simple/type/TypeConAry.java b/src/main/java/com/seaofnodes/simple/type/TypeConAry.java
deleted file mode 100644
index d4003f87..00000000
--- a/src/main/java/com/seaofnodes/simple/type/TypeConAry.java
+++ /dev/null
@@ -1,82 +0,0 @@
-package com.seaofnodes.simple.type;
-
-import com.seaofnodes.simple.util.SB;
-import com.seaofnodes.simple.util.Utils;
-import java.io.ByteArrayOutputStream;
-import java.util.ArrayList;
-
-/**
- * Represents a constant array of primitives
- */
-public class TypeConAry<A> 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<Type> 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<len; i++ ) {
- min = Math.min(min,at8(i));
- max = Math.max(max,at8(i));
- }
- return TypeInteger.make(min,max);
- }
- public long at8(int idx) { throw Utils.TODO(); }
- public int len() { throw Utils.TODO(); }
- @Override public int log_size() { throw Utils.TODO(); }
- public void write( ByteArrayOutputStream baos ) { throw Utils.TODO(); }
-
- @Override boolean eq(Type t) {
- return t instanceof TypeConAry ary && _any==ary._any && ary._ary==null;
- }
-
- @Override int hash() {
- assert _ary==null;
- return _any ? 1024 : 0;
- }
-
-}
diff --git a/src/main/java/com/seaofnodes/simple/type/TypeConAryB.java b/src/main/java/com/seaofnodes/simple/type/TypeConAryB.java
deleted file mode 100644
index fd2b2635..00000000
--- a/src/main/java/com/seaofnodes/simple/type/TypeConAryB.java
+++ /dev/null
@@ -1,21 +0,0 @@
-package com.seaofnodes.simple.type;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.io.ByteArrayOutputStream;
-
-public class TypeConAryB extends TypeConAry<byte[]> {
- 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<Type> 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<int[]> {
- 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<Type> 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<TypeFloat> FREE = new Ary<>(TypeFloat.class);
- private TypeFloat(byte sz, double con) { super(TFLT); init(sz,con); }
- private TypeFloat init( byte sz, double con ) { _sz = sz; _con = con; return this; }
- public static TypeFloat malloc(byte sz, double con) { return FREE.isEmpty() ? new TypeFloat(sz,con) : FREE.pop().init(sz,con); }
- private static TypeFloat make(int sz, double con) {
- TypeFloat f = malloc((byte)sz,con);
- TypeFloat t2 = f.intern();
- return t2==f ? f : t2.free(f);
- }
- @Override TypeFloat free(Type t) {
- TypeFloat f = (TypeFloat)t;
- f._hash = 0;
- f._dual=null;
- FREE.push(f);
- return this;
- }
-
- public static TypeFloat constant(double con) { return make(0, con); }
-
- public final static TypeFloat ONE = constant(1.0);
- public final static TypeFloat FZERO = constant(0.0);
- public final static TypeFloat F32 = make(32, 0);
- public final static TypeFloat F64 = make(64, 0);
-
- public static void gather(ArrayList<Type> ts) { ts.add(F64); ts.add(F32); ts.add(constant(3.141592653589793)); }
-
- @Override public String str() {
- 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.
- * <p></p>
- * 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).
- * <p></p>
- * 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<TypeFunPtr> FREE = new Ary<>(TypeFunPtr.class);
- private TypeFunPtr(byte nil, boolean open, Type[] sig, Type ret, long fidxs) { super(TFUNPTR,nil); init(nil,open,sig,ret,fidxs); }
- private TypeFunPtr init(byte nil, boolean open, Type[] sig, Type ret, long fidxs ) {
- assert sig != null;
- _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<<fidx ); }
-
-
- public static final Type[] TEMPTY = new Type[0];
- static final Type[] TINT = new Type[]{TypeInteger.BOT};
- static final Type[] TINTMEM = new Type[]{TypeInteger.BOT,TypeFloat.F32};
- static final Type[] TINTINT = new Type[]{TypeInteger.BOT,TypeInteger.BOT};
- public static TypeFunPtr BOT = make((byte)3,true ,TEMPTY,Type.BOTTOM,-1);
- public static TypeFunPtr TEST = make((byte)2,false,TINTMEM,TypeInteger.BOT, 1);
- public static TypeFunPtr TEST0 = make((byte)3,false,TINTMEM,TypeInteger.BOT, 3);
- public static TypeFunPtr MAIN = make((byte)3,false,TINT ,Type.BOTTOM,-1); // Main can return anything
- public static TypeFunPtr CALLOC= make((byte)3,false,TINTINT,TypeMemPtr .BOT,-1);
- public static void gather(ArrayList<Type> ts) { ts.add(TEST); ts.add(TEST0); ts.add(BOT); ts.add(MAIN);ts.add(CALLOC); }
-
- @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<min._sig.length; i++ )
- args[i] = _sig[i].meet(that._sig[i]);
- // Handle extra args
- for( int i=min._sig.length; i<_sig.length; i++ )
- args[i] = _sig[i];
- return make(xmeet0(that),_open | that._open, args, _ret.meet(that._ret), _fidxs | that._fidxs);
- }
- static Type[] xmeet( Type[] ts0, Type[] ts1) {
- if( ts0==ts1 ) return ts0;
- assert ts0.length==ts1.length;
- Type[] ts = new Type[ts0.length];
- for( int i=0; i<ts0.length; i++ )
- ts[i] = ts0[i].meet(ts1[i]);
- return ts;
- }
-
- @Override TypeFunPtr xdual() { return malloc(dual0(), !_open, xdual(_sig), _ret.dual(), ~_fidxs); }
- static Type[] xdual(Type[] ts) {
- Type[] dual = new Type[ts.length];
- for( int i=0; i<ts.length; i++ )
- dual[i] = ts[i].dual();
- return dual;
- }
-
- @Override TypeFunPtr rdual() {
- if( _dual!=null ) return dual();
- assert !_terned;
- Type[] sigs = new Type[_sig.length];
- TypeFunPtr d = malloc(dual0(),!_open,sigs,null,~_fidxs);
- (_dual = d)._dual = this; // Cross link duals
- // Return rdual
- d._ret = _ret._terned ? _ret.dual() : _ret.rdual();
- // Signature rdual
- for( int i=0; i<_sig.length; i++ )
- sigs[i] = _sig[i]._terned ? _sig[i].dual() : _sig[i].rdual();
- return d;
- }
-
- // RHS is NIL; do not deep-dual when crossing the centerline
- @Override public Type meet0() { return _nil==3 ? this : make((byte)3,_open,_sig,_ret,_fidxs); }
-
- @Override public boolean isHigh() { return _nil <= 1 || (_nil==2 && _fidxs==0); }
-
- @Override boolean _isConstant() { return (_nil==2 && Long.bitCount(_fidxs)==1) || (_nil==3 && _fidxs==0); }
-
- @Override boolean _isFinal() { return true; }
- @Override boolean _isGLB(boolean mem) { return true; }
- @Override TypeFunPtr _glb(boolean mem) { return this; }
- @Override TypeFunPtr _close() {
- Type[] sig = new Type[_sig.length];
- TypeFunPtr fun = malloc(_nil,false,sig,null,_fidxs);
- // Now start the recursion
- fun._ret = _ret._close();
- for( int i=0; i<sig.length; i++ )
- sig[i] = _sig[i]._close();
-
- return fun;
- }
-
- @Override public int log_size() { return 2; } // (1<<2)==4-byte pointers
-
- public Type arg(int i) { return _sig[i]; }
- public long fidxs() { return _fidxs; }
- public Type ret() { return _ret; }
- public int nargs() { return _sig.length; }
- public int fidx() { assert Long.bitCount(_fidxs)==1; return Long.numberOfTrailingZeros(_fidxs); }
-
-
- @Override int nkids() { return _sig.length+1; }
- @Override Type at( int idx ) {
- return idx == _sig.length ? _ret : _sig[idx];
- }
- @Override void set( int idx, Type t ) {
- if( idx == _sig.length ) _ret = t;
- else _sig[idx] = t;
- }
-
- @Override
- int hash() {
- long hash = _ret.hashCode() ^ _fidxs ^ super.hash();
- for( Type arg : _sig )
- hash = hash*19 ^ arg.hashCode();
- return Utils.fold(hash);
- }
-
- @Override
- boolean eq(Type t) {
- // Recursive; so use cyclic equals
- if( !VISIT.isEmpty() ) {
- assert TypeStruct.CEQUALS.isEmpty();
- boolean rez = cycle_eq(t);
- TypeStruct.CEQUALS.clear();
- return rez;
- }
- TypeFunPtr fun = (TypeFunPtr)t; // Invariant
- if( !super.eq(fun) || _open != fun._open || _fidxs != fun._fidxs || _sig.length != fun._sig.length ) return false;
- if( _sig == fun._sig && _ret == fun._ret ) return true;
-
- if( !_ret.eq(fun._ret) ) return false;
- for( int i = 0; i < _sig.length; i++ )
- if( !_sig[i].eq(fun._sig[i]) )
- return false;
- return true;
- }
- @Override boolean cycle_eq(Type t) {
- if( t._type != TFUNPTR ) return false;
- TypeFunPtr fun = (TypeFunPtr)t; // Invariant
- if( !super.eq(fun) || _open != fun._open || _fidxs != fun._fidxs || _sig.length != fun._sig.length ) return false;
- if( _sig == fun._sig && _ret == fun._ret ) return true;
-
- // Check to see if we've ever compared this pair of types before;
- // if so, then assume the cycle is equal here.
- int pid = pid(fun);
- if( TypeStruct.CEQUALS.find(pid)!= -1 ) return true;
- TypeStruct.CEQUALS.push(pid);
- if( !_ret.cycle_eq(fun._ret) ) return false;
- for( int i = 0; i < _sig.length; i++ )
- if( !_sig[i].cycle_eq(fun._sig[i]) )
- return false;
- return true;
- }
-
- @Override public String str() { return "{"+printFIDX()+"}"; }
-
- SB _print(SB sb, BitSet visit, boolean html ) {
- sb.p(x()).p("{ ");
- if( _sig!=null )
- for( Type t : _sig )
- sb.p(t.str()).p(" "); // Short form in signature
- _ret.print(sb.p(html ? "&rarr; " : "-> "),visit,html).p(" #");
- // Print fidxs
- return sb.p(printFIDX()).p("}").p(q());
- }
- String printFIDX() {
- String tilde = isHigh() ? "~" : "";
- long fidxs = isHigh() ? ~_fidxs : _fidxs;
- String fidx = fidxs==0 ? ""
- : Long.bitCount(fidxs) == 1 ? ""+Long.numberOfTrailingZeros(fidxs)
- : fidxs == -1 ? "ALL"
- : "b"+Long.toBinaryString(fidxs); // Just some function bits
- return 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.
- * <br>
- * If min <= max, this is a below center (towards bottom).
- * If min > max, this is an above center (towards top).
- */
- public long _min, _max;
-
- private static final Ary<TypeInteger> FREE = new Ary<>(TypeInteger.class);
- private TypeInteger(long min, long max) { super(TINT); init(min,max); }
- private TypeInteger init(long min, long max) { _min = min; _max = max; return this; }
- public static TypeInteger malloc(long lo, long hi) { return FREE.isEmpty() ? new TypeInteger(lo,hi) : FREE.pop().init(lo,hi); }
- public static TypeInteger make(long lo, long hi) {
- TypeInteger i = malloc(lo,hi);
- TypeInteger t2 = i.intern();
- return t2==i ? i : t2.free(i);
- }
- @Override TypeInteger free(Type t) {
- TypeInteger i = (TypeInteger)t;
- i._min = i._max = 0;
- i._hash = 0;
- i._dual = null;
- FREE.push(i);
- return this;
- }
-
- public static TypeInteger constant(long con) { return make(con, con); }
-
- public final static TypeInteger ZERO= make(0,0);
- public final static TypeInteger FALSE=ZERO;
- public final static TypeInteger TRUE= make(1,1);
- public final static TypeInteger TWO = make(2,2); // Shows up in some Simple tests as the starting argument
-
- public final static TypeInteger I1 = make(-1,0);
- public final static TypeInteger I8 = make(-128,127);
- public final static TypeInteger I16 = make(-32768,32767);
- public final static TypeInteger I32 = make(-1L<<31,(1L<<31)-1);
- public final static TypeInteger BOT = make(Long.MIN_VALUE,Long.MAX_VALUE);
- public final static TypeInteger TOP = (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<Type> ts) { ts.add(I32); ts.add(BOT); ts.add(U1); ts.add(I1); ts.add(U8); }
-
- @Override public String str() {
- if( _isConstant() ) return ""+_min;
- 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<TypeMem> FREE = new Ary<>(TypeMem.class);
- private TypeMem(int alias, Type t) { super(TMEM); init(alias,t); }
- private TypeMem init(int alias, Type t) {
- assert alias>1 || (t==Type.TOP || t==Type.BOTTOM);
- _alias = alias;
- _t = t;
- return this;
- }
- @Override TypeMem free(Type t) {
- TypeMem mem = (TypeMem)t;
- mem._alias= -99;
- mem._dual = null;
- mem._hash = 0;
- FREE.push(mem);
- return this;
- }
- @Override boolean isFree() { return _alias == -99; }
-
- public static TypeMem 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<Type> 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<TypeMemPtr> FREE = new Ary<>(TypeMemPtr.class);
- private TypeMemPtr(byte nil, TypeStruct obj, boolean one) { super(TMEMPTR,nil); init(nil,obj,one); }
- private TypeMemPtr init(byte nil, TypeStruct obj, boolean one) { _nil=nil; _obj=obj; _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<Type> 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<Type> 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<Type> 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<Integer> _rpcs;
-
- // If true, invert the meaning of the bits
- final boolean _any;
-
- private TypeRPC(boolean any, HashSet<Integer> rpcs) {
- super(TRPC);
- _any = any;
- _rpcs = rpcs;
- }
- private static TypeRPC make(boolean any, HashSet<Integer> rpcs) {
- return new TypeRPC(any,rpcs).intern();
- }
-
- public static TypeRPC constant(int cend) {
- HashSet<Integer> 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<Type> 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<Integer> lhs = _rpcs, rhs = rpc._rpcs;
- // Smaller on left
- if( lhs.size() > rhs.size() ) { lhs = rpc._rpcs; rhs = _rpcs; }
-
- HashSet<Integer> 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<Integer> sub = _any ? rpc._rpcs : _rpcs;
- HashSet<Integer> 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<TypeStruct> FREE = new Ary<>(TypeStruct.class);
- // Return a filled-in TypeStruct; either from free list or alloc new.
- private static TypeStruct malloc(String name, boolean open, Field[] fields) {
- if( FREE.isEmpty() ) return new TypeStruct(name,open,fields);
- TypeStruct ts = FREE.pop();
- assert ts.isFree();
- ts._name = name;
- ts._open = open;
- ts._fields = fields;
- return ts;
- }
- // Free ts; return this.
- @Override TypeStruct free(Type t) {
- TypeStruct ts = (TypeStruct)t;
- assert !ts.isFree() && !ts._terned;
- ts._fields = null;
- ts._offs = null;
- ts._dual = null;
- ts._hash = 0;
- FREE.push(ts);
- return this;
- }
- @Override boolean isFree() { return _fields==null; }
-
-
- // All fields directly listed
- public static TypeStruct make(String name, 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<flds.length; i++ )
- flds[i].setType(_fields[i]._t._close());
-
- return ts;
- }
-
- static final AryInt CEQUALS = new AryInt();
-
- public static final TypeStruct BOT = open("$STRUCT");
- public static final TypeStruct TOP = BOT.dual();
- public static final TypeStruct TEST= make("test",false,Field.TEST);
- private static final TypeStruct ARY = makeAry("[]i64",TypeInteger.U32,-1,TypeInteger.BOT,-2,false);
- private static final TypeStruct STR = makeAry("[]u8" ,TypeInteger.U32,-1,TypeInteger.U8 ,-4,false);
- private static final TypeStruct ABC = makeAry("[]u8",TypeInteger.constant(3),-1,TypeConAryB.ABC,-4,true);
-
- // A pair of self-cyclic types
- private static final TypeStruct SINT0 = open("%SINT");
- private static final TypeStruct SFLT0 = open("%SFLT");
- private static final TypeStruct SINT1 = SINT0.add(Field.make("a", TypeInteger.U32, -1, false, false)).add(Field.make("s2",TypeMemPtr.make((byte)2,SFLT0),-2, false, false));
- private static final TypeStruct SFLT1 = SFLT0.add(Field.make("b", TypeFloat .F32, -3, false, false)).add(Field.make("s1",TypeMemPtr.make((byte)2,SINT1),-4, false, false));
- public static final TypeStruct SFLT2 = SFLT1.close();
-
-
- public static void gather(ArrayList<Type> ts) {
- ts.add(BOT);
- ts.add(TEST);
- ts.add(ARY);
- ts.add(STR);
- ts.add(ABC);
- ts.add(SINT0);
- ts.add(SFLT0);
- ts.add(SINT1);
- ts.add(SFLT1);
- ts.add(SFLT2);
- ts.add(((TypeMemPtr)(SFLT2.field("s1")._t))._obj);
- // Break cyclic init: built a struct
- Field fcalloc = Field.make("calloc",TypeFunPtr.CALLOC,-2,true,true);
- TypeStruct scalloc = make("calloc",false,fcalloc);
- ts.add(scalloc);
-
- }
-
- // Find field index by name
- public int find(String fname) {
- 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<len; i++ ) {
- Field f0 = _fields[i], f1 = that._fields[i];
- if( !f0._fname.equals(f1._fname) || f0._alias != f1._alias || f0._one != f1._one )
- return BOT;
- }
-
- // See if we get a mid-recursion hit; breaks cycles
- Integer pid = pid(that);
- TypeStruct ts = (TypeStruct)VISIT.get(pid);
- if( ts!=null ) return ts;
- // If not mid-recursion, we are now; flag recursion.
- boolean notRecur = VISIT.isEmpty();
- if( notRecur ) recurOpen();
-
- // Setup and install the type, prior to recursing, so we can find our
- // recursive self again.
- ts = recurPre(pid,_open | that._open);
-
- // Recurse all common fields
- Field[] flds = ts._fields;
- for( int i=0; i<len; i++ ) {
- Field f0 = _fields[i], f1 = that._fields[i];
- flds[i].setType(f0._t.meet(f1._t ));
- flds[i]._final = f0._final | f1._final;
- }
-
- // Handle extra fields via `_open`
- for( int i=len; i<_fields.length; i++ )
- flds[i].setType(_fields[i]._t );
-
- // Cleanup post-recursion; intern/install at cycle end
- if( notRecur ) ts = (TypeStruct)ts.recurClose();
- return ts;
- }
-
- @Override
- TypeStruct xdual() {
- if( _name=="$STRUCT" )
- return malloc("$STRUCT",false,new Field[0]);
- Field[] flds = new Field[_fields.length];
- for( int i=0; i<_fields.length; i++ )
- flds[i] = _fields[i].dual();
- return malloc(_name,!_open,flds);
- }
-
- // Recursive dual
- @Override TypeStruct rdual() {
- if( _dual!=null ) return dual();
- assert !_terned;
- Field[] flds = new Field[_fields.length];
- TypeStruct d = malloc(_name,!_open,flds);
- (_dual = d)._dual = this; // Cross link duals
- for( int i=0; i<_fields.length; i++ )
- flds[i] = _fields[i]._terned ? _fields[i].dual() : _fields[i].rdual();
- return d;
- }
-
- // Is forward-reference
- @Override public boolean isFRef() { return _open; }
-
- @Override boolean _isConstant() {
- if( VISIT.containsKey(_uid) ) return true; // Cycles assume constant
- VISIT.put(_uid,this);
- // Check all fields for being constant
- for( Field field : _fields )
- if( !field._isConstant() )
- return false;
- return true;
- }
-
- // All fields are final
- @Override boolean _isFinal() {
- if( _open ) return false; // May have more more non-final fields
- if( VISIT.containsKey(_uid) ) // Test: been here before?
- return true; // Cycles assume final
- VISIT.put(_uid,this); // Set: dont do this again
- for( Field fld : _fields )
- if( !fld._isFinal() )
- return false;
- return true;
- }
-
- private TypeStruct recurPre(Object key, boolean open) {
- // Make a clone of original; suitable for hashing so can build
- // e.g. TMPs to clone as part of cycles - but will fail the 'eq' check
- // until the entire cycle is built.
- Field[] flds = new Field[_fields.length];
- for( int i=0; i<flds.length; i++ )
- flds[i] = _fields[i].malloc(); // Blank copy, but can be hashed
- TypeStruct ts = malloc(_name, open, flds );
- VISIT.put(key,ts);
- return ts;
- }
-
- // Make a read-only version
- @Override TypeStruct _makeRO() {
- // Check for already visited
- TypeStruct ts = (TypeStruct)VISIT.get(_name);
- if( ts!=null ) return ts; // Already visited
- ts = recurPre(_name,_open); // Make a new type with blank fields
- Field[] flds = ts._fields;
- for( Field fld : flds ) fld._final = true;
-
- // Now start the recursion
- for( int i=0; i<flds.length; i++ )
- flds[i].setType(_fields[i]._t._makeRO());
-
- return ts;
- }
-
-
- // All fields are at GLB already
- boolean isGLB2() {
- if( VISIT.containsKey(_uid) ) return true; // Cycles assume GLB
- VISIT.put(_uid,this);
- for( Field fld : _fields )
- if( !fld.isGLB2() )
- return false;
- return true;
- }
-
- // Keeps the same struct, but lower-bounds all fields.
- public TypeStruct glb2() {
- TypeStruct ts = (TypeStruct)VISIT.get(_name);
- if( ts!=null ) return ts;
- ts = recurPre(_name,_open);
- Field[] flds = ts._fields;
- for( int i=0; i<flds.length; i++ )
- flds[i]._final = true ;
-
- // Now start the recursion
- for( int i=0; i<flds.length; i++ )
- flds[i].setType(_fields[i]._t._glb(true));
-
- return ts;
- }
-
- // log_size for a struct is not defined, unless its exactly some power of
- // 2. *Total size* is well-defined, and is available in the offsets.
- @Override public int log_size() { throw Utils.TODO(); }
- @Override public int size() { return offset(_fields.length); }
- @Override public int alignment() {
- int align = 0;
- for( Field f : _fields )
- align = Math.max(align, f._t.alignment());
- return align;
- }
-
- // If false, always false.
- // If true , maybe true, need to check recursive fields.
- private boolean static_eq( TypeStruct ts ) {
- return _name.equals(ts._name) && _open==ts._open && _fields.length==ts._fields.length;
- }
-
- @Override boolean eq(Type t) {
- // Recursive; so use cyclic equals
- if( !VISIT.isEmpty() ) {
- assert CEQUALS.isEmpty();
- boolean rez = cycle_eq(t);
- CEQUALS.clear();
- return rez;
- }
- // Normal equals
- TypeStruct ts = (TypeStruct)t; // Invariant
- if( !static_eq(ts) ) return false;
- if( _fields==ts._fields ) return true;
- for( int i = 0; i < _fields.length; i++ )
- if( !_fields[i].eq(ts._fields[i]) )
- return false;
- return true;
- }
-
- @Override boolean cycle_eq(Type t) {
- if( this==t ) return true;
- TypeStruct ts = (TypeStruct)t; // Invariant
- if( !static_eq(ts) ) return false;
- if( _fields==ts._fields ) return true;
- // Check to see if we've ever compared this pair of types before;
- // if so, then assume the cycle is equal here.
- int pid = pid(ts);
- if( CEQUALS.find(pid)!= -1 ) return true;
- CEQUALS.push(pid);
- // Recursively check fields
- for( int i = 0; i < _fields.length; i++ )
- if( !_fields[i].cycle_eq(ts._fields[i]) )
- return false;
- return true;
- }
-
- @Override
- int hash() {
- long hash = _name.hashCode() ^ (_open ? 4 : 0);
- for( Field f : _fields )
- hash = Utils.rot(hash,13) ^ ((long)f._fname.hashCode() * f._alias);
- return Utils.fold(hash);
- }
-
- @Override int nkids() { return _fields.length; }
- @Override Type at( int idx ) { return _fields[idx]; }
- @Override void set( int idx, Type t ) { _fields[idx] = (Field)t; }
-
- @Override
- SB _print(SB sb, BitSet visit, boolean html ) {
- if( isFree() ) return sb.p("FREE:").p(_name);
- if( isAry() && field("[]")._t instanceof TypeConAry con )
- return sb.p(con.str());
- sb.p(_name);
- if( html || isAry() )
- return sb;
- sb.p(" {");
- for( Field f : _fields )
- (f._t ==null ? sb.p("---") : f._t.print(sb,visit,html)).p(f._final ? " " : " !").p(f._fname).p("; ");
- if( _open ) sb.p("... ");
- return sb.p("}");
- }
-
- @Override public String str() { return (isFree() ? "FREE:":"")+_name; }
-
-
- public boolean isAry() { return _fields.length>=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<<align)-1)) & -(1<<align);
- if( _fields[1]._t instanceof TypeConAry ary ) // Array length is well-defined for constants, not defined for runtime arrays
- _offs[2] = _offs[1]+ary.len();
-
- } else {
- int[] cnts = new int[5]; // Count of fields at log field size
- int flen = _fields.length;
- for( int i=0; i<flen; i++ )
- if( !_fields[i]._one )
- cnts[_fields[i]._t.log_size()]++; // Log size is 0(byte), 1(i16/u16), 2(i32/f32), 3(i64/dbl)
- int off = 0, idx = 0; // Base common struct fields go here, e.g. Mark/Klass
- // Compute offsets to the start of each power-of-2 aligned fields.
- int[] offs = new int[4];
- for( int i=3; i>=0; i-- ) {
- offs[i] = off;
- off += cnts[i]<<i;
- }
- // Assign offsets to all fields.
- // Really a hidden radix sort.
- for( int i=0; i<flen; i++ ) {
- if( _fields[i]._one ) continue;
- int log = _fields[i]._t.log_size();
- _offs[idx++] = offs[log]; // Field offset
- offs[log] += 1<<log; // Next field offset at same alignment
- cnts[log]--; // Count down, should be all zero at end
- }
- _offs[flen] = off; // Max struct size, no trailing padding
- }
- return _offs;
- }
-
- // Field order with increasing offset
- public int[] layout() {
- offset(0);
- int len = _offs.length;
- int[] is = new int[len];
- for( int i=0; i<len; i++ ) is[i] = i;
- // Stoopid hand rolled bubble sort
- for( int i=0; i<len; i++ )
- for( int j=i+1; j<len; j++ )
- if( _offs[is[j]] < _offs[is[i]] )
- { int tmp = is[i]; is[i] = is[j]; is[j] = tmp; }
- return is;
- }
-
-}
diff --git a/src/main/java/com/seaofnodes/simple/type/TypeTuple.java b/src/main/java/com/seaofnodes/simple/type/TypeTuple.java
deleted file mode 100644
index df9aba31..00000000
--- a/src/main/java/com/seaofnodes/simple/type/TypeTuple.java
+++ /dev/null
@@ -1,119 +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.*;
-
-public class TypeTuple extends Type {
-
- public Type[] _types;
-
- private static final Ary<TypeTuple> FREE = new Ary<>(TypeTuple.class);
- private TypeTuple(Type[] types) { super(TTUPLE); _types = types; }
- private TypeTuple init( Type[] types ) { _types = types; return this; }
- private static TypeTuple malloc(Type[] types) {
- return FREE.isEmpty() ? new TypeTuple(types) : FREE.pop().init(types);
- }
- // All fields directly listed
- public static TypeTuple make(Type... types) {
- TypeTuple tt = malloc(types);
- TypeTuple t2 = tt.intern();
- return t2==tt ? tt : t2.free(tt);
- }
- @Override TypeTuple free(Type t) {
- TypeTuple tt = (TypeTuple)t;
- tt._types = null;
- tt._dual = null;
- tt._hash = 0;
- FREE.push(tt);
- return this;
- }
- @Override boolean isFree() { return _types==null; }
-
-
- public static final TypeTuple BOT = malloc(new Type[0]).intern();
- public static final TypeTuple TOP = BOT.dual();
-
- public static final TypeTuple TEST = make(TypeInteger.BOT,TypeMemPtr.TEST);
- public static final TypeTuple START= make(Type.CONTROL,TypeMem.TOP,TypeInteger.BOT);
- public static final TypeTuple RET = make(Type.CONTROL,TypeMem.BOT,Type.BOTTOM);
-
- public static final TypeTuple IF_BOTH = make(Type. CONTROL,Type. CONTROL);
- public static final TypeTuple IF_NEITHER = make(Type.XCONTROL,Type.XCONTROL);
- public static final TypeTuple IF_TRUE = make(Type. CONTROL,Type.XCONTROL);
- public static final TypeTuple IF_FALSE = make(Type.XCONTROL,Type. CONTROL);
-
- public static void gather(ArrayList<Type> ts) { ts.add(BOT); ts.add(TEST); ts.add(START); ts.add(IF_TRUE); }
-
- @Override
- Type xmeet(Type other) {
- TypeTuple that = (TypeTuple)other; // contract from xmeet
- if( this==TOP ) return that;
- if( that==TOP ) return this;
- if( _types.length != that._types.length )
- return BOT;
- return make(TypeFunPtr.xmeet(_types,that._types));
- }
-
- @Override TypeTuple xdual() {
- if( _types.length==0 ) return malloc(null);
- return malloc(TypeFunPtr.xdual(_types));
- }
-
- @Override boolean _isConstant() {
- for( Type t : _types )
- if( !t._isConstant() )
- return false;
- return true;
- }
- @Override boolean _isFinal() { throw Utils.TODO(); }
-
- @Override TypeMemPtr _makeRO() { throw Utils.TODO(); }
-
- @Override public int log_size() { throw Utils.TODO(); }
- @Override public int alignment() {
- assert isConstant();
- int align = 0;
- for( Type t : _types )
- align = Math.max(align,t.alignment());
- return align;
- }
-
- public Type ret() { assert _types.length==3; return _types[2]; }
-
- @Override SB _print(SB sb, BitSet visit, boolean html ) {
- if( this==TOP ) return sb.p("[TOP]");
- if( this==BOT ) return sb.p("[BOT]");
- sb.p("[ ");
- for( Type t : _types )
- t.print(sb,visit,html).p(", ");
- return sb.unchar(2).p("]");
- }
-
- @Override public String str() { return print(new SB(),new BitSet(),false).toString(); }
-
- @Override
- int hash() {
- int sum = 0;
- if( _types!=null ) for( Type type : _types ) sum ^= type.hashCode();
- return sum;
- }
-
- @Override
- boolean eq( Type t ) {
- TypeTuple that = (TypeTuple)t; // Contract
- if( _types==null && that._types==null ) return true;
- if( _types==null || that._types==null ) return false;
- if( _types.length != that._types.length ) return false;
- for( int i=0; i<_types.length; i++ )
- if( _types[i]!=that._types[i] )
- return false;
- return true;
- }
-
- @Override int nkids() { return _types.length; }
- @Override Type at( int idx ) { return _types[idx]; }
- @Override void set( int idx, Type t ) { _types[idx] = t; }
-
-}
diff --git a/src/main/java/com/seaofnodes/simple/util/Ary.java b/src/main/java/com/seaofnodes/simple/util/Ary.java
deleted file mode 100644
index d67801c2..00000000
--- a/src/main/java/com/seaofnodes/simple/util/Ary.java
+++ /dev/null
@@ -1,229 +0,0 @@
-package com.seaofnodes.simple.util;
-
-import java.lang.StringBuilder;
-import java.lang.reflect.Array;
-import java.util.*;
-
-// ArrayList with saner syntax
-public class Ary<E> extends AbstractList<E> implements List<E> {
- public E[] _es;
- public int _len;
-
- public Ary(E[] es) { this(es,es.length); }
- public Ary(E[] es, int len) { if( es.length==0 ) es=Arrays.copyOf(es,1); _es=es; _len=len; }
- @SuppressWarnings("unchecked")
- public Ary(Class<E> clazz) { this((E[]) Array.newInstance(clazz, 1),0); }
-
- /** @return list is empty */
- public boolean isEmpty() { return _len==0; }
- /** @return active list length */
- public int len() { return _len; }
- public int size() { return len(); }
-
- /** @param i element index
- * @return element being returned; throws if OOB
- * @exception AIOOBE if !(0 <= i < _len)
- */
- public E get( int i ) { return at(i); }
- public E at( int i ) {
- range_check(i);
- return _es[i];
- }
- public E atX( int i ) {
- if( i >= _len ) return null;
- return _es[i];
- }
-
- /** @return last element */
- public E last( ) { return at(_len-1); }
- public E last(int x ) { return at(_len-1+x); }
-
- /** Add element in amortized constant time
- * @param e Element to add at end of list
- **/
- public E push( E e ) {
- if( _len >= _es.length ) _es = Arrays.copyOf(_es,Math.max(1,_es.length<<1));
- return (_es[_len++] = e);
- }
- /** @return remove and return last element */
- public E pop( ) {
- return _len==0 ? null : _es[--_len];
- }
- @Override public E removeLast() { return pop(); }
-
-
- /** Add element in amortized constant time
- * @param e Element to add at end of list
- * @return true */
- public boolean add( E e ) {
- if( _len >= _es.length ) _es = Arrays.copyOf(_es,Math.max(1,_es.length<<1));
- _es[_len++] = e;
- return true;
- }
-
- /** Fast, constant-time, element removal. Does not preserve order
- * @param i element to be removed
- * @return element removed */
- public E del( int i ) {
- range_check(i);
- E tmp = _es[i];
- _es[i]=_es[--_len];
- return tmp;
- }
-
- /** Slow, linear-time, element removal. Preserves order.
- * @param i element to be removed
- * @return element removed */
- @Override public E remove( int i ) {
- range_check(i);
- E e = _es[i];
- System.arraycopy(_es,i+1,_es,i,(--_len)-i);
- return e;
- }
-
- /** Slow, linear-time, element insertion. Preserves order.
- * @param i where to insert
- * @return element inserted */
- public E insert( E e, int i ) {
- setLen(_len+1);
- System.arraycopy(_es,i,_es,i+1,_len-i);
- _es[i] = e;
- return e;
- }
-
-
- /** Remove all elements */
- public void clear( ) { _len=0; }
-
- /** Set existing element
- * @param i element to set
- * @param e value to set
- * @return old value
- * @exception AIOOBE if !(0 <= i < _len)
- */
- public E set( int i, E e ) {
- range_check(i);
- E old = _es[i];
- _es[i] = e;
- return old;
- }
-
- /** Set existing element
- * @param i element to set
- * @param e value to set
- * @return old value
- * @exception AIOOBE if !(0 <= i < _len)
- */
- public E setX( int i, E e ) {
- if( i >= _len ) {
- while( i >= _es.length ) _es = Arrays.copyOf( _es, _es.length << 1 );
- _len = i+1;
- }
- E old = _es[i];
- _es[i] = e;
- return old;
- }
-
- public Ary<E> setLen( int len ) {
- if( len > _len )
- while( len>= _es.length ) _es = Arrays.copyOf(_es,_es.length<<1);
- _len = len;
- while( _es.length > (len<<1) ) // Shrink if hugely too large
- _es = Arrays.copyOf(_es,_es.length>>1);
- Arrays.fill(_es,len,_es.length,null);
- return this;
- }
-
- public E swap( int i, int j ) {
- range_check(i);
- range_check(j);
- E tmp = _es[i];
- if( i==j ) return tmp;
- _es[i] = _es[j];
- _es[j] = tmp;
- return tmp;
- }
-
-
- /** @param c Collection to be added */
- public boolean addAll( Collection<? extends E> c ) {
- if( c==null ) return false;
- for( E e : c ) add(e);
- return true;
- }
- public Ary<E> addAll( Iterable<? extends E> it ) { for( E e : it ) add(e); return this; }
-
- /** @param es Array to be added */
- public <F extends E> Ary<E> addAll( F[] es ) {
- if( es==null || es.length==0 ) return this;
- while( _len+es.length > _es.length ) _es = Arrays.copyOf(_es,_es.length<<1);
- System.arraycopy(es,0,_es,_len,es.length);
- _len += es.length;
- return this;
- }
-
- /** @param c Collection to be added */
- public Ary<E> addAll( Ary<? extends E> c ) {
- if( c==null || c._len==0 ) return this;
- while( _len+c._len > _es.length ) _es = Arrays.copyOf(_es,_es.length<<1);
- System.arraycopy(c._es,0,_es,_len,c._len);
- _len += c._len;
- return this;
- }
-
- /** @return compact array version */
- public E[] asAry() { return Arrays.copyOf(_es,_len); }
-
- /** Find the first matching element using ==, or -1 if none. Note that
- * most del calls shuffle the list, so the first element might be random.
- * @param e Element to find
- * @return index of first matching element, or -1 if none */
- public int find( E e ) {
- for( int i=0; i<_len; i++ ) if( _es[i]==e ) return i;
- return -1;
- }
-
-
- /** @return an iterator */
- @Override public Iterator<E> iterator() { return new Iter(); }
- private class Iter implements Iterator<E> {
- int _i=0;
- @Override public boolean hasNext() { return _i<_len; }
- @Override public E next() { return _es[_i++]; }
- }
-
- @Override public String toString() {
- StringBuilder sb = new StringBuilder().append('{');
- for( int i=0; i<_len; i++ ) {
- if( i>0 ) sb.append(',');
- if( _es[i] != null ) sb.append(_es[i].toString());
- }
- return sb.append('}').toString();
- }
-
- private void range_check( int i ) {
- if( i < 0 || i>=_len )
- throw new ArrayIndexOutOfBoundsException(""+i+" >= "+_len);
- }
-
- // Note that the hashCode() and equals() are not invariant to changes in the
- // underlying array. If the hashCode() is used (e.g., inserting into a
- // HashMap) and the then the array changes, the hashCode() will change also.
- @Override public boolean equals( Object o ) {
- if( this==o ) return true;
- if( !(o instanceof Ary ary) ) return false;
- if( _len != ary._len ) return false;
- if( _es == ary._es ) return true;
- for( int i=0; i<_len; i++ )
- if( !(_es[i]==null ? (ary._es[i] == null) : _es[i].equals(ary._es[i])) )
- return false;
- return true;
- }
- @Override public int hashCode( ) {
- int sum=_len;
- for( int i=0; i<_len; i++ )
- sum += _es[i]==null ? 0 : _es[i].hashCode();
- return sum;
- }
-
-}
diff --git a/src/main/java/com/seaofnodes/simple/util/AryInt.java b/src/main/java/com/seaofnodes/simple/util/AryInt.java
deleted file mode 100644
index b498fd78..00000000
--- a/src/main/java/com/seaofnodes/simple/util/AryInt.java
+++ /dev/null
@@ -1,192 +0,0 @@
-package com.seaofnodes.simple.util;
-
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.function.IntUnaryOperator;
-
-// ArrayList with saner syntax
-public class AryInt {
- public int[] _es;
- public int _len;
- public AryInt(int[] es) { this(es,es.length); }
- public AryInt(int[] es, int len) { _es=es; _len=len; }
- public AryInt() { this(new int[1],0); }
-
- /** @return list is empty */
- public boolean isEmpty() { return _len==0; }
- /** @return active list length */
- public int len() { return _len; }
- /** @param i element index
- * @return element being returned; throws if OOB */
- public int at( int i ) {
- range_check(i);
- return _es[i];
- }
- /** @param i element index
- * @return element being returned, or 0 if OOB */
- public int atX( int i ) {
- return i < _len ? _es[i] : 0;
- }
- /** @return last element */
- public int last( ) { return at(_len-1); }
- public int last(int x ) { return at(_len-1+x); }
-
- /** @return remove and return last element */
- public int pop( ) {
- range_check(0);
- return _es[--_len];
- }
-
- /** Add element in amortized constant time
- * @param e element to add at end of list
- * @return 'this' for flow-coding */
- public AryInt push( int e ) {
- if( _len >= _es.length ) _es = Arrays.copyOf(_es,Math.max(1,_es.length<<1));
- _es[_len++] = e;
- return this;
- }
-
- /** Slow, linear-time, element insert. Preserves order.
- * @param i index to insert at, between 0 and _len inclusive.
- * @param e intlement to insert
- */
- public void insert( int i, int e ) {
- if( i < 0 || i>_len )
- throw new ArrayIndexOutOfBoundsException(""+i+" >= "+_len);
- if( _len >= _es.length ) _es = Arrays.copyOf(_es,Math.max(1,_es.length<<1));
- System.arraycopy(_es,i,_es,i+1,(_len++)-i);
- _es[i] = e;
- }
-
- /** Fast, constant-time, element removal. Does not preserve order
- * @param i element to be removed
- * @return element removed */
- public int del( int i ) {
- range_check(i);
- int tmp = _es[i];
- _es[i]=_es[--_len];
- return tmp;
- }
-
- /** Slow, linear-time, element removal. Preserves order.
- * @param i element to be removed
- * @return element removed */
- public int remove( int i ) {
- range_check(i);
- int e = _es[i];
- System.arraycopy(_es,i+1,_es,i,(--_len)-i);
- return e;
- }
-
- /** Remove all elements */
- public void clear( ) { Arrays.fill(_es,0,_len,0); _len=0; }
-
- // Extend and set
- public int setX( int i, int e ) {
- while( i>= _es.length ) _es = Arrays.copyOf(_es,_es.length<<1);
- if( i >= _len ) _len = i+1;
- return (_es[i] = e);
- }
-
- public int set( int i, int e ) {
- range_check(i);
- return (_es[i] = e);
- }
-
- public AryInt set_as( int e ) { _es[0] = e; _len=1; return this; }
- public AryInt set_len( int len ) {
- if( len > _len )
- while( len>= _es.length ) _es = Arrays.copyOf(_es,_es.length<<1);
- _len = len;
- while( _es.length > (len<<1) ) // Shrink if hugely too large
- _es = Arrays.copyOf(_es,_es.length>>1);
- return this;
- }
-
- /** @param c Collection to be added */
- public AryInt addAll( Collection<? extends Integer> c ) { for( int e : c ) push(e); return this; }
-
- /** @param es Array to be added */
- public AryInt addAll( int[] es ) {
- if( es.length==0 ) return this;
- while( _len+es.length > _es.length ) _es = Arrays.copyOf(_es,_es.length<<1);
- System.arraycopy(es,0,_es,_len,es.length);
- _len += es.length;
- return this;
- }
-
- /** @param es Array to be added */
- public AryInt addAll( AryInt ary ) {
- if( ary._len==0 ) return this;
- while( _len+ary._len > _es.length ) _es = Arrays.copyOf(_es,_es.length<<1);
- System.arraycopy(ary._es,0,_es,_len,ary._len);
- _len += ary._len;
- return this;
- }
-
- public AryInt map_update( IntUnaryOperator f ) { for( int i = 0; i<_len; i++ ) _es[i] = f.applyAsInt(_es[i]); return this; }
-
- /** @return compact array version, using the internal base array where possible. */
- public int[] asAry() { return _len==_es.length ? _es : Arrays.copyOf(_es,_len); }
-
- /** Sorts in-place */
- public void sort_update() { Arrays.sort(_es, 0, _len); }
- /** Find the first matching element using ==, or -1 if none. Note that
- * most del calls shuffle the list, so the first element might be random.
- * @param e intlement to find
- * @return index of first matching element, or -1 if none */
- public int find( int e ) {
- for( int i=0; i<_len; i++ ) if( _es[i]==e ) return i;
- return -1;
- }
-
- @Override public String toString() {
- SB sb = new SB().p('[');
- for( int i=0; i<_len; i++ )
- sb.p(_es[i]).p(',');
- return sb.unchar().p(']').toString();
- }
-
- private void range_check( int i ) {
- if( i < 0 || i>=_len )
- throw new ArrayIndexOutOfBoundsException(""+i+" >= "+_len);
- }
-
- // Binary search sorted _es. Returns insertion point.
- // Undefined results if _es is not sorted.
- public int binary_search( int e ) {
- int lo=0, hi=_len-1;
- while( lo <= hi ) {
- int mid = (hi + lo) >>> 1; // midpoint, rounded down
- int mval = _es[mid];
- if( e==mval ) {
- // If dups, get to the first.
- while( mid>0 && e==_es[mid-1] ) mid--;
- return mid;
- }
- if( e >mval ) lo = mid+1;
- else hi = mid-1;
- }
- return lo;
- }
-
- // Note that the hashCode() and equals() are not invariant to changes in the
- // underlying array. If the hashCode() is used (e.g., inserting into a
- // HashMap) and the then the array changes, the hashCode() will change also.
- @Override public boolean equals( Object o ) {
- if( this==o ) return true;
- if( !(o instanceof AryInt ary) ) return false;
- if( _len != ary._len ) return false;
- if( _es == ary._es ) return true;
- for( int i=0; i<_len; i++ )
- if( _es[i] != ary._es[i] )
- return false;
- return true;
- }
- @Override public int hashCode( ) {
- int sum=_len;
- for( int i=0; i<_len; i++ )
- sum += _es[i];
- return sum;
- }
-}
diff --git a/src/main/java/com/seaofnodes/simple/util/SB.java b/src/main/java/com/seaofnodes/simple/util/SB.java
deleted file mode 100644
index d7afd6eb..00000000
--- a/src/main/java/com/seaofnodes/simple/util/SB.java
+++ /dev/null
@@ -1,99 +0,0 @@
-package com.seaofnodes.simple.util;
-
-/** Tight/tiny StringBuilder wrapper.
- * Short short names on purpose; so they don't obscure the printing.
- * Can't believe this wasn't done long, long ago. */
-public final class SB {
- private final StringBuilder _sb;
- private int _indent = 0;
- public SB( ) { _sb = new StringBuilder( ); }
- public SB(String s) { _sb = new StringBuilder(s); }
- public SB p( String s ) { _sb.append(s); return this; }
- public SB p( float s ) {
- if( Float.isNaN(s) )
- _sb.append( "Float.NaN");
- else if( Float.isInfinite(s) ) {
- _sb.append(s > 0 ? "Float.POSITIVE_INFINITY" : "Float.NEGATIVE_INFINITY");
- } else _sb.append(s);
- return this;
- }
- public SB p( double s ) {
- if( Double.isNaN(s) )
- _sb.append("Double.NaN");
- else if( Double.isInfinite(s) ) {
- _sb.append(s > 0 ? "Double.POSITIVE_INFINITY" : "Double.NEGATIVE_INFINITY");
- } else _sb.append(s);
- return this;
- }
- public SB p( char s ) { _sb.append(s); return this; }
- public SB p( int s ) { _sb.append(s); return this; }
- public SB p( long s ) { _sb.append(s); return this; }
- public SB p( boolean s) { _sb.append(s); return this; }
- // 1 byte, 2 hex digits, 8 bits
- public SB hex1(int s) {
- int digit = (s>>4) & 0xf;
- _sb.append((char)((digit <= 9 ? '0' : ('A'-10))+digit));
- digit = s & 0xf;
- _sb.append((char)((digit <= 9 ? '0' : ('A'-10))+digit));
- return this;
- }
- // 2 bytes, 4 hex digits, 16 bits, Big Endian
- public SB hex2(int s) { return hex1(s>> 8).hex1(s); }
- // 4 bytes, 8 hex digits, 32 bits, Big Endian
- public SB hex4(int s) { return hex2(s>>16).hex2(s); }
- // 8 bytes, 16 hex digits, 64 bits, Big Endian
- public SB hex8(long s) { return hex4((int)(s>>32)).hex4((int)s); }
-
- // Fixed width field
- public SB fix( int sz, String s ) {
- for( int i=0; i<sz; i++ )
- _sb.append( i < s.length() ? s.charAt(i) : ' ');
- return this;
- }
- public char at(int idx ) { return _sb.charAt(idx); }
-
- // Not spelled "p" on purpose: too easy to accidentally say "p(1.0)" and
- // suddenly call the autoboxed version.
- public SB pobj( Object s ) { _sb.append(s.toString()); return this; }
- public SB i( int d ) { for( int i=0; i<d+_indent; i++ ) p(" "); return this; }
- public SB i( ) { return i(0); }
- public SB ip(String s) { return i().p(s); }
- public SB s() { _sb.append(' '); return this; }
-
- // Increase indentation
- public SB ii( int i) { _indent += i; return this; }
- public SB ii() { return ii(1); }
- // Decrease indentation
- public SB di( int i) { _indent -= i; return this; }
- public SB di() { return di(1); }
-
- public SB nl( ) { return p(System.lineSeparator()); }
- // Last printed a nl
- public boolean was_nl() {
- int len = _sb.length();
- String nl = System.lineSeparator();
- int nlen = nl.length();
- if( len < nlen ) return false;
- for( int i=0; i<nlen; i++ )
- if( _sb.charAt(len-nlen+i)!=nl.charAt(i) )
- return false;
- return true;
- }
-
- // Delete last char. Useful when doing string-joins and JSON printing and an
- // extra separater char needs to be removed:
- //
- // sb.p('[');
- // for( Foo foo : foos )
- // sb.p(foo).p(',');
- // sb.unchar().p(']'); // remove extra trailing comma
- //
- public SB unchar() { return unchar(1); }
- public SB unchar(int x) { _sb.setLength(_sb.length()-x); return this; }
- public SB setLen(int len) { _sb.setLength(len); return this; }
- public String subString(int start, int end ) { return _sb.substring(start,end); }
-
- public SB clear() { _sb.setLength(0); return this; }
- public int len() { return _sb.length(); }
- @Override public String toString() { return _sb.toString(); }
-}
diff --git a/src/main/java/com/seaofnodes/simple/util/Utils.java b/src/main/java/com/seaofnodes/simple/util/Utils.java
deleted file mode 100644
index 577a60af..00000000
--- a/src/main/java/com/seaofnodes/simple/util/Utils.java
+++ /dev/null
@@ -1,53 +0,0 @@
-package com.seaofnodes.simple.util;
-
-import java.util.ArrayList;
-
-public class Utils {
- public static RuntimeException TODO() { return TODO("Not yet implemented"); }
- public static RuntimeException TODO(String msg) { return new RuntimeException(msg); }
-
- /**
- * Fast, constant-time, element removal. Does not preserve order
- *
- * @param array ArrayList to modify
- * @param i element to be removed
- * @return element removed
- */
- public static <E> void del(ArrayList<E> array, int i) {
- E last = array.removeLast();
- if (i < array.size()) array.set(i, last);
- }
-
- /**
- * Search a list for an element by reference
- *
- * @param ary List to search in
- * @param x Object to be searched
- * @return >= 0 on success, -1 on failure
- */
- public static <E> int find( ArrayList<E> ary, E x ) {
- for( int i=0; i<ary.size(); i++ )
- if( ary.get(i)==x )
- return i;
- return -1;
- }
- /**
- * Search a list for an element by reference
- *
- * @param ary List to search in
- * @param x Object to be searched
- * @return >= 0 on success, -1 on failure
- */
- public static <E> int find( E[] ary, E x ) {
- for( int i=0; i<ary.length; i++ )
- if( ary[i]==x )
- return i;
- return -1;
- }
-
- // Rotate a long, nice for hashes
- public static long rot( long x, int n ) { return (x<<n) | (x>>>n); }
-
- // Fold a 64bit hash into 32 bits
- public static int fold( long x ) { return (int)((x>>32) ^ x); }
-}