summaryrefslogtreecommitdiff
path: root/src/main/java/com/seaofnodes
diff options
context:
space:
mode:
authorCliff Click <cliffc@acm.org>2025-02-07 09:12:14 -0800
committerCliff Click <cliffc@acm.org>2025-04-24 14:38:36 -0700
commitf6c0b12e6cc1af4879042c0426fc96532cdfddc3 (patch)
treea23ca739b2b197128c2bd452c178d7b1143180a2 /src/main/java/com/seaofnodes
parent0c50d21b223abdc0df332223070c14e2eed96d53 (diff)
downloadSimple-f6c0b12e6cc1af4879042c0426fc96532cdfddc3.tar.gz
Simple-f6c0b12e6cc1af4879042c0426fc96532cdfddc3.zip
Chapter 20: Graph Coloring Register Allocation
Co-authored-by: Co-authored-by: Cliff Click <cliffc@acm.org> Co-authored-by: Co-authored-by: godalming123 <68993177+godalming123@users.noreply.github.com> Co-authored-by: Tamás Hadházy <85063808+Hels15@users.noreply.github.com> Co-authored-by: RobertObkircher <robert.obkircher@gmail.com>
Diffstat (limited to 'src/main/java/com/seaofnodes')
-rw-r--r--src/main/java/com/seaofnodes/simple/Ary.java19
-rw-r--r--src/main/java/com/seaofnodes/simple/IterPeeps.java9
-rw-r--r--src/main/java/com/seaofnodes/simple/Parser.java4
-rw-r--r--src/main/java/com/seaofnodes/simple/RegMask.java47
-rw-r--r--src/main/java/com/seaofnodes/simple/SB.java1
-rw-r--r--src/main/java/com/seaofnodes/simple/codegen/BuildLRG.java91
-rw-r--r--src/main/java/com/seaofnodes/simple/codegen/CodeGen.java (renamed from src/main/java/com/seaofnodes/simple/CodeGen.java)108
-rw-r--r--src/main/java/com/seaofnodes/simple/codegen/GlobalCodeMotion.java (renamed from src/main/java/com/seaofnodes/simple/GlobalCodeMotion.java)72
-rw-r--r--src/main/java/com/seaofnodes/simple/codegen/IFG.java521
-rw-r--r--src/main/java/com/seaofnodes/simple/codegen/LRG.java220
-rw-r--r--src/main/java/com/seaofnodes/simple/codegen/ListScheduler.java (renamed from src/main/java/com/seaofnodes/simple/ListScheduler.java)107
-rw-r--r--src/main/java/com/seaofnodes/simple/codegen/Machine.java (renamed from src/main/java/com/seaofnodes/simple/Machine.java)13
-rw-r--r--src/main/java/com/seaofnodes/simple/codegen/RegAlloc.java502
-rw-r--r--src/main/java/com/seaofnodes/simple/codegen/RegMask.java142
-rw-r--r--src/main/java/com/seaofnodes/simple/node/AddNode.java6
-rw-r--r--src/main/java/com/seaofnodes/simple/node/AndNode.java9
-rw-r--r--src/main/java/com/seaofnodes/simple/node/BoolNode.java10
-rw-r--r--src/main/java/com/seaofnodes/simple/node/CFGNode.java28
-rw-r--r--src/main/java/com/seaofnodes/simple/node/CProjNode.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/CallEndNode.java22
-rw-r--r--src/main/java/com/seaofnodes/simple/node/CallNode.java27
-rw-r--r--src/main/java/com/seaofnodes/simple/node/CalleeSaveNode.java25
-rw-r--r--src/main/java/com/seaofnodes/simple/node/CastNode.java8
-rw-r--r--src/main/java/com/seaofnodes/simple/node/ConstantNode.java8
-rw-r--r--src/main/java/com/seaofnodes/simple/node/CtrlNode.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/DivFNode.java11
-rw-r--r--src/main/java/com/seaofnodes/simple/node/FunNode.java25
-rw-r--r--src/main/java/com/seaofnodes/simple/node/IfNode.java6
-rw-r--r--src/main/java/com/seaofnodes/simple/node/LoadNode.java4
-rw-r--r--src/main/java/com/seaofnodes/simple/node/LogicalNode.java4
-rw-r--r--src/main/java/com/seaofnodes/simple/node/MachConcreteNode.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/MachNode.java18
-rw-r--r--src/main/java/com/seaofnodes/simple/node/MemMergeNode.java1
-rw-r--r--src/main/java/com/seaofnodes/simple/node/MemOpNode.java10
-rw-r--r--src/main/java/com/seaofnodes/simple/node/MulNode.java4
-rw-r--r--src/main/java/com/seaofnodes/simple/node/NeverNode.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/Node.java104
-rw-r--r--src/main/java/com/seaofnodes/simple/node/PhiNode.java5
-rw-r--r--src/main/java/com/seaofnodes/simple/node/ProjNode.java1
-rw-r--r--src/main/java/com/seaofnodes/simple/node/RegionNode.java4
-rw-r--r--src/main/java/com/seaofnodes/simple/node/ReturnNode.java3
-rw-r--r--src/main/java/com/seaofnodes/simple/node/ScopeNode.java1
-rw-r--r--src/main/java/com/seaofnodes/simple/node/SplitNode.java22
-rw-r--r--src/main/java/com/seaofnodes/simple/node/StartNode.java5
-rw-r--r--src/main/java/com/seaofnodes/simple/node/StopNode.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/XCtrlNode.java3
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/AddARM.java34
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/AddFARM.java31
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/AddIARM.java51
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/AndARM.java38
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/AndIARM.java56
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/AsrARM.java40
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/AsrIARM.java41
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/BranchARM.java (renamed from src/main/java/com/seaofnodes/simple/node/cpus/riscv/CBranchRISC.java)27
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/CallARM.java45
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/CallEndARM.java35
-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.java (renamed from src/main/java/com/seaofnodes/simple/node/cpus/riscv/CmpRISC.java)20
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/CmpFARM.java31
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/CmpIARM.java54
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/CmpMemARM.java4
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/DivARM.java33
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/DivFARM.java30
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/FloatARM.java36
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/FunARM.java21
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/I2F8ARM.java29
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/IntARM.java38
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/LoadARM.java37
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/LslARM.java41
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/LslIARM.java42
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/LsrARM.java40
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/LsrIARM.java41
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/MemOpARM.java58
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/MulARM.java34
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/MulFARM.java30
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/MulIARM.java (renamed from src/main/java/com/seaofnodes/simple/node/cpus/riscv/MulIRISC.java)22
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/NewARM.java31
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/NotARM.java26
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/OrARM.java42
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/OrIARM.java40
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/ParmARM.java25
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/ProjARM.java23
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/RetARM.java49
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/SetARM.java41
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/SplitARM.java22
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/StoreARM.java52
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/SubARM.java30
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/SubFARM.java32
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/SubIARM.java42
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/TFPARM.java37
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/XorARM.java41
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/XorIARM.java43
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/arm.java385
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/i2f8.java4
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/AddFRISC.java4
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/AddIRISC.java44
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/AddRISC.java17
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/AndIRISC.java41
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/AndRISC.java11
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/BranchRISC.java47
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/CallEndRISC.java35
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/CallRISC.java13
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/CallRRISC.java14
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/CastRISC.java18
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/CmpIRISC.java44
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/DivFRISC.java6
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/DivRISC.java9
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/FltRISC.java20
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/FunRISC.java7
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/I2F8RISC.java3
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/ImmRISC.java40
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/IntRISC.java28
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/LoadRISC.java30
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/MemOpRISC.java31
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/MulFRISC.java6
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/MulRISC.java7
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/NewRISC.java33
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/NotRISC.java25
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/OrIRISC.java40
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/OrRISC.java18
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/ParmRISC.java11
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/ProjRISC.java16
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/RetRISC.java30
-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.java32
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/SllIRISC.java41
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/SllRISC.java11
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/SplitRISC.java21
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/SraIRISC.java44
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/SraRISC.java12
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/SrlIRISC.java44
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/SrlRISC.java10
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/StoreRISC.java30
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/SubFRISC.java4
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/SubRISC.java6
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/TFPRISC.java18
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/XorIRISC.java42
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/XorRISC.java14
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/riscv.java428
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddFMemX86.java14
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddFX86.java4
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddIX86.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddMemX86.java7
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddX86.java6
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AndIX86.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AndX86.java7
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CallEndX86.java14
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CallRX86.java25
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CallX86.java28
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CastX86.java18
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CmpFX86.java29
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CmpIX86.java20
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CmpMemX86.java4
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CmpX86.java5
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/DivFX86.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/DivX86.java14
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/FltX86.java11
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/FunX86.java5
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/I2f8X86.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/IntX86.java24
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/JmpX86.java6
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/LeaX86.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/LoadX86.java6
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MemAddX86.java4
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MemOpX86.java18
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MulFX86.java4
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MulIX86.java11
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MulX86.java5
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/NewX86.java27
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/NotX86.java26
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/OrIX86.java7
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/OrX86.java4
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ParmX86.java14
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ProjX86.java8
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/RetX86.java31
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SarIX86.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SarX86.java15
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SetX86.java4
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShlIX86.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShlX86.java8
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShrIX86.java3
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShrX86.java4
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SplitX86.java21
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/StoreX86.java10
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SubFX86.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SubX86.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/TFPX86.java11
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/XorIX86.java7
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/XorX86.java36
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/x86_64_v2.java281
-rw-r--r--src/main/java/com/seaofnodes/simple/print/ASMPrinter.java (renamed from src/main/java/com/seaofnodes/simple/ASMPrinter.java)79
-rw-r--r--src/main/java/com/seaofnodes/simple/print/GraphVisualizer.java (renamed from src/main/java/com/seaofnodes/simple/GraphVisualizer.java)5
-rw-r--r--src/main/java/com/seaofnodes/simple/print/IRPrinter.java (renamed from src/main/java/com/seaofnodes/simple/IRPrinter.java)59
-rw-r--r--src/main/java/com/seaofnodes/simple/print/JSViewer.java (renamed from src/main/java/com/seaofnodes/simple/JSViewer.java)11
-rw-r--r--src/main/java/com/seaofnodes/simple/print/SimpleWebSocket.java (renamed from src/main/java/com/seaofnodes/simple/SimpleWebSocket.java)3
-rw-r--r--src/main/java/com/seaofnodes/simple/type/TypeFunPtr.java4
-rw-r--r--src/main/java/com/seaofnodes/simple/type/TypeStruct.java1
-rw-r--r--src/main/java/com/seaofnodes/simple/type/TypeTuple.java2
199 files changed, 5409 insertions, 1310 deletions
diff --git a/src/main/java/com/seaofnodes/simple/Ary.java b/src/main/java/com/seaofnodes/simple/Ary.java
index 73ada8e0..f43179ad 100644
--- a/src/main/java/com/seaofnodes/simple/Ary.java
+++ b/src/main/java/com/seaofnodes/simple/Ary.java
@@ -29,6 +29,10 @@ public class Ary<E> extends AbstractList<E> implements List<E> {
range_check(i);
return _es[i];
}
+ public E atX( int i ) {
+ if( i >= _len ) return null;
+ return _es[i];
+ }
/** @return last element */
public E last( ) { return at(_len-1); }
@@ -76,6 +80,17 @@ public class Ary<E> extends AbstractList<E> implements List<E> {
return e;
}
+ /** Slow, linear-time, element insertion. Preserves order.
+ * @param i where to insert
+ * @return element inserted */
+ public E insert( E e, int i ) {
+ setLen(_len+1);
+ System.arraycopy(_es,i,_es,i+1,_len-i);
+ _es[i] = e;
+ return e;
+ }
+
+
/** Remove all elements */
public void clear( ) { _len=0; }
@@ -158,10 +173,6 @@ public class Ary<E> extends AbstractList<E> implements List<E> {
/** @return compact array version */
public E[] asAry() { return Arrays.copyOf(_es,_len); }
- /** Sorts in-place
- * @param c Comparator to sort by */
- public void sort_update(Comparator<? super E> c ) { Arrays.sort(_es, 0, _len, c); }
-
/** Find the first matching element using ==, or -1 if none. Note that
* most del calls shuffle the list, so the first element might be random.
* @param e Element to find
diff --git a/src/main/java/com/seaofnodes/simple/IterPeeps.java b/src/main/java/com/seaofnodes/simple/IterPeeps.java
index 265e0b21..6208209f 100644
--- a/src/main/java/com/seaofnodes/simple/IterPeeps.java
+++ b/src/main/java/com/seaofnodes/simple/IterPeeps.java
@@ -1,7 +1,8 @@
package com.seaofnodes.simple;
+import com.seaofnodes.simple.codegen.CodeGen;
import com.seaofnodes.simple.node.*;
-
+import com.seaofnodes.simple.print.JSViewer;
import java.util.Arrays;
import java.util.BitSet;
import java.util.Random;
@@ -46,7 +47,7 @@ public class IterPeeps {
private final WorkList<Node> _work;
- IterPeeps( long seed ) { _work = new WorkList<>(seed); }
+ public IterPeeps( long seed ) { _work = new WorkList<>(seed); }
public <N extends Node> N add( N n ) { return (N)_work.push(n); }
@@ -136,7 +137,7 @@ public class IterPeeps {
/* Useful stat - how many nodes are processed in the post parse iterative opt */
private long _totalWork = 0;
- WorkList() { this(123); }
+ public WorkList() { this(123); }
WorkList(long seed) {
_es = new Node[1];
_len=0;
@@ -177,7 +178,7 @@ public class IterPeeps {
/**
* Removes a random Node from the WorkList; null if WorkList is empty
*/
- E pop() {
+ public E pop() {
if( _len == 0 ) return null;
int idx = _R.nextInt(_len);
E x = (E)_es[idx];
diff --git a/src/main/java/com/seaofnodes/simple/Parser.java b/src/main/java/com/seaofnodes/simple/Parser.java
index 19822738..07f82625 100644
--- a/src/main/java/com/seaofnodes/simple/Parser.java
+++ b/src/main/java/com/seaofnodes/simple/Parser.java
@@ -1,6 +1,8 @@
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 java.text.ParseException;
import java.util.*;
@@ -1413,7 +1415,7 @@ public class Parser {
mem.addDef(new ProjNode(cend,1,ScopeNode.MEM0).peephole());
_scope.mem(mem);
// Call result
- return new ProjNode(cend,2,null).peephole();
+ return new ProjNode(cend,2,"#2").peephole();
}
/**
diff --git a/src/main/java/com/seaofnodes/simple/RegMask.java b/src/main/java/com/seaofnodes/simple/RegMask.java
deleted file mode 100644
index 073b472b..00000000
--- a/src/main/java/com/seaofnodes/simple/RegMask.java
+++ /dev/null
@@ -1,47 +0,0 @@
-package com.seaofnodes.simple;
-
-import java.util.BitSet;
-
-// A "register mask" - 1 bit set for each allowed register. In addition "stack
-// slot" registers may be allowed, effectively making the set infinite.
-public class RegMask {
-
- public static final RegMask EMPTY = new RegMask();
-
- final BitSet _bits;
-
- public RegMask(long x ) { _bits = BitSet.valueOf(new long[]{x}); }
- public RegMask(long[] xs) { _bits = BitSet.valueOf( xs); }
-
- public RegMask or(RegMask other) {
- BitSet resultBits = (BitSet)this._bits.clone();
- resultBits.or(other._bits);
- return new RegMask(resultBits);
- }
-
- // Internal constructor
- RegMask() { _bits = new BitSet(); }
- RegMask(BitSet bs) { _bits = bs; }
-
- public boolean get(int r) { return _bits.get(r); }
-
- // Defensive writable copy
- public RegMaskRW copy() { return new RegMaskRW( (BitSet)_bits.clone() ); }
-
- boolean size1() { return _bits.cardinality()==1; }
-}
-
-class RegMaskRW extends RegMask {
- public RegMaskRW() { super(); }
- public RegMaskRW(long x) { super(new long[]{x}); }
- public RegMaskRW(long[] xs) { super(xs); }
- public RegMaskRW(BitSet bs) { super(bs); }
- public void set(int r) { _bits.set(r); }
- public void clr(int r) { _bits.clear(r); }
- public void set(int r, boolean b) { _bits.set(r,b); }
- public RegMaskRW and( RegMask r ) {
- if( r==null ) return this;
- _bits.and(r._bits);
- return this;
- }
-}
diff --git a/src/main/java/com/seaofnodes/simple/SB.java b/src/main/java/com/seaofnodes/simple/SB.java
index 7d580c21..a45f0ddf 100644
--- a/src/main/java/com/seaofnodes/simple/SB.java
+++ b/src/main/java/com/seaofnodes/simple/SB.java
@@ -40,7 +40,6 @@ public final class SB {
}
// Fixed width field
public SB fix( int sz, String s ) {
- assert sz >=0;
for( int i=0; i<sz; i++ )
_sb.append( i < s.length() ? s.charAt(i) : ' ');
return this;
diff --git a/src/main/java/com/seaofnodes/simple/codegen/BuildLRG.java b/src/main/java/com/seaofnodes/simple/codegen/BuildLRG.java
new file mode 100644
index 00000000..88b49863
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/codegen/BuildLRG.java
@@ -0,0 +1,91 @@
+package com.seaofnodes.simple.codegen;
+
+import com.seaofnodes.simple.Utils;
+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 mach ) 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);
+ if( !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 )
+ 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.java b/src/main/java/com/seaofnodes/simple/codegen/CodeGen.java
index eb8dd4aa..a1294890 100644
--- a/src/main/java/com/seaofnodes/simple/CodeGen.java
+++ b/src/main/java/com/seaofnodes/simple/codegen/CodeGen.java
@@ -1,7 +1,8 @@
- package com.seaofnodes.simple;
+package com.seaofnodes.simple.codegen;
+import com.seaofnodes.simple.*;
import com.seaofnodes.simple.node.*;
-import com.seaofnodes.simple.node.cpus.x86_64_v2.MemOpX86;
+import com.seaofnodes.simple.print.*;
import com.seaofnodes.simple.type.*;
import java.util.BitSet;
import java.util.HashMap;
@@ -16,14 +17,11 @@ public class CodeGen {
Parse, // Parse ASCII text into Sea-of-Nodes IR
Opto, // Run ideal optimizations
TypeCheck, // Last check for bad programs
- InstructionSelection, // Convert to target hardware nodes
+ InstSelect, // Convert to target hardware nodes
Schedule, // Global schedule (code motion) nodes
LocalSched, // Local schedule
- RegAlloc; // Register allocation
- }
- public enum CallingConv {
- Win64,
- SystemV
+ RegAlloc, // Register allocation
+ Encoding, // Encoding
}
public Phase _phase;
@@ -32,7 +30,6 @@ public class CodeGen {
public final String _src;
// Compile-time known initial argument type
public final TypeInteger _arg;
- public CallingConv _callingConv;
// ---------------------------
public CodeGen( String src ) { this(src, TypeInteger.BOT, 123L ); }
@@ -54,9 +51,11 @@ public class CodeGen {
* A counter, for unique node id generation. Starting with value 1, to
* avoid bugs confusing node ID 0 with uninitialized values.
*/
- private int _uid = 1;
+ public int _uid = 1;
public int UID() { return _uid; }
- public int getUID() { return _uid++; }
+ public int getUID() {
+ return _uid++;
+ }
// Next available memory alias number
private int _alias = 2; // 0 is for control, 1 for memory
@@ -108,11 +107,14 @@ public class CodeGen {
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;
}
@@ -130,12 +132,15 @@ public class CodeGen {
public void iterNop() { if( !_midAssert ) _iter_nop_cnt++; }
// Run ideal optimizations
+ public int _tOpto;
public CodeGen opto() {
assert _phase == Phase.Parse;
_phase = Phase.Opto;
+ long t0 = System.currentTimeMillis();
// Pessimistic peephole optimization on a worklist
_iter.iterate(this);
+ _tOpto = (int)(System.currentTimeMillis() - t0);
// TODO:
// Optimistic
@@ -149,13 +154,16 @@ public class CodeGen {
// ---------------------------
// Last check for bad programs
+ int _tTypeCheck;
public CodeGen typeCheck() {
// Demand phase Opto for cleaning up dead control flow at least,
// required for the following GCM.
assert _phase.ordinal() <= Phase.Opto.ordinal();
_phase = Phase.TypeCheck;
+ long t0 = System.currentTimeMillis();
Parser.ParseException err = _stop.walk( Node::err );
+ _tTypeCheck = (int)(System.currentTimeMillis() - t0);
if( err != null )
throw err;
return this;
@@ -165,25 +173,26 @@ public class CodeGen {
// ---------------------------
// Code generation CPU target
public Machine _mach;
+ //
+ public String _callingConv;
// Convert to target hardware nodes
- public CodeGen instSelect( String cpu, String callingConv ) {
+ public int _tInsSel;
+ public CodeGen instSelect( String base, String cpu, String callingConv ) {
assert _phase.ordinal() <= Phase.TypeCheck.ordinal();
- _phase = Phase.InstructionSelection;
+ _phase = Phase.InstSelect;
- _callingConv = switch(callingConv) {
- case "SystemV" -> CallingConv.SystemV;
- case "Win64" -> CallingConv.Win64;
- default -> throw Utils.TODO(); // Unknown calling convention
- };
+ _callingConv = callingConv;
+ // Not timing the class load...
// Look for CPU in fixed named place:
// com.seaofnodes.simple.node.cpus."cpu"."cpu.class"
- String clzFile = "com.seaofnodes.simple.node.cpus."+cpu+"."+cpu;
+ String clzFile = base+"."+cpu+"."+cpu;
try { _mach = ((Class<Machine>) Class.forName( clzFile )).getDeclaredConstructor().newInstance(); }
catch( Exception e ) { throw new RuntimeException(e); }
// Convert to machine ops
+ long t0 = System.currentTimeMillis();
_uid = 1; // All new machine nodes reset numbering
var map = new IdentityHashMap<Node,Node>();
_instSelect( _stop, map );
@@ -191,6 +200,7 @@ public class CodeGen {
_start = (StartNode)map.get(_start);
_instOuts(_stop,visit());
_visit.clear();
+ _tInsSel = (int)(System.currentTimeMillis() - t0);
return this;
}
@@ -201,10 +211,10 @@ public class CodeGen {
Node x = map.get(n);
if( x !=null ) return x; // Been there, done that
- // If n is a MachConcrete, then its part of a multi-node expansion.
+ // 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 MachConcreteNode || n instanceof MemOpX86) {
+ if( n instanceof MachNode ) {
for( int i=0; i < n.nIns(); i++ )
n._inputs.set(i, _instSelect(n.in(i),map) );
return n;
@@ -237,17 +247,20 @@ public class CodeGen {
// ---------------------------
// Control Flow Graph in RPO order.
- Ary<CFGNode> _cfg = new Ary<>(CFGNode.class);
+ 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.InstructionSelection.ordinal();
+ assert _phase.ordinal() <= Phase.InstSelect.ordinal();
_phase = Phase.Schedule;
+ long t0 = System.currentTimeMillis();
// Build the loop tree, fix never-exit loops
_start.buildLoopTree(_stop);
GlobalCodeMotion.buildCFG(this);
+ _tGCM = (int)(System.currentTimeMillis() - t0);
if( show )
System.out.println(new GraphVisualizer().generateDotOutput(_stop,null,null));
return this;
@@ -255,33 +268,70 @@ public class CodeGen {
// ---------------------------
// Local (basic block) scheduler phase, a classic list scheduler
+ int _tLocal;
public CodeGen localSched() {
assert _phase == Phase.Schedule;
_phase = Phase.LocalSched;
+ long t0 = System.currentTimeMillis();
ListScheduler.sched(this);
+ _tLocal = (int)(System.currentTimeMillis() - t0);
return this;
}
// ---------------------------
+ // Register Allocation
+ int _tRegAlloc;
+ public RegAlloc _regAlloc;
+ public CodeGen regAlloc() {
+ assert _phase == Phase.LocalSched;
+ _phase = Phase.RegAlloc;
+ long t0 = System.currentTimeMillis();
+ _regAlloc = new RegAlloc(this);
+ _regAlloc.regAlloc();
+ _tRegAlloc = (int)(System.currentTimeMillis() - t0);
+ return this;
+ }
public String reg(Node n) {
- if( _phase == Phase.RegAlloc ) throw Utils.TODO();
+ if( _phase.ordinal() >= Phase.RegAlloc.ordinal() ) {
+ String s = _regAlloc.reg(n);
+ if( s!=null ) return s;
+ }
return "N"+ n._nid;
}
+ // ---------------------------
+ // Encoding
+ int _tEncode;
+ public CodeGen encode() {
+ assert _phase == Phase.RegAlloc;
+ _phase = Phase.Encoding;
+ long t0 = System.currentTimeMillis();
+
+
+ //_tEncode = (int)(System.currentTimeMillis() - t0);
+ //throw Utils.TODO();
+ return this;
+ }
+ // Encoded binary, no relocation info
+ public byte[] binary() { throw Utils.TODO(); }
// ---------------------------
SB asm(SB sb) { return ASMPrinter.print(sb,this); }
- String asm() { return asm(new SB()).toString(); }
+ public String asm() { return asm(new SB()).toString(); }
// Testing shortcuts
- Node ctrl() { return _stop.ret().ctrl(); }
- Node expr() { return _stop.ret().expr(); }
- String print() { return _stop.print(); }
+ 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 _stop.p(9999); }
+ @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); }
diff --git a/src/main/java/com/seaofnodes/simple/GlobalCodeMotion.java b/src/main/java/com/seaofnodes/simple/codegen/GlobalCodeMotion.java
index 12dd9243..11c2b061 100644
--- a/src/main/java/com/seaofnodes/simple/GlobalCodeMotion.java
+++ b/src/main/java/com/seaofnodes/simple/codegen/GlobalCodeMotion.java
@@ -1,6 +1,8 @@
- package com.seaofnodes.simple;
+package com.seaofnodes.simple.codegen;
+import com.seaofnodes.simple.Ary;
import com.seaofnodes.simple.IterPeeps.WorkList;
+import com.seaofnodes.simple.Utils;
import com.seaofnodes.simple.node.*;
import com.seaofnodes.simple.type.*;
import java.util.*;
@@ -14,32 +16,70 @@ public abstract class GlobalCodeMotion {
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++ ) {
- int j = rpo.size()-1-i;
- CFGNode tmp = rpo.get(i);
- rpo.set(i, rpo.get(j));
- rpo.set(j, tmp);
- }
+ 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);
+
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 CallNode call ) call.unlink_all();
if( !(use instanceof CFGNode cfg) || visit.get(cfg._nid) )
return; // Been there, done that
- if( def instanceof CallNode && cfg instanceof FunNode )
- return; // Ignore linked function calls
+ 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( CodeGen code ) {
+ // For all global constants
+ for( int i=0; i< code._start.nOuts(); i++ ) {
+ Node con = code._start.out(i);
+ if( con instanceof MachNode mach && mach.isClone() ) {
+ // 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.cfg0().fun();
+ if( fun==null || fun==fun2 ) fun=fun2;
+ else { done=false; break; }
+ }
+ // Single function user, so this constant is not shared
+ if( done ) { 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
@@ -50,30 +90,30 @@ public abstract class GlobalCodeMotion {
for( CFGNode cfg : code._cfg ) {
cfg.loopDepth();
for( Node n : cfg._inputs )
- _schedEarly(n,code._visit );
+ _schedEarly(n,code );
if( cfg instanceof RegionNode )
for( Node phi : cfg._outputs )
if( phi instanceof PhiNode )
- _schedEarly(phi,code._visit );
+ _schedEarly(phi,code );
}
}
- private static void _schedEarly(Node n, BitSet visit) {
- if( n==null || visit.get(n._nid) ) return; // Been there, done that
+ 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);
- visit.set(n._nid);
+ 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,visit);
+ _schedEarly(def,code);
// If not-pinned (e.g. constants, projections, phi) and not-CFG
if( !n.isPinned() ) {
// Schedule at deepest input
- CFGNode early = CodeGen.CODE._start; // Maximally early, lowest idepth
+ 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() )
diff --git a/src/main/java/com/seaofnodes/simple/codegen/IFG.java b/src/main/java/com/seaofnodes/simple/codegen/IFG.java
new file mode 100644
index 00000000..1f3186ff
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/codegen/IFG.java
@@ -0,0 +1,521 @@
+package com.seaofnodes.simple.codegen;
+
+import com.seaofnodes.simple.Ary;
+import com.seaofnodes.simple.Utils;
+import com.seaofnodes.simple.node.*;
+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
+ private 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());
+
+ 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
+ }
+
+ if( n instanceof PhiNode )
+ return;
+ // A copy does not define a new value, and the src and dst can use the
+ // same register. Remove the input from TMP/liveout set before
+ // interfering.
+ //if( n instanceof MachNode m && m.isSplit() )
+ // TMP.remove(alloc.lrg(n.in(1))); // Kill spill-use
+
+ // Interfere n with all live
+ if( lrg!=null ) {
+ if( n instanceof MachNode m )
+ kills(alloc,m);
+ // 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( lrg.size1() && !tlrg.clr(lrg._mask.firstReg()) )
+ alloc.fail(tlrg);
+ 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.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.
+ if( m.isClone() )
+ alloc.fail(alloc.lrg((Node)m));
+ else if( !tlrg.sub(killMask) ) {
+ tlrg._killed = true; // Failed by a kill-mask
+ 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
+ if( !bb.blockHead() ) bb = bb.cfg0();
+ //if( i==0 && !(bb instanceof StartNode) ) bb = bb.cfg0();
+ assert bb.blockHead();
+
+ // 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);
+ }
+ }
+ }
+
+
+ // ------------------------------------------------------------------------
+ // Color the inference graph.
+
+ // Coloring works by removing "trivial" live ranges from the IFG - those
+ // live ranges with fewer neighbors than available colors (registers).
+ // These are trivial because even if every neighbor takes a unique color,
+ // there's at least one more available to color this live range.
+
+ // If we hit a clique of non-trivial live ranges, we pick one to be "at
+ // risk" of not-coloring. Good choices include live ranges with a large
+ // area and few hot uses.
+
+ // Then we reverse and put-back live ranges - picking a color as we go.
+ // If there's no spare color we'll have to spill this at-risk live range.
+
+ public static boolean color(int round, RegAlloc alloc) {
+
+ // Convert the 2-D array of bits (a 1-D array of BitSets) into an
+ // adjacency matrix.
+ int maxlrg = alloc._LRGS.length, nlrgs=0;
+ for( int i=1; i<maxlrg; i++ ) {
+ if( alloc._LRGS[i] != null ) nlrgs++;
+ 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);
+ }
+ }
+ }
+
+ // 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 == 999999 ) 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 ) {
+ // Always pick callee-save registers as being very large area recovered
+ // and very cheap to spill.
+ if( lrg._machDef instanceof CalleeSaveNode )
+ return 999998;
+ if( lrg._splitDef != null && lrg._splitDef.in(1) instanceof CalleeSaveNode &&
+ lrg._splitUse != null && lrg._splitUse.out(0) instanceof ReturnNode )
+ return 999999;
+
+ // TODO: cost/benefit model. Perhaps counting loop-depth (freq) of def/use for cost
+ // and "area" for benefit
+ return 1000;
+ }
+
+ private static short biasColor( RegAlloc alloc, LRG lrg, short reg, RegMask mask ) {
+ if( mask.size1() ) return reg;
+ // Check chain of splits up the def-chain. Take first allocated
+ // register, and if it's available in the mask, take it.
+ Node defSplit = lrg._splitDef, useSplit = lrg._splitUse;
+ int tidx, cnt=0;
+
+ while( (tidx=biasable(defSplit)) != 0 || biasable(useSplit) != 0 ) {
+ if( cnt++ > 10 ) break;
+
+ if( tidx != 0 ) {
+ short bias = biasColor( alloc, defSplit, mask );
+ if( bias >= 0 ) return bias; // Good bias
+ if( bias == -2 ) defSplit = null; // Kill this side, no more searching
+ } else defSplit = null;
+
+ if( biasable(useSplit) != 0 ) {
+ short bias = biasColor( alloc, useSplit, mask );
+ if( bias >= 0 ) return bias; // Good bias
+ if( bias == -2 ) useSplit = null; // Kill this side, no more searching
+ } else useSplit = null;
+
+ if( defSplit != null ) {
+ short bias = biasColorNeighbors( alloc, defSplit, mask );
+ if( bias >= 0 ) return bias;
+ // Advance def side
+ defSplit = defSplit.in(tidx);
+ }
+
+ if( useSplit != null ) {
+ short bias = biasColorNeighbors( alloc, useSplit, mask );
+ if( bias >= 0 ) return bias;
+ useSplit = useSplit.out(0);
+ }
+
+ }
+ return mask.firstReg();
+ }
+
+ private static int biasable(Node split) {
+ if( split instanceof SplitNode ) return 1; // Yes biasable, advance is slot 1
+ if( split instanceof PhiNode ) return 1; // Yes biasable, advance is slot 1
+ if( !(split instanceof MachNode mach) ) return 0; // Not biasable
+ return mach.twoAddress(); // Only biasable if 2-addr
+ }
+
+ // 3-way return:
+ // - good bias reg, take it & exit
+ // - this path is cutoff; do not search here anymore
+ // - advance this side
+ private static short biasColor( RegAlloc alloc, Node split, RegMask mask ) {
+ short bias = alloc.lrg(split)._reg;
+ if( bias != -1 ) {
+ if( mask.test(bias) ) return bias; // Good bias
+ else return -2; // Kill this side
+ } else return -1; // Advance this side
+ }
+
+ // Check if we can match "split" color, or else trim "mask" to colors
+ // "split" might get.
+ private static short biasColorNeighbors( RegAlloc alloc, Node split, RegMask mask ) {
+ LRG slrg = alloc.lrg(split);
+ if( slrg._adj == null ) return -1; // No trimming
+
+ // Can I limit my own choices to valid neighbor choices?
+ for( LRG alrg : slrg._adj ) {
+ int reg = alrg._reg;
+ if( reg == -1 && alrg._mask.size1() )
+ reg = alrg._mask.firstReg();
+ if( reg != -1 ) {
+ mask.clr(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
new file mode 100644
index 00000000..394c4a01
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/codegen/LRG.java
@@ -0,0 +1,220 @@
+package com.seaofnodes.simple.codegen;
+
+import com.seaofnodes.simple.Ary;
+import com.seaofnodes.simple.SB;
+import com.seaofnodes.simple.node.*;
+
+import java.util.IdentityHashMap;
+
+/** A Live Range
+ * <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;
+
+ // A sample MachNode def in the live range
+ public MachNode _machDef, _machUse;
+ short _uidx; // _machUse input
+
+ // Mask set to empty via a kill-mask. This is usually a capacity kill,
+ // which means we need to split and spill into memory - which means even if
+ // the def-side has many registers, it MUST spill.
+ boolean _killed;
+
+ // 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;
+
+ void addNeighbor(LRG lrg) {
+ if( _adj==null ) _adj = new Ary<>(LRG.class);
+ _adj.push(lrg);
+ }
+
+ // Remove and compress neighbor list; store ex-neighbor past
+ // end for unwinding Simplify. True if exactly going low-degree
+ boolean removeCompress( LRG lrg ) {
+ _adj.swap(_adj.find(lrg),_adj._len-1);
+ return _adj._len-- == _mask.size();
+ }
+
+ void reinsert( LRG lrg ) {
+ assert _adj._es[_adj._len]==lrg;
+ _adj._len++;
+ }
+
+ // More registers than neighbors
+ boolean lowDegree() { return (_adj==null ? 0 : _adj._len) < _mask.size(); }
+
+ LRG( short lrg ) { _lrg = lrg; _reg = -1; }
+
+ boolean leader() { return _leader == null; }
+
+ // Fast-path Find from the Union-Find algorithm
+ LRG find() {
+ if( _leader==null ) return this; // I am the leader
+ if( _leader._leader==null ) // I point to the leader
+ return _leader;
+ return _rollup();
+ }
+ // Slow-path rollup of U-F
+ private LRG _rollup() {
+ LRG ldr = _leader._leader;
+ // Roll-up
+ while( ldr._leader!=null ) ldr = ldr._leader;
+ LRG l2 = this;
+ while( l2 != ldr ) {
+ LRG l3 = l2._leader;
+ l2._leader = ldr;
+ l2 = l3;
+ }
+ return ldr;
+ }
+
+ // Union `this` and `lrg`, keeping the lower numbered _lrg.
+ // Includes a number of fast-path cutouts.
+ LRG union( LRG lrg ) {
+ assert leader();
+ if( lrg==null ) return this;
+ lrg = lrg.find();
+ if( lrg==this ) return this;
+ return _lrg < lrg._lrg ? _union(lrg) : lrg._union(this);
+ }
+ // Union `this` and `lrg`, folding together all stats.
+ private LRG _union( LRG lrg ) {
+ // Set U-F leader
+ lrg._leader = this;
+ // Fold together stats
+ if( _machDef==null ) {
+ _machDef = lrg._machDef;
+ } else if( lrg._machDef!=null ) {
+ if( _1regDefCnt==0 )
+ _machDef = lrg._machDef;
+ else if( _machDef==lrg._machDef )
+ _1regDefCnt--;
+ }
+ _1regDefCnt += lrg._1regDefCnt;
+
+ if( _machUse==null ) {
+ _machUse = lrg._machUse;
+ } else if( lrg._machUse!=null ) {
+ if( _1regUseCnt==0 )
+ _machUse = lrg._machUse;
+ else if( _machUse==lrg._machUse )
+ _1regUseCnt--;
+ }
+ _1regUseCnt += lrg._1regUseCnt;
+
+ // Fold deepest Split
+ _splitDef = deepSplit(_splitDef,lrg._splitDef);
+ _splitUse = deepSplit(_splitUse,lrg._splitUse);
+
+ // Fold together masks
+ _mask = _mask.and(lrg._mask);
+ return this;
+ }
+
+ private static MachConcreteNode deepSplit( MachConcreteNode s0, MachConcreteNode s1 ) {
+ return s0==null || (s1!=null && s0.cfg0().loopDepth() < s1.cfg0().loopDepth()) ? s1 : s0;
+ }
+
+ // Record any Mach def for spilling heuristics
+ LRG machDef( MachNode def, boolean size1 ) {
+ if( _machDef==null || size1 )
+ _machDef = def;
+ if( size1 )
+ _1regDefCnt++;
+ if( def instanceof SplitNode split )
+ _splitDef = deepSplit(_splitDef,split);
+ return this;
+ }
+
+ // Record any Mach use for spilling heuristics
+ LRG machUse( MachNode use, short uidx, boolean size1 ) {
+ if( _machUse==null || size1 )
+ { _machUse = use; _uidx = uidx; }
+ if( size1 )
+ _1regUseCnt++;
+ if( use instanceof SplitNode split )
+ _splitUse = deepSplit(_splitUse,split);
+ return this;
+ }
+
+ boolean hasSplit() { return _splitDef != null || _splitUse != null; }
+ short size() { return _mask.size(); }
+ boolean size1() { return _mask.size1(); }
+
+
+ void selfConflict( Node def ) {
+ if( _selfConflicts == null ) _selfConflicts = new IdentityHashMap<>();
+ _selfConflicts.put(def,"");
+ }
+
+
+ // Record intersection of all register masks.
+ // True if still has registers
+ boolean and( RegMask mask ) {
+ RegMask mask2 = mask.and(_mask);
+ if( mask2==null )
+ mask2 = _mask.copy().and(mask);
+ _mask = mask2;
+ return !_mask.isEmpty();
+ }
+ // Remove this singular register
+ // True if still has registers
+ boolean clr( int reg ) {
+ if( _mask.clr(reg) ) return true;
+ _mask = _mask.copy(); // Need a mutable copy
+ return _mask.clr(reg);
+ }
+ // Subtract mask (AND complement)
+ // True if still has registers
+ boolean sub( RegMask mask ) {
+ RegMask mask2 = _mask.sub(mask);
+ if( mask2==null )
+ mask2 = _mask.copy().sub(mask);
+ _mask = mask2;
+ return !_mask.isEmpty();
+ }
+
+ @Override public String toString() { return toString(new SB()).toString(); }
+
+ public SB toString( SB sb ) {
+ sb.p("V").p(_lrg);
+ if( _mask!=null ) _mask.toString(sb);
+ return sb;
+ }
+}
diff --git a/src/main/java/com/seaofnodes/simple/ListScheduler.java b/src/main/java/com/seaofnodes/simple/codegen/ListScheduler.java
index efca945a..1796ef7c 100644
--- a/src/main/java/com/seaofnodes/simple/ListScheduler.java
+++ b/src/main/java/com/seaofnodes/simple/codegen/ListScheduler.java
@@ -1,6 +1,8 @@
- package com.seaofnodes.simple;
+package com.seaofnodes.simple.codegen;
+import com.seaofnodes.simple.Ary;
import com.seaofnodes.simple.IterPeeps.WorkList;
+import com.seaofnodes.simple.Utils;
import com.seaofnodes.simple.node.*;
import com.seaofnodes.simple.type.*;
import java.util.*;
@@ -8,8 +10,17 @@ 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();
@@ -17,7 +28,7 @@ public abstract class ListScheduler {
}
void free() { _n = null; FREE.push(this); }
- static XSched get(Node n) { return n==null ? null : XS.get(n); }
+ 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;
@@ -29,11 +40,11 @@ public abstract class ListScheduler {
for( Node def : n._inputs )
if( def != null && def != bb ) {
if( def.cfg0()==bb ) _bcnt++; // Raise block-local input count
- else _ruse = true;
+ else if( !def.isMem() ) _ruse = true; // Remote register user
}
// Count remote outputs and allowed registers
if( n instanceof MultiNode )
- for( Node use : n._inputs )
+ for( Node use : n._outputs )
computeSingleRDef(bb,use);
else
computeSingleRDef(bb,n);
@@ -41,7 +52,14 @@ public abstract class ListScheduler {
}
private void computeSingleRDef(CFGNode bb, Node n) {
- RegMaskRW rmask = n instanceof MachNode mach && mach.outregmap()!=null ? mach.outregmap().copy() : null;
+ // 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;
@@ -52,7 +70,7 @@ public abstract class ListScheduler {
rmask.and(mach.regmap(i));
}
// Defines in a single register
- _single = rmask!=null && rmask.size1();
+ _single |= rmask!=null && rmask.size1();
}
// If _bcnt==0, declare ready; move user bcnts into rcnts.
@@ -82,13 +100,6 @@ public abstract class ListScheduler {
_rcnt--;
return isReady();
}
-
- Node _n; // Node this extra is for
- int _bcnt; // Not-ready not-scheduled inputs
- int _rcnt; // Ready not-scheduled inputs
- boolean _ruse; // Node IS a "remote use", uses a other-block value
- boolean _rdef; // Node IS a "remote def" of a value used in other block
- boolean _single; // Defines a single register, likely to conflict
}
@@ -97,11 +108,11 @@ public abstract class ListScheduler {
XS.clear();
for( CFGNode bb : code._cfg )
if( bb.blockHead() )
- local(bb);
+ local(bb,false);
}
// Classic list scheduler
- private static void local( CFGNode bb ) {
+ private static void local( CFGNode bb, boolean trace ) {
assert XS.isEmpty();
int len = bb.nOuts();
@@ -126,7 +137,7 @@ public abstract class ListScheduler {
int sched = 0;
while( sched < len ) {
assert sched < ready;
- int pick = best(bb._outputs,sched,ready); // Pick best
+ 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.
@@ -145,7 +156,7 @@ public abstract class ListScheduler {
}
private static int ready(CFGNode bb, Node use, int ready) {
- if( use!=null && !(use instanceof PhiNode) && use.in(0)==bb && XSched.get(use).decIsReady() )
+ 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;
@@ -153,14 +164,19 @@ public abstract class ListScheduler {
// Pick best between sched and ready.
- private static int best( Ary<Node> blk, int sched, int ready ) {
+ private static int best( Ary<Node> blk, int sched, int ready, boolean trace ) {
int pick = sched;
- int score = score(blk.at(pick));
+ 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++ ) {
- int nscore = score(blk.at(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; }
+ { score = nscore; pick = i; p = n; }
}
+ if( trace ) { System.out.printf("Pick: N%d %s\n\n",p._nid,p); }
return pick;
}
@@ -180,9 +196,9 @@ public abstract class ListScheduler {
// Register pressure local scheduling: avoid overlapping specialized
// registers usages.
- // Subtract 10 if this op forces a live range to exist that cannot be
- // resolved immediately; i.e., live count goes up. Scale to 20 if
- // multi-def.
+ // Subtract 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.
@@ -190,42 +206,53 @@ public abstract class ListScheduler {
// 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 )
- singleUseNotReady( use, xn._single );
- } else if( n.nOuts() == 1 )
- singleUseNotReady( n, xn._single );
+ 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 && xd._n.nOuts()==1 )
+ 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
- //if( n instanceof Pe ) return 500; // RISC want to go after PE
- //if( n instanceof Risc ) return 400; // RISC want to go after PE
+ // 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.
- // Return 0 for false, 1 if true, 10 if also single register
- private static void singleUseNotReady( Node n, boolean single ) {
- if( n.nOuts() != 1 ) return;
- XSched xu = XSched.get(n.out(0));
- if( xu !=null && xu._bcnt==0 )
- return;
- CNT[single ? 2 : 1]++;
+ // 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/Machine.java b/src/main/java/com/seaofnodes/simple/codegen/Machine.java
index b29e9aba..13d7abc1 100644
--- a/src/main/java/com/seaofnodes/simple/Machine.java
+++ b/src/main/java/com/seaofnodes/simple/codegen/Machine.java
@@ -1,9 +1,6 @@
-package com.seaofnodes.simple;
+package com.seaofnodes.simple.codegen;
-import com.seaofnodes.simple.RegMask;
-import com.seaofnodes.simple.node.CFGNode;
-import com.seaofnodes.simple.node.IfNode;
-import com.seaofnodes.simple.node.Node;
+import com.seaofnodes.simple.node.*;
abstract public class Machine {
// Human readable machine name. Something like "x86-64" or "arm" or "risc5"
@@ -11,7 +8,11 @@ abstract public class Machine {
// Human-readable name for a register number, e.g. "RAX" or "R0"
public abstract String reg( int reg );
// Create a split op; any register to any register, including stack slots
- public abstract Node split();
+ public abstract SplitNode split( String kind, byte round, LRG lrg);
+ // List of caller-save registers
+ public abstract RegMask callerSave();
+ // List of callee-save registers
+ public abstract RegMask calleeSave();
// Return a MachNode unconditional branch
public abstract CFGNode jump();
// Break an infinite loop
diff --git a/src/main/java/com/seaofnodes/simple/codegen/RegAlloc.java b/src/main/java/com/seaofnodes/simple/codegen/RegAlloc.java
new file mode 100644
index 00000000..badec740
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/codegen/RegAlloc.java
@@ -0,0 +1,502 @@
+package com.seaofnodes.simple.codegen;
+
+import com.seaofnodes.simple.Ary;
+import com.seaofnodes.simple.Utils;
+import com.seaofnodes.simple.node.*;
+import java.util.Arrays;
+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 ) {
+ LRG lrg = lrg(n);
+ if( lrg==null ) return null;
+ if( lrg._reg == -1 ) return "V"+lrg._lrg;
+ return _code._mach.reg(lrg._reg);
+ }
+
+ // -----------------------
+ RegAlloc( CodeGen code ) { _code = code; }
+
+ public void regAlloc() {
+ // Insert callee-save registers
+ for( CFGNode bb : _code._cfg )
+ if( bb instanceof FunNode fun )
+ insertCalleeSave(fun);
+
+ // Top driver: repeated rounds of coloring and splitting.
+ byte round=0;
+ while( !graphColor(round) ) {
+ split(round);
+ assert round < 7; // Really expect to be done soon
+ 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
+ // Color attempt
+ IFG.color(round,this); // If colorable
+ }
+
+ // Insert callee-save registers. Walk the callee-save RegMask ignoring any
+ // Parms, then insert a Parm and an edge from the Ret to the Parm with the
+ // callee-save register.
+ private void insertCalleeSave( FunNode fun ) {
+ RegMask saves = _code._mach.calleeSave();
+ ReturnNode ret = fun.ret();
+
+ for( short reg = saves.firstReg(); reg != -1; reg = saves.nextReg(reg) ) {
+ ret.addDef(new CalleeSaveNode(fun,reg,_code._mach.reg(reg)));
+ assert ((MachNode)ret).regmap(ret.nIns()-1).firstReg()==reg;
+ }
+ }
+
+
+ // -----------------------
+ // Split conflicted live ranges.
+ void split(byte round) {
+
+ // In C2, all splits are handling in one pass over the program. Here,
+ // in the name of clarity, we'll handle each failing live range
+ // independently... which generally requires a full pass over the
+ // program for each failing live range. i.e., might be a lot of
+ // passes.
+ for( LRG lrg : _failed.keySet() )
+ split(round,lrg);
+ }
+
+ // Split this live range
+ boolean split( byte round, LRG lrg ) {
+ assert lrg.leader(); // Already rolled up
+
+ if( lrg._selfConflicts != null )
+ return splitSelfConflict(round,lrg);
+
+ // Register mask when empty; split around defs and uses with limited
+ // register masks.
+ if( lrg._mask.isEmpty() ) {
+ if( lrg._1regDefCnt <= 1 &&
+ lrg._1regUseCnt <= 1 &&
+ (lrg._1regDefCnt + lrg._1regUseCnt) > 0 )
+ return splitEmptyMaskSimple(round,lrg);
+ // Default to splitByLoop
+ //return splitEmptyMask(round,lrg);
+ }
+
+ // 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
+ makeSplit("def/empty1",round,lrg).insertAfter((Node)lrg._machDef, false/*true*/);
+ // Split just before use
+ if( lrg._1regUseCnt==1 )
+ insertBefore((Node)lrg._machUse,lrg._uidx,"use/empty1",round,lrg);
+ return true;
+ }
+
+ // Split live range with an empty mask. Specifically forces splits at
+ // single-register defs or uses everywhere.
+ boolean splitEmptyMask( byte round, LRG lrg ) {
+ findAllLRG(lrg);
+ // If no single-use or single-def, assume this is a complete register
+ // kill and force spilling everywhere.
+ boolean all = lrg._killed || (lrg._1regDefCnt + lrg._1regUseCnt)==0;
+ for( Node n : _ns ) {
+ if( !(n instanceof MachNode mach) ) continue;
+ // Find def of spilling live range; spilling everywhere, OR
+ // single-register DEF and not cloneable (since these will clone
+ // before every use)
+ if( lrg(n)==lrg && (all || (!mach.isClone() && mach.outregmap().size1() )) )
+ makeSplit(n,"def/empty2",round,lrg).insertAfter(n,true);
+ // Find all uses
+ for( int i=1; i<n.nIns(); i++ ) {
+ Node def = n.in(i);
+ // Skip any new splits inserted just this pass
+ while( def instanceof SplitNode && lrg(def)==null )
+ def = def.in(1);
+ // Main def (past splits) is of the spilling lrg, and spilling
+ // single-register USE (or everywhere)
+ if( lrg(def)==lrg && (all || mach.regmap(i).size1()) )
+ insertBefore(n,i,"use/empty2",round,lrg);
+ }
+ }
+ return true;
+ }
+
+ // Self conflicts require Phis (or two-address).
+ // Insert a split after every def.
+ boolean splitSelfConflict( byte round, LRG lrg ) {
+ // Sort conflict set, so we're deterministic
+ Node[] conflicts = lrg._selfConflicts.keySet().toArray(new Node[0]);
+ Arrays.sort(conflicts, (x,y) -> x._nid - y._nid );
+
+ // For all conflicts
+ for( Node def : conflicts ) {
+ assert lrg(def)==lrg; // Might be conflict use-side
+ // Split 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);
+ split.insertAfter(def,false);
+ if( split.nOuts()==0 )
+ split.kill();
+ 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);
+ // 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 && phi.in(2)==def ) ) ||
+ (use instanceof MachNode mach && mach.twoAddress()!=0 && use.in(mach.twoAddress())==def) )
+ insertBefore( use, use._inputs.find(def), "use/self/use",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 ) continue; // Ignoring splits; since spilling need to split in a deeper loop
+ if( n.isDead() ) continue; // Some Clonable went dead by other spill changes
+ // If this is a 2-address commutable op (e.g. AddX86, MulX86) and the rhs has only a single user,
+ // commute the inputs... which chops the LHS live ranges' upper bound to just the RHS.
+ if( n instanceof MachNode mach && lrg(n)==lrg && mach.twoAddress()==1 && mach.commutes() && n.in(2).nOuts()==1 )
+ n.swap12();
+
+ if( lrg(n)==lrg && // This is a LRG def
+ // At loop boundary, or splitting in inner loop
+ (min==max || n.cfg0().loopDepth() <= min) ) {
+ // Cloneable constants will be cloned at uses, not after def
+ if( !(n instanceof MachNode mach && mach.isClone()) &&
+ // Single user is already a split
+ !(n.nOuts()==1 && n.out(0) instanceof SplitNode) )
+ // Split after def in min loop nest
+ makeSplit("def/loop",round,lrg).insertAfter(n,false);
+ }
+
+ // 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) )
+ // 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++ ) {
+ if( lrgSame(n.in(i),lrg) && // This is a LRG use
+ // splitting in inner loop or at loop border
+ (min==max || n.cfg0().loopDepth() <= min) &&
+ // Not a single-use split same block already
+ !(n.in(i) instanceof SplitNode && n.in(i).nOuts()==1 && n.cfg0()==n.in(i).cfg0()) )
+ // Split before in this block
+ insertBefore( n, i, "use/loop/use", round,lrg );
+ }
+ }
+ }
+ 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();
+ 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) {
+ Node def = n.in(i);
+ // Effective block for use
+ CFGNode cfg = n instanceof PhiNode phi ? phi.region().cfg(i) : n.cfg0();
+ // Def is a split ?
+ if( def instanceof SplitNode ) {
+ boolean singleReg = n instanceof MachNode mach && mach.regmap(i).size1();
+ // Same block, multiple registers, split is only used by n,
+ // assume this is good enough and do not split again.
+ if( cfg==def.cfg0() && def.nOuts()==1 && !singleReg )
+ return;
+ }
+ makeSplit(def,kind,round,lrg).insertBefore(n, i);
+ // Skip split-of-split same block
+ if( def instanceof SplitNode && cfg==def.cfg0() )
+ n.in(i).setDefOrdered(1,def.in(1));
+ }
+
+ private Node makeSplit( Node def, String kind, byte round, LRG lrg ) {
+ Node split = def instanceof MachNode mach && mach.isClone()
+ ? mach.copy()
+ : _code._mach.split(kind,round,lrg);
+ _lrgs.put(split,lrg);
+ return split;
+ }
+ private SplitNode makeSplit( String kind, byte round, LRG lrg ) {
+ SplitNode split = _code._mach.split(kind,round,lrg);
+ _lrgs.put(split,lrg);
+ return split;
+ }
+
+
+ // -----------------------
+ // POST PASS: Remove empty spills that biased-coloring made
+ private void postColor() {
+ for( CFGNode bb : _code._cfg ) { // For all ops
+ for( int j=0; j<bb.nOuts(); j++ ) {
+ Node n = bb.out(j);
+ if( !(n instanceof SplitNode lo) ) 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;
+ }
+}
diff --git a/src/main/java/com/seaofnodes/simple/codegen/RegMask.java b/src/main/java/com/seaofnodes/simple/codegen/RegMask.java
new file mode 100644
index 00000000..f28805b5
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/codegen/RegMask.java
@@ -0,0 +1,142 @@
+package com.seaofnodes.simple.codegen;
+
+import com.seaofnodes.simple.Utils;
+import com.seaofnodes.simple.SB;
+
+/** RegMask
+ * A "register mask" - 1 bit set for each allowed register. In addition
+ * "stack slot" registers may be allowed, effectively making the set infinite.
+ * <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; }
+ 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() {
+ 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; }
+
+ 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("[");
+ for( int i=0; i<64; i++ )
+ if( ((_bits0 >> i)&1) != 0 )
+ sb.p(mach.reg(i)).p(",");
+ for( int i=0; i<64; i++ )
+ if( ((_bits1 >> i)&1) != 0 )
+ sb.p(mach.reg(i+64)).p(",");
+ return sb.unchar().p("]");
+ }
+}
+
+// Mutable regmask(writable)
+class RegMaskRW extends RegMask {
+ public RegMaskRW(long x, long y) { super(x,y); }
+ // clears bit at position r. Returns true if the mask is still not empty.
+ public boolean clr(int r) {
+ if( r < 64 ) _bits0 &= ~(1L<<(r ));
+ else _bits1 &= ~(1L<<(r-64));
+ return _bits0!=0 || _bits1!=0;
+ }
+ @Override RegMaskRW and( RegMask mask ) {
+ if( mask==null ) return this;
+ _bits0 &= mask._bits0;
+ _bits1 &= mask._bits1;
+ return this;
+ }
+ @Override RegMaskRW sub( RegMask mask ) {
+ if( mask==null ) return this;
+ _bits0 &= ~mask._bits0;
+ _bits1 &= ~mask._bits1;
+ return this;
+ }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/AddNode.java b/src/main/java/com/seaofnodes/simple/node/AddNode.java
index 5a42e2e8..bc65e79a 100644
--- a/src/main/java/com/seaofnodes/simple/node/AddNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/AddNode.java
@@ -2,8 +2,11 @@ package com.seaofnodes.simple.node;
import com.seaofnodes.simple.Parser;
import com.seaofnodes.simple.type.*;
+
import java.util.BitSet;
+import static com.seaofnodes.simple.Parser.con;
+
public class AddNode extends Node {
public AddNode(Node lhs, Node rhs) { super(null, lhs, rhs); }
@@ -44,7 +47,8 @@ public class AddNode extends Node {
public Node idealize () {
Node lhs = in(1);
Node rhs = in(2);
- if( rhs.err()!=null ) return null;
+ if( rhs instanceof AddNode add && add.err()!=null )
+ return null;
Type t2 = rhs._type;
// Add of 0. We do not check for (0+x) because this will already
diff --git a/src/main/java/com/seaofnodes/simple/node/AndNode.java b/src/main/java/com/seaofnodes/simple/node/AndNode.java
index 36e3a949..3cfcbda3 100644
--- a/src/main/java/com/seaofnodes/simple/node/AndNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/AndNode.java
@@ -36,14 +36,17 @@ public class AndNode extends LogicalNode {
Node rhs = in(2);
Type t1 = lhs._type;
Type t2 = rhs._type;
+ if( !(t1 instanceof TypeInteger && t2 instanceof TypeInteger t2i) )
+ return null; // Malformed, e.g. (17 & 3.14)
// And of -1. We do not check for (-1&x) because this will already
- // canonicalize to (x&-1)
- if( t2.isConstant() && t2 instanceof TypeInteger i && i.value()==-1 )
+ // canonicalize to (x&-1). We do not check for zero, because
+ // the compute() call will return a zero already.
+ if( t2i.isConstant() && t2i.value()==-1 )
return lhs;
// Move constants to RHS: con*arg becomes arg*con
- if ( t1.isConstant() && !t2.isConstant() )
+ if( t1.isConstant() && !t2.isConstant() )
return swap12();
// Do we have ((x & (phi cons)) & con) ?
diff --git a/src/main/java/com/seaofnodes/simple/node/BoolNode.java b/src/main/java/com/seaofnodes/simple/node/BoolNode.java
index 93e5e4e9..e6b87ce8 100644
--- a/src/main/java/com/seaofnodes/simple/node/BoolNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/BoolNode.java
@@ -49,6 +49,8 @@ abstract public class BoolNode extends Node {
TypeInteger doOp(TypeInteger t1, TypeInteger t2) { throw Utils.TODO(); }
boolean doOp(double lhs, double rhs) { throw Utils.TODO(); }
+ Node copyF(Node lhs, Node rhs) { return null; }
+ public boolean isFloat() { return false; }
@Override
public Node idealize() {
@@ -89,6 +91,7 @@ abstract public class BoolNode extends Node {
Node copy(Node lhs, Node rhs) { return new EQ(lhs,rhs); }
Node copyF() { return new EQF(null,null); }
}
+
public static class LT extends BoolNode {
public LT(Node lhs, Node rhs) { super(lhs,rhs); }
public String op() { return "<" ; }
@@ -101,6 +104,7 @@ abstract public class BoolNode extends Node {
Node copy(Node lhs, Node rhs) { return new LT(lhs,rhs); }
Node copyF() { return new LTF(null,null); }
}
+
public static class LE extends BoolNode {
public LE(Node lhs, Node rhs) { super(lhs,rhs); }
public String op() { return "<="; }
@@ -114,9 +118,9 @@ abstract public class BoolNode extends Node {
Node copyF() { return new LEF(null,null); }
}
- public static class EQF extends EQ { public EQF(Node lhs, Node rhs) { super(lhs,rhs); } boolean doOp(double lhs, double rhs) { return lhs == rhs; } }
- public static class LTF extends LT { public LTF(Node lhs, Node rhs) { super(lhs,rhs); } boolean doOp(double lhs, double rhs) { return lhs < rhs; } }
- public static class LEF extends LE { public LEF(Node lhs, Node rhs) { super(lhs,rhs); } boolean doOp(double lhs, double rhs) { return lhs <= rhs; } }
+ public static class EQF extends EQ { public EQF(Node lhs, Node rhs) { super(lhs,rhs); } boolean doOp(double lhs, double rhs) { return lhs == rhs; } public boolean isFloat() { return true; } }
+ public static class LTF extends LT { public LTF(Node lhs, Node rhs) { super(lhs,rhs); } boolean doOp(double lhs, double rhs) { return lhs < rhs; } public boolean isFloat() { return true; } }
+ public static class LEF extends LE { public LEF(Node lhs, Node rhs) { super(lhs,rhs); } boolean doOp(double lhs, double rhs) { return lhs <= rhs; } public boolean isFloat() { return true; } }
// Unsigned less that, for range checks. Not directly user writable.
public static class ULT extends BoolNode {
diff --git a/src/main/java/com/seaofnodes/simple/node/CFGNode.java b/src/main/java/com/seaofnodes/simple/node/CFGNode.java
index 174725f5..37d08fb0 100644
--- a/src/main/java/com/seaofnodes/simple/node/CFGNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/CFGNode.java
@@ -18,15 +18,26 @@ import java.util.HashSet;
* <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);
- _idepth = cfg._idepth;
- _ltree = cfg._ltree;
- _pre = cfg._pre;
+ if( cfg != null ) {
+ _idepth = cfg._idepth;
+ _ltree = cfg._ltree;
+ _pre = cfg._pre;
+ }
}
public CFGNode cfg(int idx) { return (CFGNode)in(idx); }
@@ -75,6 +86,14 @@ public abstract class CFGNode extends Node {
// 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; }
@@ -151,12 +170,11 @@ public abstract class CFGNode extends Node {
return pre;
}
- boolean skip(CFGNode usecfg) {
+ private boolean skip(CFGNode usecfg) {
// Only walk control users that are alive.
// Do not walk from a Call to linked Fun's.
return usecfg instanceof XCtrlNode ||
(this instanceof CallNode && usecfg instanceof FunNode) ||
(this instanceof ReturnNode && usecfg instanceof CallEndNode);
}
-
}
diff --git a/src/main/java/com/seaofnodes/simple/node/CProjNode.java b/src/main/java/com/seaofnodes/simple/node/CProjNode.java
index 690ef590..adc4e66f 100644
--- a/src/main/java/com/seaofnodes/simple/node/CProjNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/CProjNode.java
@@ -1,6 +1,7 @@
package com.seaofnodes.simple.node;
import com.seaofnodes.simple.Parser;
+import com.seaofnodes.simple.Utils;
import com.seaofnodes.simple.type.Type;
import com.seaofnodes.simple.type.TypeMem;
import com.seaofnodes.simple.type.TypeTuple;
@@ -25,7 +26,6 @@ public class CProjNode extends CFGNode {
@Override StringBuilder _print1(StringBuilder sb, BitSet visited) { return sb.append(_label); }
- @Override public boolean isMultiTail() { return in(0).isMultiHead(); }
@Override public boolean blockHead() { return true; }
public CFGNode ctrl() { return cfg(0); }
diff --git a/src/main/java/com/seaofnodes/simple/node/CallEndNode.java b/src/main/java/com/seaofnodes/simple/node/CallEndNode.java
index 46188ed5..03a4462d 100644
--- a/src/main/java/com/seaofnodes/simple/node/CallEndNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/CallEndNode.java
@@ -1,6 +1,7 @@
package com.seaofnodes.simple.node;
import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.codegen.CodeGen;
import com.seaofnodes.simple.type.*;
import java.util.BitSet;
@@ -11,13 +12,12 @@ 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}); }
- public CallEndNode(CallEndNode cend) { super(cend); }
+ public CallEndNode(CallNode call) { super(new Node[]{call}); _rpc = TypeRPC.constant(_nid); }
+ public CallEndNode(CallEndNode cend) { super(cend); _rpc = cend._rpc; }
- @Override
- public String label() { return "CallEnd"; }
- @Override public boolean isMultiHead() { return true; }
+ @Override public String label() { return "CallEnd"; }
@Override public boolean blockHead() { return true; }
public CallNode call() { return (CallNode)in(0); }
@@ -36,7 +36,17 @@ public class CallEndNode extends CFGNode implements MultiNode {
public Type compute() {
if( !(in(0) instanceof CallNode call) )
return TypeTuple.RET.dual();
- Type ret = call.fptr().addDep(this)._type instanceof TypeFunPtr tfp ? tfp.ret() : Type.BOTTOM;
+ Type ret = Type.BOTTOM;
+ TypeMem mem = TypeMem.BOT;
+ if( call.fptr().addDep(this)._type instanceof TypeFunPtr tfp ) {
+ ret = tfp.ret();
+ // Here, if I can figure out I've found *all* callers, then I can meet
+ // across the linked returns and join with the function return type.
+ if( tfp.isConstant() && nIns()>1 ) {
+ assert nIns()==2; // Linked exactly once for a constant
+ ret = ((TypeTuple)in(1)._type).ret(); // Return type
+ }
+ }
return TypeTuple.make(call._type,TypeMem.BOT,ret);
}
diff --git a/src/main/java/com/seaofnodes/simple/node/CallNode.java b/src/main/java/com/seaofnodes/simple/node/CallNode.java
index 25f76a71..c9bbcc52 100644
--- a/src/main/java/com/seaofnodes/simple/node/CallNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/CallNode.java
@@ -1,6 +1,6 @@
package com.seaofnodes.simple.node;
-import com.seaofnodes.simple.CodeGen;
+import com.seaofnodes.simple.codegen.CodeGen;
import com.seaofnodes.simple.IterPeeps;
import com.seaofnodes.simple.Parser;
import com.seaofnodes.simple.Utils;
@@ -19,8 +19,7 @@ public class CallNode extends CFGNode {
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 String label() { return "Call"; }
@Override StringBuilder _print1(StringBuilder sb, BitSet visited) {
String fname = name();
@@ -43,9 +42,9 @@ public class CallNode extends CFGNode {
Node ctrl() { return in(0); }
Node mem () { return in(1); }
// Args mapped 1-to-1 on inputs, so conceptually start at 2
- Node arg(int idx) { return in(idx); }
+ public Node arg(int idx) { return in(idx); }
// Same arg accounting as TFPs, although the numbering starts at 2
- int nargs() { return nIns()-3; } // Minus control, memory, fptr
+ public int nargs() { return nIns()-3; } // Minus control, memory, fptr
// args from input 2 to last; last is function input
public Node fptr() { return _inputs.last(); }
// Error if not a TFP
@@ -122,12 +121,28 @@ public class CallNode extends CFGNode {
fun.addDef(this);
for( Node use : fun._outputs )
if( use instanceof ParmNode parm )
- parm.addDef(parm._idx==0 ? cend() : arg(parm._idx));
+ parm.addDef(parm._idx==0 ? new ConstantNode(cend()._rpc).peephole() : arg(parm._idx));
// Call end points to function return
CodeGen.CODE.add(cend()).addDef(fun.ret());
assert linked(fun);
}
+ // Unlink all linked functions
+ public void unlink_all() {
+ for( int i=0; i<_outputs._len; i++ )
+ if( out(i) instanceof FunNode fun ) {
+ assert linked(fun);
+ int idx = fun._inputs.find(this);
+ for( Node use : fun._outputs )
+ if( use instanceof ParmNode parm )
+ use.delDef(idx);
+ fun.delDef(idx);
+ cend().delDef(cend()._inputs.find(fun.ret()));
+ assert !linked(fun);
+ i--;
+ }
+ }
+
@Override
public Parser.ParseException err() {
if( !(fptr()._type instanceof TypeFunPtr tfp) )
diff --git a/src/main/java/com/seaofnodes/simple/node/CalleeSaveNode.java b/src/main/java/com/seaofnodes/simple/node/CalleeSaveNode.java
new file mode 100644
index 00000000..d4a937bd
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/CalleeSaveNode.java
@@ -0,0 +1,25 @@
+package com.seaofnodes.simple.node;
+
+import com.seaofnodes.simple.Utils;
+import com.seaofnodes.simple.codegen.RegMask;
+import com.seaofnodes.simple.type.Type;
+import java.util.BitSet;
+import java.io.ByteArrayOutputStream;
+
+public class CalleeSaveNode extends ProjNode implements MachNode {
+ final RegMask _mask;
+ public CalleeSaveNode(FunNode fun, int reg, String label) {
+ super(fun,reg,label);
+ fun._outputs.pop();
+ int i=0;
+ while( fun.out(i) instanceof PhiNode ) i++;
+ fun._outputs.insert(this,i);
+ _mask = new RegMask(reg);
+ }
+ @Override public RegMask regmap(int i) { return null; }
+ @Override public RegMask outregmap() { return _mask; }
+ @Override public int encoding(ByteArrayOutputStream bytes) { return 0; }
+
+ @Override public Type compute() { return Type.BOTTOM; }
+ @Override public Node idealize() { return null; }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/CastNode.java b/src/main/java/com/seaofnodes/simple/node/CastNode.java
index d8a968c1..5a92802c 100644
--- a/src/main/java/com/seaofnodes/simple/node/CastNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/CastNode.java
@@ -9,13 +9,19 @@ 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 {
- private final Type _t;
+ public Type _t;
public CastNode(Type t, Node ctrl, Node in) {
super(ctrl, in);
_t = t;
setType(compute());
}
+ public CastNode(CastNode c) {
+ super(c.in(0), c.in(1)); // Call parent constructor
+ this._t = c._t; // Copy the Type field
+ setType(compute()); // Ensure type is recomputed
+ }
+
@Override public String label() { return "("+_t.str()+")"; }
@Override
diff --git a/src/main/java/com/seaofnodes/simple/node/ConstantNode.java b/src/main/java/com/seaofnodes/simple/node/ConstantNode.java
index d788cff7..f7ebc006 100644
--- a/src/main/java/com/seaofnodes/simple/node/ConstantNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/ConstantNode.java
@@ -1,6 +1,6 @@
package com.seaofnodes.simple.node;
-import com.seaofnodes.simple.CodeGen;
+import com.seaofnodes.simple.codegen.CodeGen;
import com.seaofnodes.simple.Parser;
import com.seaofnodes.simple.SB;
import com.seaofnodes.simple.type.Type;
@@ -26,7 +26,8 @@ public class ConstantNode extends Node {
super(new Node[]{CodeGen.CODE._start});
_con = _type = type;
}
- public ConstantNode( ConstantNode con ) { super(con); _con = con._type; }
+ public ConstantNode( Node con, Type t ) { super(con); _con = t; }
+ public ConstantNode( ConstantNode con ) { this(con,con._type); }
public static Node make( Type type ) {
if( type==Type. CONTROL ) return new CtrlNode();
@@ -42,7 +43,7 @@ public class ConstantNode extends Node {
@Override
StringBuilder _print1(StringBuilder sb, BitSet visited) {
- if( _con instanceof TypeFunPtr tfp ) {
+ if( _con instanceof TypeFunPtr tfp && tfp.isConstant() ) {
FunNode fun = CodeGen.CODE.link(tfp);
if( fun._name != null )
return sb.append("{ ").append(fun._name).append("}");
@@ -50,7 +51,6 @@ public class ConstantNode extends Node {
return sb.append(_con.print(new SB()));
}
- @Override public boolean isMultiTail() { return true; }
@Override public boolean isConst() { return true; }
@Override
diff --git a/src/main/java/com/seaofnodes/simple/node/CtrlNode.java b/src/main/java/com/seaofnodes/simple/node/CtrlNode.java
index 0ea060bd..61d9c310 100644
--- a/src/main/java/com/seaofnodes/simple/node/CtrlNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/CtrlNode.java
@@ -1,6 +1,6 @@
package com.seaofnodes.simple.node;
-import com.seaofnodes.simple.CodeGen;
+import com.seaofnodes.simple.codegen.CodeGen;
import com.seaofnodes.simple.Parser;
import com.seaofnodes.simple.type.Type;
import java.util.BitSet;
diff --git a/src/main/java/com/seaofnodes/simple/node/DivFNode.java b/src/main/java/com/seaofnodes/simple/node/DivFNode.java
index 36fd4524..a6e459c9 100644
--- a/src/main/java/com/seaofnodes/simple/node/DivFNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/DivFNode.java
@@ -31,14 +31,9 @@ public class DivFNode extends Node {
@Override
public Node idealize() {
- Node lhs = in(1);
- Node rhs = in(2);
- Type t1 = lhs._type;
- Type t2 = rhs._type;
-
- // Div of 1.
- if ( t2.isConstant() && t2 instanceof TypeFloat i && i.value()==1 )
- return lhs;
+ // Div of constant
+ if( in(2)._type instanceof TypeFloat f && f.isConstant() )
+ return new MulFNode(in(1),new ConstantNode(TypeFloat.constant(1.0/f.value())).peephole());
return null;
}
diff --git a/src/main/java/com/seaofnodes/simple/node/FunNode.java b/src/main/java/com/seaofnodes/simple/node/FunNode.java
index 506e7758..38aa83a0 100644
--- a/src/main/java/com/seaofnodes/simple/node/FunNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/FunNode.java
@@ -3,8 +3,10 @@ package com.seaofnodes.simple.node;
import com.seaofnodes.simple.*;
import com.seaofnodes.simple.type.Type;
import com.seaofnodes.simple.type.TypeFunPtr;
+import com.seaofnodes.simple.type.TypeTuple;
+
import java.util.BitSet;
-import static com.seaofnodes.simple.CodeGen.CODE;
+import static com.seaofnodes.simple.codegen.CodeGen.CODE;
public class FunNode extends RegionNode {
@@ -17,10 +19,19 @@ public class FunNode extends RegionNode {
public String _name; // Debug name
public FunNode( Parser.Lexer loc, TypeFunPtr sig, Node... nodes ) { super(loc,nodes); _sig = sig; }
- public FunNode( FunNode fun ) { super( fun, fun._loc ); _sig = fun.sig(); _name = fun._name; }
+ public FunNode( FunNode fun ) {
+ super( fun, fun==null ? null : fun._loc );
+ if( fun!=null ) {
+ _sig = fun.sig();
+ _name = fun._name;
+ } else {
+ _sig = TypeFunPtr.BOT;
+ _name = "";
+ }
+ }
@Override
- public String label() { return _name == null ? "$fun" : _name; }
+ public String label() { return _name == null ? "$fun"+_sig.fidx() : _name; }
// Find the one CFG user from Fun. It's not always the Return, but always
// the Return *is* a CFG user of Fun.
@@ -43,11 +54,11 @@ public class FunNode extends RegionNode {
// Cannot create the Return and Fun at the same time; one has to be first.
// So setting the return requires a second step.
public void setRet(ReturnNode ret) { _ret=ret; }
- ReturnNode ret() { assert _ret!=null; return _ret; }
+ public ReturnNode ret() { assert _ret!=null; return _ret; }
// Signature can improve over time
public TypeFunPtr sig() { return _sig; }
- void setSig( TypeFunPtr sig ) {
+ public void setSig( TypeFunPtr sig ) {
assert sig.isa(_sig);
if( _sig != sig ) {
CODE.add(this);
@@ -72,6 +83,10 @@ public class FunNode extends RegionNode {
return progress;
}
+ // Upgrade inferred or user-written return type to actual
+ if( _ret!=null && _ret._type instanceof TypeTuple tt && tt.ret() != _sig.ret() )
+ throw Utils.TODO();
+
// When can we assume no callers? Or no other callers (except main)?
// In a partial compilation, we assume Start gets access to any/all
// top-level public structures and recursively what they point to.
diff --git a/src/main/java/com/seaofnodes/simple/node/IfNode.java b/src/main/java/com/seaofnodes/simple/node/IfNode.java
index 9e6d2dff..e04522b0 100644
--- a/src/main/java/com/seaofnodes/simple/node/IfNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/IfNode.java
@@ -1,6 +1,6 @@
package com.seaofnodes.simple.node;
-import com.seaofnodes.simple.CodeGen;
+import com.seaofnodes.simple.codegen.CodeGen;
import com.seaofnodes.simple.IterPeeps;
import com.seaofnodes.simple.type.*;
import java.util.BitSet;
@@ -18,13 +18,11 @@ public class IfNode extends CFGNode implements MultiNode {
public String label() { return "If"; }
@Override
- StringBuilder _print1(StringBuilder sb, BitSet visited) {
+ public StringBuilder _print1(StringBuilder sb, BitSet visited) {
sb.append("if( ");
return in(1)._print0(sb, visited).append(" )");
}
- @Override public boolean isMultiHead() { return true; }
-
public Node ctrl() { return in(0); }
public Node pred() { return in(1); }
diff --git a/src/main/java/com/seaofnodes/simple/node/LoadNode.java b/src/main/java/com/seaofnodes/simple/node/LoadNode.java
index 4004f87f..b70a938d 100644
--- a/src/main/java/com/seaofnodes/simple/node/LoadNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/LoadNode.java
@@ -201,9 +201,9 @@ public class LoadNode extends MemOpNode {
return new AndNode(null,val,con(ti._max));
// Signed extension
int shift = Long.numberOfLeadingZeros(ti._max)-1;
- if( shift==0 )
- return val;
Node shf = con(shift);
+ if( shf._type==TypeInteger.ZERO )
+ return val;
Node shl = new ShlNode(null,val,shf.keep()).peephole();
return new SarNode(null,shl,shf.unkeep());
}
diff --git a/src/main/java/com/seaofnodes/simple/node/LogicalNode.java b/src/main/java/com/seaofnodes/simple/node/LogicalNode.java
index ccc1f6b5..8c3452bf 100644
--- a/src/main/java/com/seaofnodes/simple/node/LogicalNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/LogicalNode.java
@@ -22,8 +22,8 @@ public abstract class LogicalNode extends Node {
@Override public Parser.ParseException err() {
if( in(1)._type.isHigh() || in(2)._type.isHigh() ) return null;
- if( !(in(1)._type instanceof TypeInteger) ) return Parser.error("Cannot '"+op()+"' " + in(1)._type.glb(),_loc);
- if( !(in(2)._type instanceof TypeInteger) ) return Parser.error("Cannot '"+op()+"' " + in(2)._type.glb(), _loc);
+ if( !(in(1)._type instanceof TypeInteger) ) return Parser.error("Cannot '"+op()+"' " + in(1)._type,_loc);
+ if( !(in(2)._type instanceof TypeInteger) ) return Parser.error("Cannot '"+op()+"' " + in(2)._type,_loc);
return null;
}
}
diff --git a/src/main/java/com/seaofnodes/simple/node/MachConcreteNode.java b/src/main/java/com/seaofnodes/simple/node/MachConcreteNode.java
index 0c49fc64..b01b3308 100644
--- a/src/main/java/com/seaofnodes/simple/node/MachConcreteNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/MachConcreteNode.java
@@ -7,7 +7,7 @@ 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 abstract class MachConcreteNode extends Node implements MachNode {
public MachConcreteNode(Node node) { super(node); }
public MachConcreteNode(Node[]nodes) { super(nodes); }
diff --git a/src/main/java/com/seaofnodes/simple/node/MachNode.java b/src/main/java/com/seaofnodes/simple/node/MachNode.java
index 9f2fee85..ecdbc651 100644
--- a/src/main/java/com/seaofnodes/simple/node/MachNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/MachNode.java
@@ -1,6 +1,9 @@
package com.seaofnodes.simple.node;
import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.codegen.Machine;
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.RegMask;
import java.io.ByteArrayOutputStream;
public interface MachNode {
@@ -12,13 +15,16 @@ public interface MachNode {
default void postSelect() { }
// Register mask allowed on input i. 0 for no register.
- abstract public RegMask regmap(int i);
+ RegMask regmap( int i );
// Register mask allowed as a result. 0 for no register.
- abstract public RegMask outregmap();
+ RegMask outregmap();
// Multi-reg-defining machine op; idx comes from Proj.
// Sometimes these Projs have no uses, and just exist to kill a register.
default RegMask outregmap(int idx) { throw Utils.TODO(); }
+ // Set of killed registers
+ default RegMask killmap() { return null; }
+
// If this op updates the same register as input 'idx', e.g. many X86 "R1 +=
// R2" style ops. This forces the output live range to equal the input live
// range, so they both get the same register.
@@ -32,11 +38,17 @@ public interface MachNode {
// Returns either 0 for not-two-adr or the updated input; for projections
// this is their Multi's updated input.
default int twoAddress( ) { return 0; }
+ // Ok to switch arguments
+ default boolean commutes() { return false; }
// Instructions cheaper to recreate than to spill, such as loading small constants
default boolean isClone() { return false; }
+
+ // Make a clone of a cheap instruction
+ default Node copy() { return null; }
+
// Encoding is appended into the byte array; size is returned
- abstract public int encoding(ByteArrayOutputStream bytes);
+ int encoding(ByteArrayOutputStream bytes);
// Human-readable form appended to the SB. Things like the encoding,
// indentation, leading address or block labels not printed here.
diff --git a/src/main/java/com/seaofnodes/simple/node/MemMergeNode.java b/src/main/java/com/seaofnodes/simple/node/MemMergeNode.java
index 5363bb6f..1495a703 100644
--- a/src/main/java/com/seaofnodes/simple/node/MemMergeNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/MemMergeNode.java
@@ -1,6 +1,7 @@
package com.seaofnodes.simple.node;
import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.codegen.CodeGen;
import com.seaofnodes.simple.type.*;
import java.util.*;
diff --git a/src/main/java/com/seaofnodes/simple/node/MemOpNode.java b/src/main/java/com/seaofnodes/simple/node/MemOpNode.java
index d9ab6b89..cba4dc1b 100644
--- a/src/main/java/com/seaofnodes/simple/node/MemOpNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/MemOpNode.java
@@ -33,14 +33,14 @@ public abstract class MemOpNode extends Node {
}
public MemOpNode( Node mach, MemOpNode mop ) {
super(mach);
- _name = mop._name;
- _alias = mop._alias;
- _declaredType = mop._declaredType;
- _loc = mop._loc;
+ _name = mop==null ? null : mop._name;
+ _alias = mop==null ? 0 : mop._alias;
+ _loc = mop==null ? null : mop._loc;
+ _declaredType = mop==null ? Type.BOTTOM : mop._declaredType;
}
//
- static String mlabel(String name) { return name.equals("[]") ? "ary" : (name.equals("#") ? "len" : name); }
+ static String mlabel(String name) { return "[]".equals(name) ? "ary" : ("#".equals(name) ? "len" : name); }
String mlabel() { return mlabel(_name); }
public Node mem() { return in(1); }
diff --git a/src/main/java/com/seaofnodes/simple/node/MulNode.java b/src/main/java/com/seaofnodes/simple/node/MulNode.java
index 53472994..80f8c319 100644
--- a/src/main/java/com/seaofnodes/simple/node/MulNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/MulNode.java
@@ -80,8 +80,8 @@ public class MulNode extends Node {
@Override Node copyF() { return new MulFNode(null,null); }
@Override public Parser.ParseException err() {
if( in(1)._type.isHigh() || in(2)._type.isHigh() ) return null;
- if( !(in(1)._type instanceof TypeInteger) ) return Parser.error("Cannot '"+label()+"' " + in(1)._type,null);
- if( !(in(2)._type instanceof TypeInteger) ) return Parser.error("Cannot '"+label()+"' " + in(2)._type,null);
+ if( !(in(1)._type instanceof TypeInteger) ) return Parser.error("Cannot '"+label()+"' " + in(1)._type.glb(),null);
+ if( !(in(2)._type instanceof TypeInteger) ) return Parser.error("Cannot '"+label()+"' " + in(2)._type.glb(),null);
return null;
}
}
diff --git a/src/main/java/com/seaofnodes/simple/node/NeverNode.java b/src/main/java/com/seaofnodes/simple/node/NeverNode.java
index e77ed653..26198500 100644
--- a/src/main/java/com/seaofnodes/simple/node/NeverNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/NeverNode.java
@@ -13,7 +13,7 @@ public class NeverNode extends IfNode {
@Override public String label() { return "Never"; }
- @Override StringBuilder _print1(StringBuilder sb, BitSet visited) { return sb.append("Never"); }
+ @Override public StringBuilder _print1(StringBuilder sb, BitSet visited) { return sb.append("Never"); }
@Override public Type compute() { return TypeTuple.IF_BOTH; }
diff --git a/src/main/java/com/seaofnodes/simple/node/Node.java b/src/main/java/com/seaofnodes/simple/node/Node.java
index bbf1ebe9..b3d6c94e 100644
--- a/src/main/java/com/seaofnodes/simple/node/Node.java
+++ b/src/main/java/com/seaofnodes/simple/node/Node.java
@@ -1,12 +1,15 @@
package com.seaofnodes.simple.node;
import com.seaofnodes.simple.*;
+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.CODE;
+import static com.seaofnodes.simple.codegen.CodeGen.CODE;
/**
* All Nodes in the Sea of Nodes IR inherit from the Node class.
@@ -63,11 +66,11 @@ public abstract class Node {
// 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 == CodeGen.Phase.InstructionSelection;
+ assert CodeGen.CODE._phase.ordinal() >= CodeGen.Phase.InstSelect.ordinal();
_nid = CODE.getUID(); // allocate unique dense ID
- _inputs = new Ary<>(n._inputs.asAry());
+ _inputs = new Ary<>(n==null ? new Node[0] : n._inputs.asAry());
_outputs = new Ary<>(Node.class);
- _type = n._type;
+ _type = n==null ? Type.BOTTOM : n._type;
_deps = null;
_hash = 0;
}
@@ -108,7 +111,7 @@ public abstract class Node {
// This is the common print: check for repeats, check for DEAD and print
// "DEAD" else call the per-Node print1.
- final StringBuilder _print0(StringBuilder sb, BitSet visited) {
+ public final StringBuilder _print0(StringBuilder sb, BitSet visited) {
if (visited.get(_nid) && !(this instanceof ConstantNode) )
return sb.append(label());
visited.set(_nid);
@@ -121,9 +124,7 @@ public abstract class Node {
public String p(int depth) { return IRPrinter.prettyPrint(this,depth); }
- public boolean isMultiHead() { return false; }
- public boolean isMultiTail() { return false; }
- public boolean isConst () { return false; }
+ public boolean isConst() { return false; }
// ------------------------------------------------------------------------
// Graph Node & Edge manipulation
@@ -181,12 +182,16 @@ public abstract class Node {
// 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.
- Node delDef(int idx) {
+ public Node delDef(int idx) {
unlock();
Node old_def = in(idx);
_inputs.del(idx);
@@ -295,6 +300,74 @@ public abstract class Node {
kill();
}
+ // Replace uses of `def` with `this`, and insert `this` immediately after
+ // `def` in the basic block.
+ public void insertAfter( Node def, boolean must ) {
+ 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);
+ for( int j=def.nOuts()-1; j>=0; j-- ) {
+ // Can we avoid a split of a split? 'this' split is used by
+ // another split in the same block.
+ if( !must && def.out(j) instanceof SplitNode split && def.out(j).cfg0()==cfg &&
+ !split._kind.contains("self") )
+ continue;
+ Node use = def._outputs.del(j);
+ use.unlock();
+ int idx = use._inputs.find(def);
+ use._inputs.set(idx,this);
+ addUse(use);
+ }
+ if( nIns()>1 ) setDef(1,def);
+ }
+
+ // Insert this in front of use.in(uidx) with this, and insert this
+ // immediately before use in the basic block.
+ public void insertBefore( Node use, int uidx ) {
+ CFGNode cfg = use.cfg0();
+ int i;
+ if( use instanceof PhiNode phi ) {
+ cfg = phi.region().cfg(uidx);
+ i = cfg.nOuts()-1;
+ } else {
+ i = cfg._outputs.find(use);
+ }
+ cfg._outputs.insert(this,i);
+ _inputs.set(0,cfg);
+ if( _inputs._len > 1 && this instanceof SplitNode )
+ setDefOrdered(1,use.in(uidx));
+ use.setDefOrdered(uidx,this);
+ }
+
+ public void setDefOrdered(int idx, Node def) {
+ // If old is dying, remove from CFG ordered
+ Node old = in(idx);
+ if( old!=null && old.nOuts()==1 ) {
+ CFGNode cfg = old.cfg0();
+ if( cfg!=null ) {
+ cfg._outputs.remove(cfg._outputs.find(old));
+ old._inputs.set(0,null);
+ }
+ }
+ setDef(idx,def);
+ }
+
+ public void 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
@@ -578,7 +651,7 @@ public abstract class Node {
// Peephole utilities
// Swap inputs without letting either input go dead during the swap.
- Node swap12() {
+ public Node swap12() {
unlock(); // Hash is order dependent
_inputs.swap(1,2);
return this;
@@ -634,7 +707,14 @@ public abstract class Node {
/**
* Debugging utility to find a Node by index
*/
- public Node find(int nid) {
- return walk( n -> n._nid==nid ? n : null );
+ public Node find(int nid) { return _find(nid, new BitSet()); }
+ private Node _find(int nid, BitSet bs) {
+ if( bs.get(_nid) ) return null; // Been there, done that
+ bs.set(_nid);
+ if( _nid==nid ) return this;
+ Node x;
+ for( Node def : _inputs ) if( def != null && (x = def._find(nid,bs)) != null ) return x;
+ for( Node use : _outputs ) if( use != null && (x = use._find(nid,bs)) != null ) return x;
+ return null;
}
}
diff --git a/src/main/java/com/seaofnodes/simple/node/PhiNode.java b/src/main/java/com/seaofnodes/simple/node/PhiNode.java
index 17748afe..16f38d45 100644
--- a/src/main/java/com/seaofnodes/simple/node/PhiNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/PhiNode.java
@@ -44,7 +44,6 @@ public class PhiNode extends Node {
}
public CFGNode region() { return (CFGNode)in(0); }
- @Override public boolean isMultiTail() { return true; }
@Override public boolean isMem() { return _declaredType instanceof TypeMem; }
@Override public boolean isPinned() { return true; }
@@ -101,6 +100,10 @@ public class PhiNode extends Node {
// Test not running backwards, which can happen for e.g. And's
if( down.compute().isa(compute()) )
return down;
+ in(1).in(1).addDep(this);
+ in(1).in(2).addDep(this);
+ in(2).in(1).addDep(this);
+ in(2).in(2).addDep(this);
down.kill();
}
diff --git a/src/main/java/com/seaofnodes/simple/node/ProjNode.java b/src/main/java/com/seaofnodes/simple/node/ProjNode.java
index ef7b17e4..3d9db4ed 100644
--- a/src/main/java/com/seaofnodes/simple/node/ProjNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/ProjNode.java
@@ -35,7 +35,6 @@ public class ProjNode extends Node {
return in(0) instanceof CFGNode cfg ? cfg : in(0).cfg0();
}
- @Override public boolean isMultiTail() { return in(0).isMultiHead(); }
@Override public boolean isMem() { return _type instanceof TypeMem; }
@Override public boolean isPinned() { return true; }
diff --git a/src/main/java/com/seaofnodes/simple/node/RegionNode.java b/src/main/java/com/seaofnodes/simple/node/RegionNode.java
index 5d3db5c6..fb6539ae 100644
--- a/src/main/java/com/seaofnodes/simple/node/RegionNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/RegionNode.java
@@ -1,6 +1,7 @@
package com.seaofnodes.simple.node;
import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.codegen.CodeGen;
import com.seaofnodes.simple.type.Type;
import java.util.*;
@@ -10,7 +11,7 @@ public class RegionNode extends CFGNode {
public RegionNode(Parser.Lexer loc, Node... nodes) { super(nodes); _loc = loc; }
public RegionNode(RegionNode r, Parser.Lexer loc) { super(r); _loc = loc; }
- public RegionNode(RegionNode r) { super(r); _loc = r._loc; }
+ public RegionNode(RegionNode r) { super(r); if( r!=null ) _loc = r._loc; }
@Override
public String label() { return "Region"; }
@@ -20,7 +21,6 @@ public class RegionNode extends CFGNode {
return sb.append(label());
}
- @Override public boolean isMultiHead() { return true; }
@Override public boolean blockHead() { return true; }
@Override
diff --git a/src/main/java/com/seaofnodes/simple/node/ReturnNode.java b/src/main/java/com/seaofnodes/simple/node/ReturnNode.java
index 20b6c7b9..809dd1e1 100644
--- a/src/main/java/com/seaofnodes/simple/node/ReturnNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/ReturnNode.java
@@ -37,7 +37,8 @@ public class ReturnNode extends CFGNode {
@Override
StringBuilder _print1(StringBuilder sb, BitSet visited) {
sb.append("return ");
- expr()._print0(sb, visited);
+ if( expr()==null ) sb.append("----");
+ else expr()._print0(sb, visited);
return sb.append(";");
}
diff --git a/src/main/java/com/seaofnodes/simple/node/ScopeNode.java b/src/main/java/com/seaofnodes/simple/node/ScopeNode.java
index 1208ff63..f193e55a 100644
--- a/src/main/java/com/seaofnodes/simple/node/ScopeNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/ScopeNode.java
@@ -1,6 +1,7 @@
package com.seaofnodes.simple.node;
import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.codegen.CodeGen;
import com.seaofnodes.simple.type.*;
import java.util.*;
diff --git a/src/main/java/com/seaofnodes/simple/node/SplitNode.java b/src/main/java/com/seaofnodes/simple/node/SplitNode.java
new file mode 100644
index 00000000..22cb9bb9
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/SplitNode.java
@@ -0,0 +1,22 @@
+package com.seaofnodes.simple.node;
+
+import com.seaofnodes.simple.SB;
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.type.Type;
+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) {
+ return in(1)._print0(sb.append("mov("),visited).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
index 26e08535..22a1750d 100644
--- a/src/main/java/com/seaofnodes/simple/node/StartNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/StartNode.java
@@ -1,6 +1,6 @@
package com.seaofnodes.simple.node;
-import com.seaofnodes.simple.CodeGen;
+import com.seaofnodes.simple.codegen.CodeGen;
import com.seaofnodes.simple.Parser;
import com.seaofnodes.simple.type.*;
import java.util.Arrays;
@@ -20,7 +20,7 @@ public class StartNode extends LoopNode implements MultiNode {
final Type _arg;
public StartNode(Type arg) { super((Parser.Lexer)null,null); _arg = arg; _type = compute(); }
- public StartNode(StartNode start) { super(start); _arg = start._arg; }
+ public StartNode(StartNode start) { super(start); _arg = start==null ? null : start._arg; }
@Override public String label() { return "Start"; }
@@ -29,7 +29,6 @@ public class StartNode extends LoopNode implements MultiNode {
return sb.append(label());
}
- @Override public boolean isMultiHead() { return true; }
@Override public boolean blockHead() { return true; }
@Override public CFGNode cfg0() { return null; }
diff --git a/src/main/java/com/seaofnodes/simple/node/StopNode.java b/src/main/java/com/seaofnodes/simple/node/StopNode.java
index 6aaba3b3..b4cb002b 100644
--- a/src/main/java/com/seaofnodes/simple/node/StopNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/StopNode.java
@@ -13,7 +13,7 @@ public class StopNode extends CFGNode {
_src = src;
_type = compute();
}
- public StopNode(StopNode stop) { super(stop); _src = stop._src; }
+ public StopNode(StopNode stop) { super(stop); _src = stop==null ? null : stop._src; }
@Override
public String label() {
diff --git a/src/main/java/com/seaofnodes/simple/node/XCtrlNode.java b/src/main/java/com/seaofnodes/simple/node/XCtrlNode.java
index 9b4245c7..da021246 100644
--- a/src/main/java/com/seaofnodes/simple/node/XCtrlNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/XCtrlNode.java
@@ -1,6 +1,6 @@
package com.seaofnodes.simple.node;
-import com.seaofnodes.simple.CodeGen;
+import com.seaofnodes.simple.codegen.CodeGen;
import com.seaofnodes.simple.Parser;
import com.seaofnodes.simple.type.Type;
import java.util.BitSet;
@@ -10,7 +10,6 @@ public class XCtrlNode extends CFGNode {
@Override public String label() { return "Xctrl"; }
@Override StringBuilder _print1(StringBuilder sb, BitSet visited) { return sb.append("Xctrl"); }
@Override public boolean isConst() { return true; }
- @Override public boolean isMultiTail() { return true; }
@Override public Type compute() { return Type.XCONTROL; }
@Override public Node idealize() { return null; }
}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/AddARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/AddARM.java
new file mode 100644
index 00000000..c4014571
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/AddARM.java
@@ -0,0 +1,34 @@
+package com.seaofnodes.simple.node.cpus.arm;
+
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.RegMask;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.type.TypeInteger;
+import java.io.ByteArrayOutputStream;
+
+public class AddARM extends MachConcreteNode implements MachNode {
+
+ AddARM( Node add) {super(add); }
+
+ // Register mask allowed on input i.
+ @Override public RegMask regmap(int i) {
+ assert i== 1 || i == 2 || i == 3;
+ return arm.RMASK;
+ }
+ // Register mask allowed as a result. 0 for no register.
+ @Override public RegMask outregmap() { return arm.RMASK; }
+
+ // Encoding is appended into the byte array; size is returned
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+
+ // General form: "rd = rs1 + rs2"
+ @Override public void asm(CodeGen code, SB sb) {
+ sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" + ").p(code.reg(in(2)));
+ }
+
+ @Override public String op() {
+ return "add";}
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/AddFARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/AddFARM.java
new file mode 100644
index 00000000..098ab8c1
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/AddFARM.java
@@ -0,0 +1,31 @@
+package com.seaofnodes.simple.node.cpus.arm;
+
+
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.RegMask;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.type.TypeInteger;
+import java.io.ByteArrayOutputStream;
+
+public class AddFARM extends MachConcreteNode implements MachNode {
+ AddFARM(Node addf) { super(addf); }
+
+ // Register mask allowed on input i.
+ @Override public RegMask regmap(int i) { assert i==1 || i==2; return arm.DMASK; }
+ // Register mask allowed as a result. 0 for no register.
+ @Override public RegMask outregmap() { return arm.DMASK; }
+
+ // Encoding is appended into the byte array; size is returned
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+
+ // Default on double precision for now(64 bits)
+ // General form: "vadd.32 rd = src1 + src2
+ @Override public void asm(CodeGen code, SB sb) {
+ sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" + ").p(code.reg(in(2)));
+ }
+
+ @Override public String op() { return "addf"; }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/AddIARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/AddIARM.java
new file mode 100644
index 00000000..c8f5f8dd
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/AddIARM.java
@@ -0,0 +1,51 @@
+package com.seaofnodes.simple.node.cpus.arm;
+
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.RegMask;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.type.TypeInteger;
+import java.io.ByteArrayOutputStream;
+
+public class AddIARM extends MachConcreteNode implements MachNode {
+ final TypeInteger _ti;
+
+ AddIARM(Node add, TypeInteger ti) {
+ super(add);
+ _inputs.pop();
+ _ti = ti;
+ }
+
+ // Register mask allowed on input i.
+ @Override
+ public RegMask regmap(int i) {
+ // assert i== i;
+ return arm.RMASK;
+ }
+
+ // Register mask allowed as a result. 0 for no register.
+ @Override
+ public RegMask outregmap() {
+ return arm.RMASK;
+ }
+
+ // Encoding is appended into the byte array; size is returned
+ @Override
+ public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+
+ // General form: "addi rd = rs1 + imm"
+ @Override
+ public void asm(CodeGen code, SB sb) {
+ sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" + #");
+ _ti.print(sb);
+ }
+
+ @Override
+ public String op() {
+ return _ti.value() == 1 ? "inc" : (_ti.value() == -1 ? "dec" : "addi");
+
+ }
+
+} \ No newline at end of file
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/AndARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/AndARM.java
new file mode 100644
index 00000000..7655df40
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/AndARM.java
@@ -0,0 +1,38 @@
+package com.seaofnodes.simple.node.cpus.arm;
+
+import com.seaofnodes.simple.SB;
+import com.seaofnodes.simple.Utils;
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.RegMask;
+import com.seaofnodes.simple.node.MachConcreteNode;
+import com.seaofnodes.simple.node.MachNode;
+import com.seaofnodes.simple.node.Node;
+
+import java.io.ByteArrayOutputStream;
+
+public class AndARM extends MachConcreteNode implements MachNode {
+ AndARM(Node and) {
+ super(and);
+ }
+
+ // Register mask allowed on input i.
+ // This is the normal calling convention
+ @Override public RegMask regmap(int i) {
+ assert i==1 || i==2;
+ return arm.RMASK;
+ }
+
+ // Register mask allowed as a result. 0 for no register.
+ @Override public RegMask outregmap() { return arm.RMASK; }
+
+ // Encoding is appended into the byte array; size is returned
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+
+ // General form
+ // General form: #rd = rs1 & rs2
+ @Override public void asm(CodeGen code, SB sb){
+ sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" & ").p(code.reg(in(2)));
+ }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/AndIARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/AndIARM.java
new file mode 100644
index 00000000..ee05654b
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/AndIARM.java
@@ -0,0 +1,56 @@
+package com.seaofnodes.simple.node.cpus.arm;
+
+
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.RegMask;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.type.Type;
+import com.seaofnodes.simple.type.TypeInteger;
+import java.io.ByteArrayOutputStream;
+import java.util.BitSet;
+import java.lang.StringBuilder;
+
+public class AndIARM extends MachConcreteNode implements MachNode {
+ final TypeInteger _ti;
+
+ AndIARM(AndNode and, TypeInteger ti) {
+ super(and);
+ _inputs.pop();
+ _ti = ti;
+ }
+
+ // Register mask allowed on input i.
+ // This is the normal calling convention
+ @Override
+ public RegMask regmap(int i) {
+ assert i == 1 || i == 2;
+ return arm.RMASK;
+ }
+
+ // Register mask allowed as a result. 0 for no register.
+ @Override
+ public RegMask outregmap() {
+ return arm.RMASK;
+ }
+
+ // Encoding is appended into the byte array; size is returned
+ @Override
+ public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+
+ // General form
+ // General form: "andi rd = rs1 & imm"
+ @Override
+ public void asm(CodeGen code, SB sb) {
+ sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" & #");
+ _ti.print(sb);
+ }
+
+ @Override
+ public String op() {
+ return "andi";
+ }
+
+} \ No newline at end of file
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/AsrARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/AsrARM.java
new file mode 100644
index 00000000..a79de49b
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/AsrARM.java
@@ -0,0 +1,40 @@
+package com.seaofnodes.simple.node.cpus.arm;
+
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.RegMask;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.type.Type;
+import com.seaofnodes.simple.type.TypeInteger;
+import java.io.ByteArrayOutputStream;
+import java.util.BitSet;
+import java.lang.StringBuilder;
+
+// Arithmetic Shift Right (register)
+public class AsrARM extends MachConcreteNode implements MachNode {
+ AsrARM(Node asr) {super(asr);}
+ // Register mask allowed on input i.
+ // This is the normal calling convention
+ @Override public RegMask regmap(int i) {
+ // assert i==1;
+ return arm.RMASK; }
+ // Register mask allowed as a result. 0 for no register.
+ @Override public RegMask outregmap() { return arm.RMASK; }
+
+ // Output is same register as input#1
+ @Override public int twoAddress() { return 1; }
+
+ // Encoding is appended into the byte array; size is returned
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+
+ // General form
+ // General form: "asr rd, rs1, rs2"
+ @Override public void asm(CodeGen code, SB sb) {
+
+ sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" >> ").p(code.reg(in(2)));
+ }
+
+ @Override public String op() { return "asr"; }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/AsrIARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/AsrIARM.java
new file mode 100644
index 00000000..099c3699
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/AsrIARM.java
@@ -0,0 +1,41 @@
+package com.seaofnodes.simple.node.cpus.arm;
+
+
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.RegMask;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.type.Type;
+import com.seaofnodes.simple.type.TypeInteger;
+import java.io.ByteArrayOutputStream;
+import java.util.BitSet;
+import java.lang.StringBuilder;
+
+// Arithmetic Shift Right (immediate)
+public class AsrIARM extends MachConcreteNode implements MachNode {
+ final TypeInteger _ti;
+ AsrIARM(Node asri, TypeInteger ti) {super(asri); _inputs.pop(); _ti = ti;}
+
+ // Register mask allowed on input i.
+ // This is the normal calling convention
+ @Override public RegMask regmap(int i) {
+ // assert i==1;
+ return arm.RMASK; }
+
+ // Register mask allowed as a result. 0 for no register.
+ @Override public RegMask outregmap() { return arm.RMASK; }
+
+ // Encoding is appended into the byte array; size is returned
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+
+ // General form
+ // General form: "asri rd, rs1, imm"
+ @Override public void asm(CodeGen code, SB sb) {
+ sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" >> #");
+ _ti.print(sb);
+ }
+
+ @Override public String op() { return "asri"; }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CBranchRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/BranchARM.java
index d7e23c6d..1a5108cf 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CBranchRISC.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/BranchARM.java
@@ -1,16 +1,17 @@
-package com.seaofnodes.simple.node.cpus.riscv;
-
+package com.seaofnodes.simple.node.cpus.arm;
import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.RegMask;
import com.seaofnodes.simple.node.*;
-import com.seaofnodes.simple.type.Type;
-import com.seaofnodes.simple.type.TypeInteger;
+
import java.io.ByteArrayOutputStream;
-// Conditional branch such as: BEQ
-public class CBranchRISC extends IfNode implements MachNode{
+
+
+// Jump on flags, uses flags
+public class BranchARM extends IfNode implements MachNode{
final String _bop;
- // label is obtained implicitly
- CBranchRISC( IfNode iff, String bop ) {
+ BranchARM(IfNode iff, String bop ) {
super(iff);
_bop = bop;
}
@@ -21,28 +22,26 @@ public class CBranchRISC extends IfNode implements MachNode{
Node set = in(1);
Node cmp = set.in(1);
// Bypass an expected Set and just reference the cmp directly
- if( set instanceof SetRISC)
+ if( set instanceof SetARM)
_inputs.set(1,cmp);
else
throw Utils.TODO();
}
- @Override public RegMask regmap(int i) { assert i==1; return riscv.RMASK; }
+ @Override public RegMask regmap(int i) { assert i==1; return arm.FLAGS_MASK; }
@Override public RegMask outregmap() { return null; }
// Encoding is appended into the byte array; size is returned
-
@Override public int encoding(ByteArrayOutputStream bytes) {
throw Utils.TODO();
}
@Override public void asm(CodeGen code, SB sb) {
String src = code.reg(in(1));
- if( src!="FLAGS" ) sb.p(src);
+ if( src!="flags" ) sb.p(src);
}
- @Override public String op() { return "b"+_bop; }
-
+ @Override public String op() { return "j"+_bop; }
@Override public String comment() {
return "L"+cproj(1)._nid+", L"+cproj(0)._nid;
}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/CallARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/CallARM.java
new file mode 100644
index 00000000..4359c045
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/CallARM.java
@@ -0,0 +1,45 @@
+package com.seaofnodes.simple.node.cpus.arm;
+
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.RegMask;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.type.TypeFunPtr;
+import java.io.ByteArrayOutputStream;
+
+public class CallARM extends CallNode implements MachNode {
+ final TypeFunPtr _tfp;
+ final String _name;
+
+ CallARM(CallNode call, TypeFunPtr tfp) {
+ super(call);
+ _inputs.pop(); // Pop constant target
+ assert tfp.isConstant();
+ _tfp = tfp;
+ _name = CodeGen.CODE.link(tfp)._name;
+ }
+
+ @Override public String label() { return op(); }
+ @Override public RegMask regmap(int i) {
+ return arm.callInMask(_tfp,i); // Normal argument
+ }
+ @Override public RegMask outregmap() { return null; }
+
+ @Override public String name() { return _name; }
+ @Override public TypeFunPtr tfp() { return _tfp; }
+
+ // Encoding is appended into the byte array; size is returned
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+
+ @Override public void asm(CodeGen code, SB sb) {
+ sb.p(_name);
+ for( int i=0; i<nargs(); i++ )
+ sb.p(code.reg(arg(i))).p(" ");
+ sb.unchar(2);
+ }
+
+ @Override public String op() { return "call"; }
+
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/CallEndARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/CallEndARM.java
new file mode 100644
index 00000000..6177c5ea
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/CallEndARM.java
@@ -0,0 +1,35 @@
+package com.seaofnodes.simple.node.cpus.arm;
+
+import com.seaofnodes.simple.SB;
+import com.seaofnodes.simple.Utils;
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.RegMask;
+import com.seaofnodes.simple.node.CallEndNode;
+import com.seaofnodes.simple.node.MachNode;
+import com.seaofnodes.simple.type.TypeFloat;
+import com.seaofnodes.simple.type.TypeFunPtr;
+import java.io.ByteArrayOutputStream;
+
+public class CallEndARM extends CallEndNode implements MachNode {
+ final TypeFunPtr _tfp;
+ CallEndARM( CallEndNode cend ) {
+ super(cend);
+ _tfp = (TypeFunPtr)(cend.call().fptr()._type);
+ }
+
+ @Override public String label() { return op(); }
+ @Override public RegMask regmap(int i) { return null; }
+ @Override public RegMask outregmap() { return null; }
+ @Override public RegMask outregmap(int idx) { return idx == 2 ? arm.retMask(_tfp,2) : null; }
+ @Override public RegMask killmap() { return arm.armCallerSave(); }
+
+ // Encoding is appended into the byte array; size is returned
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+
+ @Override public void asm(CodeGen code, SB sb) { }
+
+ @Override public String op() { return "cend"; }
+
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/CallRRARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/CallRRARM.java
new file mode 100644
index 00000000..db85a9a9
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/CallRRARM.java
@@ -0,0 +1,32 @@
+package com.seaofnodes.simple.node.cpus.arm;
+
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.RegMask;
+import com.seaofnodes.simple.node.*;
+import java.io.ByteArrayOutputStream;
+
+public class 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) {
+ // Todo: float or int?
+ return i==_inputs._len
+ ? arm.RMASK // Function call target
+ : arm.callInMask(tfp(),i); // Normal argument
+ }
+ @Override public RegMask outregmap() { return null; }
+
+ // Encoding is appended into the byte array; size is returned
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+
+ @Override public void asm(CodeGen code, SB sb) {
+ 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/CmpRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/CmpARM.java
index 3bd4008e..f4f6242d 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CmpRISC.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/CmpARM.java
@@ -1,28 +1,30 @@
-package com.seaofnodes.simple.node.cpus.riscv;
+package com.seaofnodes.simple.node.cpus.arm;
import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.RegMask;
import com.seaofnodes.simple.node.*;
import com.seaofnodes.simple.type.Type;
import com.seaofnodes.simple.type.TypeFunPtr;
import java.io.ByteArrayOutputStream;
-// Compare. Sets flags.(RFLAGS)
-public class CmpRISC extends MachConcreteNode implements MachNode{
- CmpRISC( Node cmp ) { super(cmp); }
+public class CmpARM extends MachConcreteNode implements MachNode {
+ CmpARM( Node cmp ) { super(cmp); }
+ @Override public String op() { return "cmp"; }
+
+
+ @Override public RegMask regmap(int i) { assert i==1 || i==2; return arm.RMASK; }
+ @Override public RegMask outregmap() { return arm.FLAGS_MASK; }
// Encoding is appended into the byte array; size is returned
@Override public int encoding(ByteArrayOutputStream bytes) {
throw Utils.TODO();
}
-
// General form: "cmp rs1, rs2"
@Override public void asm(CodeGen code, SB sb) {
String dst = code.reg(this);
- if( dst!="FLAGS" ) sb.p(dst).p(" = ");
+ if( dst!="flags" ) sb.p(dst).p(" = ");
sb.p(code.reg(in(1))).p(", ").p(code.reg(in(2)));
}
-
- @Override public RegMask regmap(int i) { assert i==1 || i==2; return riscv.RMASK; }
- @Override public RegMask outregmap() { return riscv.FLAGS_MASK; }
}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/CmpFARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/CmpFARM.java
new file mode 100644
index 00000000..9c287fcb
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/CmpFARM.java
@@ -0,0 +1,31 @@
+package com.seaofnodes.simple.node.cpus.arm;
+
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.RegMask;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.type.Type;
+import com.seaofnodes.simple.type.TypeFunPtr;
+import java.io.ByteArrayOutputStream;
+
+// compare instruction on float regs input(D-registers)
+public class CmpFARM extends MachConcreteNode implements MachNode{
+ CmpFARM(Node cmp) {super(cmp);}
+
+ // Encoding is appended into the byte array; size is returned
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+
+ // General form: "cmp 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)));
+ }
+
+ @Override public RegMask regmap(int i) { assert i==1 || i==2; return arm.DMASK; }
+ @Override public RegMask outregmap() { return arm.FLAGS_MASK; }
+
+ @Override public String op() { return "cmpf"; }
+}
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
new file mode 100644
index 00000000..a0b10ba4
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/CmpIARM.java
@@ -0,0 +1,54 @@
+package com.seaofnodes.simple.node.cpus.arm;
+
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.RegMask;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.type.Type;
+import com.seaofnodes.simple.type.TypeInteger;
+import java.io.ByteArrayOutputStream;
+
+// Compare with immediate.
+// Conditional compare (immediate)? e.g CCMP(immediate)
+public class CmpIARM extends MachConcreteNode implements MachNode {
+ final int _imm;
+ final String _bop;
+ CmpIARM(BoolNode bool, TypeInteger ti) {
+ super(bool);
+ _inputs.pop();
+ _bop = bool.op();
+ _imm = (int)ti.value();
+ assert _imm == ti.value();
+ }
+ // Copy constructor does not set reverse edges in, because the basic block
+ // is changing from the original node - and it must be properly placed in a
+ // new block.
+ CmpIARM( CmpIARM cmp ) {
+ super(cmp);
+ _bop = cmp._bop;
+ _imm = cmp._imm;
+ // While in(0) will be handled by the caller, all other edges must be
+ // handled in the constructor.
+ cmp.in(1)._outputs.push(this);
+ }
+
+ @Override public RegMask regmap(int i) { assert i==1; 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); }
+
+ // Encoding is appended into the byte array; size is returned
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+
+ // General form: "cmp rs1, 1"
+ @Override public void asm(CodeGen code, SB sb) {
+ String dst = code.reg(this);
+ if( dst!="flags" ) sb.p(dst).p(" = ");
+ sb.p(code.reg(in(1)));
+ if( _imm != 0 ) sb.p(", #").p(_imm);
+ }
+ @Override public String op() { return _imm==0 ? "test" : "cmp"; }
+
+ }
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/CmpMemARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/CmpMemARM.java
new file mode 100644
index 00000000..47f68ccc
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/CmpMemARM.java
@@ -0,0 +1,4 @@
+package com.seaofnodes.simple.node.cpus.arm;
+
+public class CmpMemARM {
+}
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
new file mode 100644
index 00000000..7c67e2dd
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/DivARM.java
@@ -0,0 +1,33 @@
+package com.seaofnodes.simple.node.cpus.arm;
+
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.RegMask;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.type.TypeInteger;
+import java.io.ByteArrayOutputStream;
+
+public class DivARM extends MachConcreteNode implements MachNode {
+ DivARM( Node div ) { super(div); }
+
+ // Register mask allowed on input i.
+ @Override public RegMask regmap(int i) {
+ assert i==1 || i==2;
+ return arm.RMASK; }
+ // Register mask allowed as a result. 0 for no register.
+ @Override public RegMask outregmap() { return arm.RMASK; }
+ // Output is same register as input#1
+ @Override public int twoAddress() { return 1; }
+
+ // Encoding is appended into the byte array; size is returned
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+
+ // General form: "div dst /= src"
+ @Override public void asm(CodeGen code, SB sb) {
+ sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" / ").p(code.reg(in(2)));
+ }
+
+ @Override public String op() { return "div"; }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/DivFARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/DivFARM.java
new file mode 100644
index 00000000..2137c01c
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/DivFARM.java
@@ -0,0 +1,30 @@
+package com.seaofnodes.simple.node.cpus.arm;
+
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.RegMask;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.type.TypeInteger;
+import java.io.ByteArrayOutputStream;
+
+public class DivFARM extends MachConcreteNode implements MachNode {
+ DivFARM( Node divf) { super(divf); }
+
+ // Register mask allowed on input i.
+ @Override public RegMask regmap(int i) { assert i==1 || i==2; return arm.DMASK; }
+ // Register mask allowed as a result. 0 for no register.
+ @Override public RegMask outregmap() { return arm.DMASK; }
+
+ // Encoding is appended into the byte array; size is returned
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+
+
+ // 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)));
+ }
+
+ @Override public String op() { return "divf"; }
+}
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
new file mode 100644
index 00000000..7fafc31c
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/FloatARM.java
@@ -0,0 +1,36 @@
+package com.seaofnodes.simple.node.cpus.arm;
+
+import com.seaofnodes.simple.SB;
+import com.seaofnodes.simple.Utils;
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.RegMask;
+import com.seaofnodes.simple.node.ConstantNode;
+import com.seaofnodes.simple.node.MachNode;
+import java.io.ByteArrayOutputStream;
+
+//FMOV (scalar, immediate)
+//Floating-point move immediate.
+public class FloatARM extends ConstantNode implements MachNode {
+ FloatARM(ConstantNode con) { super(con); }
+ @Override public String op() { return "fld"; }
+ // Register mask allowed on input i. 0 for no register.
+ @Override public RegMask regmap(int i) { return null; }
+ // General int registers
+ @Override public RegMask outregmap() { return arm.DMASK; }
+
+ @Override public boolean isClone() { return true; }
+ @Override public FloatARM copy() { return new FloatARM(this); }
+
+ // Encoding is appended into the byte array; size is returned
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+
+ // Human-readable form appended to the SB. Things like the encoding,
+ // indentation, leading address or block labels not printed here.
+ // Just something like "ld4\tR17=[R18+12] // Load array base".
+ // General form: "op\tdst=src+src"
+ @Override public void asm(CodeGen code, SB sb) {
+ _con.print(sb.p(code.reg(this)).p(" #"));
+ }
+}
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
new file mode 100644
index 00000000..0134911f
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/FunARM.java
@@ -0,0 +1,21 @@
+package com.seaofnodes.simple.node.cpus.arm;
+
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.RegMask;
+import com.seaofnodes.simple.Utils;
+import com.seaofnodes.simple.node.FunNode;
+import com.seaofnodes.simple.node.MachNode;
+import java.io.ByteArrayOutputStream;
+
+public class FunARM extends FunNode implements MachNode{
+ FunARM(FunNode fun) {super(fun);}
+ // Register mask allowed on input i. 0 for no register.
+ @Override public RegMask regmap(int i) { return null; }
+ // Register mask allowed as a result. 0 for no register.
+ @Override public RegMask outregmap() { return null; }
+
+ // Encoding is appended into the byte array; size is returned
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/I2F8ARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/I2F8ARM.java
new file mode 100644
index 00000000..b106ab8a
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/I2F8ARM.java
@@ -0,0 +1,29 @@
+package com.seaofnodes.simple.node.cpus.arm;
+
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.RegMask;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.type.TypeInteger;
+import java.io.ByteArrayOutputStream;
+
+// VCVT (between floating-point and integer)
+public class I2F8ARM extends MachConcreteNode implements MachNode {
+ I2F8ARM(Node i2f8) {super(i2f8);}
+ @Override public String op() { return "i2f8"; }
+
+ // Register mask allowed on input i.
+ @Override public RegMask regmap(int i) { assert i==1; return arm.RMASK; }
+ // Register mask allowed as a result. 0 for no register.
+ @Override public RegMask outregmap() { return arm.DMASK; }
+
+ // Encoding is appended into the byte array; size is returned
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+
+ // General form: "i2f8 (flt)int_value"
+ @Override public void asm(CodeGen code, SB sb) {
+ sb.p(code.reg(this)).p(" = ").p("(flt)").p(code.reg(in(1)));
+ }
+}
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
new file mode 100644
index 00000000..1f81300e
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/IntARM.java
@@ -0,0 +1,38 @@
+package com.seaofnodes.simple.node.cpus.arm;
+
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.RegMask;
+import com.seaofnodes.simple.node.ConstantNode;
+import com.seaofnodes.simple.node.MachNode;
+import com.seaofnodes.simple.type.Type;
+import com.seaofnodes.simple.type.TypeInteger;
+import java.io.ByteArrayOutputStream;
+
+// Integer constants
+public class IntARM extends ConstantNode implements MachNode {
+ IntARM( ConstantNode con ) { super(con); }
+ @Override public String op() { return "ldi"; }
+
+ // Register mask allowed on input i. 0 for no register.
+ @Override public RegMask regmap(int i) { return null; }
+ // General int registers
+ @Override public RegMask outregmap() { return arm.WMASK; }
+
+ @Override public boolean isClone() { return true; }
+ @Override public IntARM copy() { return new IntARM(this); }
+
+ // Encoding is appended into the byte array; size is returned
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+
+ // Human-readable form appended to the SB. Things like the encoding,
+ // indentation, leading address or block labels not printed here.
+ // Just something like "ld4\tR17=[R18+12] // Load array base".
+ // General form: "op\tdst=src+src"
+ @Override public void asm(CodeGen code, SB sb) {
+ String reg = code.reg(this);
+ _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
new file mode 100644
index 00000000..0d59cc47
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/LoadARM.java
@@ -0,0 +1,37 @@
+package com.seaofnodes.simple.node.cpus.arm;
+
+
+import com.seaofnodes.simple.SB;
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.RegMask;
+import com.seaofnodes.simple.node.LoadNode;
+import com.seaofnodes.simple.node.Node;
+
+
+// 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 RegMask regmap(int i) {
+ return arm.RMASK;
+ }
+
+ // Wide mask loads both ints and floats; encoding varies.
+ @Override public RegMask outregmap() { return arm.MEM_MASK; }
+
+
+ @Override public String op() { return "ld"+_sz; }
+
+ @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
new file mode 100644
index 00000000..6108c889
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/LslARM.java
@@ -0,0 +1,41 @@
+package com.seaofnodes.simple.node.cpus.arm;
+
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.RegMask;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.type.Type;
+import com.seaofnodes.simple.type.TypeInteger;
+import java.io.ByteArrayOutputStream;
+import java.util.BitSet;
+import java.lang.StringBuilder;
+
+
+// Logical Shift Left (register)
+public class LslARM extends MachConcreteNode implements MachNode {
+ LslARM(Node asr) {super(asr);}
+ // Register mask allowed on input i.
+ // This is the normal calling convention
+ @Override public RegMask regmap(int i) {
+ // assert i==1;
+ return arm.RMASK; }
+ // Register mask allowed as a result. 0 for no register.
+ @Override public RegMask outregmap() { return arm.RMASK; }
+
+ // Output is same register as input#1
+ @Override public int twoAddress() { return 1; }
+
+ // Encoding is appended into the byte array; size is returned
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+
+ // General form
+ // General form: "lsl rd, rs1, rs2"
+ @Override public void asm(CodeGen code, SB sb) {
+
+ sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" << ").p(code.reg(in(2)));
+ }
+
+ @Override public String op() { return "lsl"; }
+}
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
new file mode 100644
index 00000000..7a7014f4
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/LslIARM.java
@@ -0,0 +1,42 @@
+package com.seaofnodes.simple.node.cpus.arm;
+
+
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.RegMask;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.type.Type;
+import com.seaofnodes.simple.type.TypeInteger;
+import java.io.ByteArrayOutputStream;
+import java.util.BitSet;
+import java.lang.StringBuilder;
+
+
+// Logical Shift Left (immediate)
+public class LslIARM extends MachConcreteNode implements MachNode {
+ final TypeInteger _ti;
+ LslIARM(Node lsli, TypeInteger ti) {super(lsli); _inputs.pop(); _ti = ti;}
+
+ // Register mask allowed on input i.
+ // This is the normal calling convention
+ @Override public RegMask regmap(int i) {
+ // assert i==1;
+ return arm.RMASK; }
+
+ // Register mask allowed as a result. 0 for no register.
+ @Override public RegMask outregmap() { return arm.RMASK; }
+
+ // Encoding is appended into the byte array; size is returned
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+
+ // General form
+ // General form: "lsli rd, rs1, imm"
+ @Override public void asm(CodeGen code, SB sb) {
+ sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" << #");
+ _ti.print(sb);
+ }
+
+ @Override public String op() { return "lsli"; }
+}
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
new file mode 100644
index 00000000..e6168dee
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/LsrARM.java
@@ -0,0 +1,40 @@
+package com.seaofnodes.simple.node.cpus.arm;
+
+
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.RegMask;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.type.Type;
+import com.seaofnodes.simple.type.TypeInteger;
+import java.io.ByteArrayOutputStream;
+import java.util.BitSet;
+import java.lang.StringBuilder;
+
+// Logical Shift Right (register)
+public class LsrARM extends MachConcreteNode implements MachNode {
+ LsrARM(Node asr) {super(asr);}
+ // Register mask allowed on input i.
+ // This is the normal calling convention
+ @Override public RegMask regmap(int i) {
+ // assert i==1;
+ return arm.RMASK; }
+ // Register mask allowed as a result. 0 for no register.
+ @Override public RegMask outregmap() { return arm.RMASK; }
+
+ // Output is same register as input#1
+ @Override public int twoAddress() { return 1; }
+
+ // Encoding is appended into the byte array; size is returned
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+
+ // General form
+ // General form: "lsr rd, rs1, rs2"
+ @Override public void asm(CodeGen code, SB sb) {
+ sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" >>> ").p(code.reg(in(2)));
+ }
+
+ @Override public String op() { return "lsr"; }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/LsrIARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/LsrIARM.java
new file mode 100644
index 00000000..8fb20cb2
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/LsrIARM.java
@@ -0,0 +1,41 @@
+package com.seaofnodes.simple.node.cpus.arm;
+
+
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.RegMask;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.type.Type;
+import com.seaofnodes.simple.type.TypeInteger;
+import java.io.ByteArrayOutputStream;
+import java.util.BitSet;
+import java.lang.StringBuilder;
+
+// Logical Shift Right (immediate)
+public class LsrIARM extends MachConcreteNode implements MachNode{
+ final TypeInteger _ti;
+ LsrIARM(Node lsri, TypeInteger ti) {super(lsri); _inputs.pop(); _ti = ti;}
+
+ // Register mask allowed on input i.
+ // This is the normal calling convention
+ @Override public RegMask regmap(int i) {
+ // assert i==1;
+ return arm.RMASK; }
+
+ // Register mask allowed as a result. 0 for no register.
+ @Override public RegMask outregmap() { return arm.RMASK; }
+
+ // Encoding is appended into the byte array; size is returned
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+
+ // General form
+ // General form: "lsrl rd, rs1, imm"
+ @Override public void asm(CodeGen code, SB sb) {
+ sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" >>> #");
+ _ti.print(sb);
+ }
+
+ @Override public String op() { return "lsrl"; }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/MemOpARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/MemOpARM.java
new file mode 100644
index 00000000..adffa501
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/MemOpARM.java
@@ -0,0 +1,58 @@
+package com.seaofnodes.simple.node.cpus.arm;
+
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.RegMask;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.type.*;
+import java.io.ByteArrayOutputStream;
+import java.lang.StringBuilder;
+import java.util.BitSet;
+
+
+public class MemOpARM extends MemOpNode implements MachNode {
+ final int _off; // Limit 32 bits
+ final int _imm; // Limit 32 bits
+ final char _sz = (char)('0'+(1<<_declaredType.log_size()));
+ MemOpARM(MemOpNode mop, Node base, Node idx, int off, int imm) {
+ super(mop,mop);
+ assert base._type instanceof TypeMemPtr && !(base instanceof AddNode);
+ assert ptr() == base;
+ _inputs.setX(3,idx);
+ _off = off;
+ _imm = imm;
+ }
+
+ // Store-based flavors have a value edge
+ MemOpARM( MemOpNode mop, Node base, Node idx, int off, int imm, Node val ) {
+ this(mop,base,idx,off,imm);
+ _inputs.setX(4,val);
+ }
+
+ Node idx() { return in(3); }
+ Node val() { return in(4); } // Only for stores, includin
+
+ @Override 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) {
+ throw Utils.TODO();
+ }
+ // Register mask allowed as a result. 0 for no register.
+ @Override public RegMask outregmap() { throw Utils.TODO(); }
+
+ @Override public int encoding(ByteArrayOutputStream bytes) { throw Utils.TODO(); }
+
+ SB asm_address(CodeGen code, SB sb) {
+ sb.p("[").p(code.reg(ptr())).p("+");
+ if( idx() != null ) sb.p(code.reg(idx()));
+ else sb.p(_off);
+ return sb.p("]");
+ }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/MulARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/MulARM.java
new file mode 100644
index 00000000..582b97d4
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/MulARM.java
@@ -0,0 +1,34 @@
+package com.seaofnodes.simple.node.cpus.arm;
+
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.RegMask;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.type.TypeInteger;
+import java.io.ByteArrayOutputStream;
+
+
+// mulh signed multiply instruction(no-imm form)
+public class MulARM extends MachConcreteNode implements MachNode{
+ MulARM(Node mul) {super(mul);}
+
+ // Register mask allowed on input i.
+ @Override public RegMask regmap(int i) { assert i==1 || i==2; return arm.RMASK; }
+ // Register mask allowed as a result. 0 for no register.
+ @Override public RegMask outregmap() { return arm.RMASK; }
+ // Output is same register as input#1
+ @Override public int twoAddress() { return 1; }
+
+ // Encoding is appended into the byte array; size is returned
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+
+ // General form: "rd = rs1 * rs2"
+ @Override public void asm(CodeGen code, SB sb) {
+ sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" * ").p(code.reg(in(2)));
+ }
+
+ @Override public String op() { return "mul"; }
+
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/MulFARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/MulFARM.java
new file mode 100644
index 00000000..b71b412d
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/MulFARM.java
@@ -0,0 +1,30 @@
+package com.seaofnodes.simple.node.cpus.arm;
+
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.RegMask;
+import com.seaofnodes.simple.node.*;
+import java.io.ByteArrayOutputStream;
+
+public class MulFARM extends MachConcreteNode implements MachNode{
+ MulFARM(Node mulf) {super(mulf);}
+
+ // Register mask allowed on input i.
+ @Override public RegMask regmap(int i) { assert i==1 || i==2; return arm.DMASK; }
+ // Register mask allowed as a result. 0 for no register.
+ @Override public RegMask outregmap() { return arm.DMASK; }
+
+ // Encoding is appended into the byte array; size is returned
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+
+ // Default on double precision for now(64 bits)
+ // General form: "VMUL.f32 rd = src1 * src2
+ @Override public void asm(CodeGen code, SB sb) {
+ sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" * ").p(code.reg(in(2)));
+ }
+
+ @Override public String op() { return "mulf"; }
+
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/MulIRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/MulIARM.java
index 8735068e..c118b00a 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/MulIRISC.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/MulIARM.java
@@ -1,29 +1,37 @@
-package com.seaofnodes.simple.node.cpus.riscv;
+package com.seaofnodes.simple.node.cpus.arm;
import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.RegMask;
import com.seaofnodes.simple.node.*;
import com.seaofnodes.simple.type.TypeInteger;
import java.io.ByteArrayOutputStream;
-public class MulIRISC extends MachConcreteNode implements MachNode {
+public class MulIARM extends MachConcreteNode implements MachNode{
final TypeInteger _ti;
- MulIRISC(Node mul, TypeInteger ti) {super(mul); _inputs.pop(); _ti = ti;}
+ MulIARM(Node mul, TypeInteger ti) {
+ super(mul);
+ _inputs.pop();
+ _ti = ti;
+ }
// Register mask allowed on input i.
// This is the normal calling convention
@Override public RegMask regmap(int i) {
- return riscv.RMASK; }
- // Register mask allowed as a result. 0 for no register.
- @Override public RegMask outregmap() { return riscv.RMASK; }
+ // assert i==1;
+ return arm.RMASK; }
+ // Register mask allowed as a result. 0 for no register.
+ @Override public RegMask outregmap() { return arm.RMASK; }
// Output is same register as input#1
- @Override public int twoAddress() { return 0; }
+ @Override public int twoAddress() { return 1; }
// Encoding is appended into the byte array; size is returned
@Override public int encoding(ByteArrayOutputStream bytes) {
throw Utils.TODO();
}
+
// General form
// General form: "muli dst * #imm"
@Override public void asm(CodeGen code, SB sb) {
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/NewARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/NewARM.java
new file mode 100644
index 00000000..bd88fa83
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/NewARM.java
@@ -0,0 +1,31 @@
+package com.seaofnodes.simple.node.cpus.arm;
+
+
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.RegMask;
+import com.seaofnodes.simple.node.*;
+
+import java.io.ByteArrayOutputStream;
+
+public class NewARM extends NewNode implements MachNode {
+ // A pre-zeroed chunk of memory.
+ NewARM(NewNode nnn) { super(nnn); }
+ // Size and pointer result in standard calling convention; null for all the
+ // memory aliases edges
+ @Override public RegMask regmap(int i) { return i == 1 ? arm. X0_MASK : null; }
+ @Override public RegMask outregmap(int i) { return i == 1 ? arm. X0_MASK : null; }
+ @Override public RegMask outregmap() { return null; }
+
+ // Encoding is appended into the byte array; size is returned
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+
+ // General form: "alloc #bytes"
+ @Override public void asm(CodeGen code, SB sb) {
+ sb.p(code.reg(size()));
+ }
+
+ @Override public String op() { return "alloc"; }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/NotARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/NotARM.java
new file mode 100644
index 00000000..28ebd60d
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/NotARM.java
@@ -0,0 +1,26 @@
+package com.seaofnodes.simple.node.cpus.arm;
+
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.LRG;
+import com.seaofnodes.simple.codegen.RegMask;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.type.TypeInteger;
+import java.io.ByteArrayOutputStream;
+
+import com.seaofnodes.simple.node.MachConcreteNode;
+
+public class NotARM extends MachConcreteNode implements MachNode{
+ NotARM(NotNode not) {super(not);}
+ @Override public RegMask regmap(int i) { return arm.RMASK; }
+ @Override public RegMask outregmap() { return arm.RMASK; }
+
+ @Override public int twoAddress( ) { return 0; }
+
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+
+ @Override public void asm(CodeGen code, SB sb) { sb.p(code.reg(this)); }
+ @Override public String op() { return "not"; }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/OrARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/OrARM.java
new file mode 100644
index 00000000..12ea8265
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/OrARM.java
@@ -0,0 +1,42 @@
+package com.seaofnodes.simple.node.cpus.arm;
+
+import com.seaofnodes.simple.SB;
+import com.seaofnodes.simple.Utils;
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.RegMask;
+import com.seaofnodes.simple.node.MachConcreteNode;
+import com.seaofnodes.simple.node.MachNode;
+import com.seaofnodes.simple.node.Node;
+
+import java.io.ByteArrayOutputStream;
+
+// Not using the ORRS variant.
+public class OrARM extends MachConcreteNode implements MachNode {
+ OrARM(Node and) {
+ super(and);
+ }
+
+ // Register mask allowed on input i.
+ // This is the normal calling convention
+ @Override public RegMask regmap(int i) {
+ assert i==1 || i==2;
+ return arm.RMASK;
+ }
+
+ // Register mask allowed as a result. 0 for no register.
+ @Override public RegMask outregmap() { return arm.RMASK; }
+
+ // Encoding is appended into the byte array; size is returned
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+
+ // General form
+ // General form: #rd = rs1 & rs2
+ @Override public void asm(CodeGen code, SB sb) {
+
+ sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" | ").p(code.reg(in(2)));
+ }
+
+ @Override public String op() { return "orr"; }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/OrIARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/OrIARM.java
new file mode 100644
index 00000000..f257a867
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/OrIARM.java
@@ -0,0 +1,40 @@
+package com.seaofnodes.simple.node.cpus.arm;
+
+import com.seaofnodes.simple.SB;
+import com.seaofnodes.simple.Utils;
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.RegMask;
+import com.seaofnodes.simple.node.MachConcreteNode;
+import com.seaofnodes.simple.node.MachNode;
+import com.seaofnodes.simple.node.Node;
+
+import com.seaofnodes.simple.type.Type;
+import com.seaofnodes.simple.type.TypeInteger;
+import java.io.ByteArrayOutputStream;
+
+public class OrIARM extends MachConcreteNode implements MachNode{
+ final TypeInteger _ti;
+ OrIARM(Node or, TypeInteger ti) {super(or); _inputs.pop(); _ti = ti;}
+ // Register mask allowed on input i.
+ // This is the normal calling convention
+ @Override public RegMask regmap(int i) {
+ assert i==1 || i==2;
+ return arm.RMASK;
+ }
+ // Register mask allowed as a result. 0 for no register.
+ @Override public RegMask outregmap() { return arm.RMASK; }
+
+ // Encoding is appended into the byte array; size is returned
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+
+ // General form
+ // General form: "rd = rs1 | imm"
+ @Override public void asm(CodeGen code, SB sb) {
+ sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" | #");
+ _ti.print(sb);
+ }
+
+ @Override public String op() { return "ori"; }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/ParmARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/ParmARM.java
new file mode 100644
index 00000000..c3fa132c
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/ParmARM.java
@@ -0,0 +1,25 @@
+package com.seaofnodes.simple.node.cpus.arm;
+
+import com.seaofnodes.simple.SB;
+import com.seaofnodes.simple.Utils;
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.RegMask;
+import com.seaofnodes.simple.node.MachNode;
+import com.seaofnodes.simple.node.ParmNode;
+import java.io.ByteArrayOutputStream;
+
+public class ParmARM extends ParmNode implements MachNode {
+ final RegMask _rmask;
+ ParmARM(ParmNode parm) {
+ super(parm);
+ // Assume int
+ _rmask = arm.callInMask(fun().sig(),_idx);
+ }
+ // Register mask allowed on input i. 0 for no register.
+ @Override public RegMask regmap(int i) { return null; }
+ // Register mask allowed as a result. Calling convention register
+ @Override public RegMask outregmap() { return _rmask; }
+
+ // Encoding is appended into the byte array. Returns size
+ @Override public int encoding(ByteArrayOutputStream bytes) { return 0; }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/ProjARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/ProjARM.java
new file mode 100644
index 00000000..b2add015
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/ProjARM.java
@@ -0,0 +1,23 @@
+package com.seaofnodes.simple.node.cpus.arm;
+
+import com.seaofnodes.simple.Utils;
+import com.seaofnodes.simple.codegen.RegMask;
+import com.seaofnodes.simple.node.MachNode;
+import com.seaofnodes.simple.node.ProjNode;
+import java.io.ByteArrayOutputStream;
+
+public class ProjARM extends ProjNode implements MachNode {
+ ProjARM( ProjNode p ) { super(p); }
+
+ // Register mask allowed on input i. 0 for no register.
+ @Override public RegMask regmap(int i) { return null; }
+ // Register mask allowed as a result. 0 for no register.
+ @Override public RegMask outregmap() {
+ return ((MachNode)in(0)).outregmap(_idx);
+ }
+
+ // Encoding is appended into the byte array; size is returned
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/RetARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/RetARM.java
new file mode 100644
index 00000000..c7d0dd4c
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/RetARM.java
@@ -0,0 +1,49 @@
+package com.seaofnodes.simple.node.cpus.arm;
+
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.RegMask;
+import com.seaofnodes.simple.node.FunNode;
+import com.seaofnodes.simple.node.ReturnNode;
+import com.seaofnodes.simple.node.MachNode;
+import com.seaofnodes.simple.type.TypeFloat;
+
+import java.io.ByteArrayOutputStream;
+
+
+public class RetARM extends ReturnNode implements MachNode {
+ RetARM(ReturnNode ret, FunNode fun) { super(ret, fun); fun.setRet(this); }
+
+ // Register mask allowed on input i.
+ // This is the normal calling convention
+ @Override public RegMask regmap(int i) { return arm.retMask(_fun.sig(),i); }
+ // Register mask allowed as a result. 0 for no register.
+ @Override public RegMask outregmap() { return null; }
+
+ // Encoding is appended into the byte array; size is returned
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+
+ @Override public void asm(CodeGen code, SB sb) {
+ // Post code-gen, just print the "ret"
+ if( code._phase.ordinal() <= CodeGen.Phase.RegAlloc.ordinal() )
+ // Prints return reg (either X0 or D0), RPC (always R30) and
+ // then the callee-save registers.
+ for( int i=2; i<nIns(); i++ )
+ sb.p(code.reg(in(i))).p(" ");
+
+ // Post-allocation, if we did not get the expected return register, print what we got
+ else if( code._regAlloc.regnum(in(3)) != arm.X30 )
+ sb.p("[").p(code.reg(in(3))).p("]");
+ }
+
+ // Correct Nodes outside the normal edges
+ @Override public void postSelect() {
+ FunNode fun = (FunNode)rpc().in(0);
+ _fun = fun;
+ fun.setRet(this);
+ }
+
+ @Override public String op() { return "ret"; }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/SetARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/SetARM.java
new file mode 100644
index 00000000..c3945d1a
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/SetARM.java
@@ -0,0 +1,41 @@
+package com.seaofnodes.simple.node.cpus.arm;
+
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.RegMask;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.type.Type;
+import com.seaofnodes.simple.type.TypeInteger;
+import java.io.ByteArrayOutputStream;
+
+
+// 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 RegMask regmap(int i) { assert i==1; return arm.FLAGS_MASK; }
+ @Override public RegMask outregmap() { return arm.RMASK; }
+
+ // Encoding is appended into the byte array; size is returned
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+
+ @Override public void asm(CodeGen code, SB sb) {
+ sb.p(code.reg(this));
+ String src = code.reg(in(1));
+ if( src!="flags" ) sb.p(" = ").p(src);
+ }
+
+ @Override public String op() { return "set"+_bop; }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/SplitARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/SplitARM.java
new file mode 100644
index 00000000..85771842
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/SplitARM.java
@@ -0,0 +1,22 @@
+package com.seaofnodes.simple.node.cpus.arm;
+
+import com.seaofnodes.simple.Utils;
+import com.seaofnodes.simple.codegen.RegMask;
+import com.seaofnodes.simple.node.Node;
+import com.seaofnodes.simple.node.SplitNode;
+import java.io.ByteArrayOutputStream;
+
+public class SplitARM extends SplitNode {
+ SplitARM(String kind, byte round) { super(kind,round,new Node[2]);}
+
+ // Register mask allowed on input i.
+ @Override public RegMask regmap(int i) { return arm.SPLIT_MASK; }
+
+ // Register mask allowed as a result. 0 for no register.
+ @Override public RegMask outregmap() { return arm.SPLIT_MASK; }
+
+ // Encoding is appended into the byte array; size is returned
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/StoreARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/StoreARM.java
new file mode 100644
index 00000000..49045b28
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/StoreARM.java
@@ -0,0 +1,52 @@
+package com.seaofnodes.simple.node.cpus.arm;
+
+import com.seaofnodes.simple.SB;
+import com.seaofnodes.simple.Utils;
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.RegMask;
+import com.seaofnodes.simple.node.MachConcreteNode;
+import com.seaofnodes.simple.node.MachNode;
+import com.seaofnodes.simple.node.Node;
+import com.seaofnodes.simple.node.StoreNode;
+import java.io.ByteArrayOutputStream;
+import java.util.BitSet;
+
+// Store memory addressing on ARM
+// Support imm, reg(direct), or reg+off(indirect) addressing
+// Base = base - base pointer, offset is added to base
+// idx = null
+// off = off - offset added to base)
+// imm = imm - immediate value to store
+// val = Node of immediate value to store(null if its a constant immediate)
+
+//e.g s.cs[0] = 67; // C
+// base = s.cs, off = 4, imm = 67, val = null
+public class StoreARM extends MemOpARM {
+ StoreARM(StoreNode st, Node base, Node idx, int off, int imm, Node val) {
+ super(st, base, idx, off, imm, val);
+ }
+
+ @Override public String op() { return "st"+_sz; }
+
+ // Wider mask to store both GPRs and FPRs
+ @Override public RegMask regmap(int i) {
+ if( i==1 ) return arm.MEM_MASK;
+ if( i==2 ) return arm.RMASK;
+ if( i==3 ) return arm.RMASK;
+ if( i==4 ) return arm.RMASK;
+ throw Utils.TODO();
+ }
+ // Register mask allowed as a result. 0 for no register.
+ @Override public RegMask outregmap() { return null; }
+
+ // Encoding is appended into the byte array; size is returned
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+
+ @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
new file mode 100644
index 00000000..27555b53
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/SubARM.java
@@ -0,0 +1,30 @@
+package com.seaofnodes.simple.node.cpus.arm;
+
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.RegMask;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.type.TypeInteger;
+import java.io.ByteArrayOutputStream;
+
+
+public class SubARM extends MachConcreteNode implements MachNode {
+ SubARM(Node sub) {super(sub);}
+
+ // Register mask allowed on input i.
+ @Override public RegMask regmap(int i) { assert i==1 || i==2; return arm.RMASK; }
+ // Register mask allowed as a result. 0 for no register.
+ @Override public RegMask outregmap() { return arm.RMASK; }
+
+ // Encoding is appended into the byte array; size is returned
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+
+ // General form: "sub # rd = rs1 - rs2"
+ @Override public void asm(CodeGen code, SB sb) {
+ sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" - ").p(code.reg(in(2)));
+ }
+
+ @Override public String op() { return "sub"; }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/SubFARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/SubFARM.java
new file mode 100644
index 00000000..8ae05b40
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/SubFARM.java
@@ -0,0 +1,32 @@
+package com.seaofnodes.simple.node.cpus.arm;
+
+
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.RegMask;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.type.TypeInteger;
+import java.io.ByteArrayOutputStream;
+
+public class SubFARM extends MachConcreteNode implements MachNode{
+ SubFARM(Node subf) {super(subf);}
+
+ // Register mask allowed on input i.
+ @Override public RegMask regmap(int i) { assert i==1 || i==2; return arm.DMASK; }
+ // Register mask allowed as a result. 0 for no register.
+ @Override public RegMask outregmap() { return arm.DMASK; }
+
+ // Encoding is appended into the byte array; size is returned
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+
+ // Default on double precision for now(64 bits)
+ // General form: "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)));
+ }
+
+ @Override public String op() { return "subf"; }
+
+}
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
new file mode 100644
index 00000000..d19210cb
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/SubIARM.java
@@ -0,0 +1,42 @@
+package com.seaofnodes.simple.node.cpus.arm;
+
+import com.seaofnodes.simple.node.MachConcreteNode;
+
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.LRG;
+import com.seaofnodes.simple.codegen.RegMask;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.type.TypeInteger;
+import java.io.ByteArrayOutputStream;
+
+public class SubIARM extends MachConcreteNode implements MachNode {
+ final TypeInteger _ti;
+ SubIARM(Node sub, TypeInteger ti) {
+ super(sub);
+ _inputs.pop();
+ _ti = ti;
+ }
+
+ // Register mask allowed on input i.
+ @Override public RegMask regmap(int i) { return arm.RMASK; }
+
+ // Register mask allowed as a result. 0 for no register.
+ @Override public RegMask outregmap() { return arm.RMASK; }
+
+ // Encoding is appended into the byte array; size is returned
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+
+ // General form: "subi rd = rs1 - imm"
+ @Override public void asm(CodeGen code, SB sb) {
+ sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" - #");
+ _ti.print(sb);
+ }
+
+ @Override public String op() {
+ return (_ti.value() == -1 ? "dec" : "subi");
+ }
+
+}
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
new file mode 100644
index 00000000..cdb2b6ec
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/TFPARM.java
@@ -0,0 +1,37 @@
+package com.seaofnodes.simple.node.cpus.arm;
+
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.RegMask;
+import com.seaofnodes.simple.node.ConstantNode;
+import com.seaofnodes.simple.node.MachNode;
+import com.seaofnodes.simple.type.Type;
+import com.seaofnodes.simple.type.TypeFunPtr;
+import java.io.ByteArrayOutputStream;
+
+public class TFPARM extends ConstantNode implements MachNode {
+ TFPARM( ConstantNode con ) { super(con); }
+ @Override public String op() { return "ldx"; }
+ // Register mask allowed on input i. 0 for no register.
+ @Override public RegMask regmap(int i) { return null; }
+ // General int registers
+ @Override public RegMask outregmap() { return arm.WMASK; }
+
+ @Override public boolean isClone() { return true; }
+ @Override public TFPARM copy() { return new TFPARM(this); }
+
+ // Encoding is appended into the byte array; size is returned
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+
+ // Human-readable form appended to the SB. Things like the encoding,
+ // indentation, leading address or block labels not printed here.
+ // Just something like "ld4\tR17=[R18+12] // Load array base".
+ // General form: "op\tdst=src+src"
+ @Override public void asm(CodeGen code, SB sb) {
+ String reg = code.reg(this);
+ _con.print(sb.p(reg).p(" #"));
+ }
+
+}
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
new file mode 100644
index 00000000..fd127b18
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/XorARM.java
@@ -0,0 +1,41 @@
+package com.seaofnodes.simple.node.cpus.arm;
+
+import com.seaofnodes.simple.SB;
+import com.seaofnodes.simple.Utils;
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.RegMask;
+import com.seaofnodes.simple.node.MachConcreteNode;
+import com.seaofnodes.simple.node.MachNode;
+import com.seaofnodes.simple.node.Node;
+import com.seaofnodes.simple.type.TypeInteger;
+import java.io.ByteArrayOutputStream;
+
+public class XorARM extends MachConcreteNode implements MachNode{
+ XorARM(Node xor) {super(xor); _inputs.pop();}
+
+ // Register mask allowed on input i.
+ // This is the normal calling convention
+ @Override public RegMask regmap(int i) {
+ assert i==1 || i==2;
+
+ return arm.RMASK; }
+
+ // Register mask allowed as a result. 0 for no register.
+ @Override public RegMask outregmap() { return arm.RMASK; }
+
+ // Output is same register as input#1
+ @Override public int twoAddress() { return 1; }
+
+ // Encoding is appended into the byte array; size is returned
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+
+ // General form
+ // General form: "rd = 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)));
+ }
+
+ @Override public String op() { return "eor"; }
+} \ No newline at end of file
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
new file mode 100644
index 00000000..d230ea47
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/XorIARM.java
@@ -0,0 +1,43 @@
+package com.seaofnodes.simple.node.cpus.arm;
+
+import com.seaofnodes.simple.SB;
+import com.seaofnodes.simple.Utils;
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.RegMask;
+import com.seaofnodes.simple.node.MachConcreteNode;
+import com.seaofnodes.simple.node.MachNode;
+import com.seaofnodes.simple.node.Node;
+import com.seaofnodes.simple.type.TypeInteger;
+import java.io.ByteArrayOutputStream;
+
+public class XorIARM extends MachConcreteNode implements MachNode{
+ final TypeInteger _ti;
+ XorIARM(Node xor, TypeInteger ti) {super(xor); _inputs.pop(); _ti = ti;}
+
+ // Register mask allowed on input i.
+ // This is the normal calling convention
+ @Override public RegMask regmap(int i) {
+ assert i==1 || i==2;
+
+ return arm.RMASK; }
+
+ // Register mask allowed as a result. 0 for no register.
+ @Override public RegMask outregmap() { return arm.RMASK; }
+
+ // Output is same register as input#1
+ @Override public int twoAddress() { return 1; }
+
+ // Encoding is appended into the byte array; size is returned
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+
+ // General form
+ // General form: "rd = rs1 ^ imm"
+ @Override public void asm(CodeGen code, SB sb) {
+ sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" ^ #");
+ _ti.print(sb);
+ }
+
+ @Override public String op() { return "eor"; }
+} \ No newline at end of file
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
new file mode 100644
index 00000000..fd207157
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/arm.java
@@ -0,0 +1,385 @@
+package com.seaofnodes.simple.node.cpus.arm;
+
+import com.seaofnodes.simple.Utils;
+import com.seaofnodes.simple.codegen.LRG;
+import com.seaofnodes.simple.codegen.Machine;
+import com.seaofnodes.simple.codegen.RegMask;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.type.*;
+
+public class arm extends Machine {
+
+ // X86-64 V2. Includes e.g. SSE4.2 and POPCNT.
+ @Override public String name() { return "arm"; }
+
+ // GPR(S)
+ static final int X0 = 0, X1 = 1, X2 = 2, X3 = 3, X4 = 4, X5 = 5, X6 = 6, X7 = 7;
+ static final int X8 = 8, X9 = 9, X10 = 10, X11 = 11, X12 = 12, X13 = 13, X14 = 14, X15 = 15;
+ static final int X16 = 16, X17 = 17, X18 = 18, X19 = 19, X20 = 20, X21 = 21, X22 = 22, X23 = 23;
+ static final int X24 = 24, X25 = 25, X26 = 26, X27 = 27, X28 = 28, X29 = 29, X30 = 30, RSP = 31;
+
+ // Floating point registers
+ static final int D0 = 32, D1 = 33, D2 = 34, D3 = 35, D4 = 36, D5 = 37, D6 = 38, D7 = 39;
+ static final int D8 = 40, D9 = 41, D10 = 42, D11 = 43, D12 = 44, D13 = 45, D14 = 46, D15 = 47;
+ static final int D16 = 48, D17 = 49, D18 = 50, D19 = 51, D20 = 52, D21 = 53, D22 = 54, D23 = 55;
+ 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 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 reg( int reg ) {
+ return reg < REGS.length ? REGS[reg] : "[rsp+"+(reg-REGS.length)*4+"]";
+ }
+
+ // 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, -1L);
+
+ static final RegMask FLAGS_MASK = new RegMask(FLAGS);
+ // x30 (LR): Procedure link register, used to return from subroutines.
+ static final RegMask RPC_MASK = new RegMask(1L << X30);
+
+ // 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);
+
+ // 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,
+ };
+
+ static RegMask callInMask( TypeFunPtr tfp, int idx ) {
+ if( idx==0 ) return RPC_MASK;
+ if( idx==1 ) return null;
+ // Count floats in signature up to index
+ int fcnt=0;
+ for( int i=2; i<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
+ }
+
+ 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 RegMask CALLER_SAVE_MASK;
+ static {
+ long caller = ~CALLEE_SAVE;
+ caller &= ~(1L<<RSP);
+ CALLER_SAVE_MASK = new RegMask(caller,1L<<(FLAGS-64));
+ }
+ static RegMask armCallerSave() { return CALLER_SAVE_MASK; }
+ @Override public RegMask callerSave() { return armCallerSave(); }
+
+ static final RegMask CALLEE_SAVE_MASK = new RegMask(CALLEE_SAVE);
+ static RegMask armCalleeSave() { return CALLEE_SAVE_MASK; }
+ @Override public RegMask calleeSave() { return armCalleeSave(); }
+
+
+ // Return single int/ptr register. Used by CallEnd output and Return input.
+ static final RegMask[] RET_MASKS;
+ static {
+ int nSaves = CALLEE_SAVE_MASK.size();
+ RET_MASKS = new RegMask[4 + nSaves];
+ RET_MASKS[0] = null;
+ RET_MASKS[1] = null; // Memory
+ RET_MASKS[2] = null; // Varies, either XMM0 or RAX
+ RET_MASKS[3] = RMASK; // Expected R30 but could be any register
+ short reg = CALLEE_SAVE_MASK.firstReg();
+ for( int i=0; i<nSaves; i++ ) {
+ RET_MASKS[i+4] = new RegMask(reg);
+ reg = CALLEE_SAVE_MASK.nextReg(reg);
+ }
+ }
+ static RegMask retMask( TypeFunPtr tfp, int i ) {
+ return i==2
+ ? (tfp.ret() instanceof TypeFloat ? D0_MASK : X0_MASK)
+ : RET_MASKS[i];
+ }
+
+ // 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() {
+ throw Utils.TODO();
+ }
+
+ // Break an infinite loop
+ @Override public IfNode never( CFGNode ctrl ) {
+ throw Utils.TODO();
+ }
+
+ // Instruction selection
+ @Override public Node instSelect( Node n ) {
+ return switch( n ) {
+ case AddFNode addf -> addf(addf);
+ case AddNode add -> add(add);
+ case AndNode and -> and(and);
+ case BoolNode bool -> cmp(bool);
+ case CallNode call -> call(call);
+ case CastNode cast -> new CastNode(cast);
+ case CallEndNode cend -> new CallEndARM(cend);
+ case CProjNode c -> new CProjNode(c);
+ case ConstantNode con -> con(con);
+ case DivFNode divf -> new DivFARM(divf);
+ case DivNode div -> new DivARM(div);
+ case FunNode fun -> new FunARM(fun);
+ case IfNode iff -> jmp(iff);
+ case LoadNode ld -> ld(ld);
+ case MemMergeNode mem -> new MemMergeNode(mem);
+ case MulFNode mulf -> new MulFARM(mulf);
+ case MulNode mul -> mul(mul);
+ case NewNode nnn -> new NewARM(nnn);
+ case OrNode or -> or(or);
+ case ParmNode parm -> new ParmARM(parm);
+ case PhiNode phi -> new PhiNode(phi);
+ case ProjNode prj -> prj(prj);
+ case ReadOnlyNode read -> new ReadOnlyNode(read);
+ case ReturnNode ret -> new RetARM(ret,ret.fun());
+ case SarNode sar -> sar(sar);
+ case ShlNode shl -> shl(shl);
+ case ShrNode shr -> shr(shr);
+ case StartNode start -> new StartNode(start);
+ case StopNode stop -> new StopNode(stop);
+ case StoreNode st -> st(st);
+ case SubFNode subf -> new SubFARM(subf);
+ case SubNode sub -> sub(sub);
+ case ToFloatNode tfn -> i2f8(tfn);
+ case XorNode xor -> xor(xor);
+
+ case LoopNode loop -> new LoopNode(loop);
+ case RegionNode region-> new RegionNode(region);
+ default -> throw Utils.TODO();
+ };
+ }
+
+ private Node mul(MulNode mul) {
+ Node rhs = mul.in(2);
+ if( rhs instanceof ConstantNode off && off._con instanceof TypeInteger toff ) {
+ return new MulIARM(mul, toff);
+ }
+ return new MulARM(mul);
+ }
+ private Node cmp(BoolNode bool){
+ Node cmp = _cmp(bool);
+ return new SetARM(cmp, bool.op());
+ }
+
+ private Node i2f8(ToFloatNode tfn) {
+ assert tfn.in(1)._type instanceof TypeInteger ti;
+ return new I2F8ARM(tfn);
+ }
+
+ private Node ld(LoadNode ld) {
+ return new LoadARM(address(ld), ld.ptr(), idx, off);
+ }
+
+ private Node jmp(IfNode iff) {
+ // If/Bool combos will match to a Cmp/Set which sets flags.
+ // Most general arith ops will also set flags, which the Jmp needs directly.
+ // Loads do not set the flags, and will need an explicit TEST
+ if( !(iff.in(1) instanceof BoolNode) )
+ iff.setDef(1,new BoolNode.EQ(iff.in(1),new ConstantNode(TypeInteger.ZERO)));
+ return new BranchARM(iff, ((BoolNode)iff.in(1)).op());
+ }
+
+ private Node _cmp(BoolNode bool) {
+ if( bool instanceof BoolNode.EQF ||
+ bool instanceof BoolNode.LTF ||
+ bool instanceof BoolNode.LEF )
+ return new CmpFARM(bool);
+
+
+ return bool.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti
+ ? new CmpIARM(bool, ti)
+ : new CmpARM(bool);
+ }
+
+
+ private Node addf(AddFNode addf) {
+ return new AddFARM(addf);
+ }
+
+ private Node add(AddNode add) {
+ Node rhs = add.in(2);
+ if( rhs instanceof ConstantNode off && off._con instanceof TypeInteger toff ) {
+ return new AddIARM(add, toff);
+ }
+ return new AddARM(add);
+ }
+
+ private Node sub(SubNode sub) {
+ return sub.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti
+ ? new AddIARM(sub, TypeInteger.constant(-ti.value()))
+ : new SubARM(sub);
+ }
+
+ private Node and(AndNode and) {
+ if( and.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti )
+ return new AndIARM(and, ti);
+ return new AndARM(and);
+ }
+
+ private Node or(OrNode or) {
+ if(or.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti)
+ return new OrIARM(or, ti);
+
+ return new OrARM(or);
+ }
+
+ private Node xor(XorNode xor) {
+ if(xor.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti)
+ return new XorIARM(xor, ti);
+ return new XorARM(xor);
+ }
+
+ private Node con(ConstantNode con) {
+ if( !con._con.isConstant() ) return new ConstantNode( con ); // Default unknown caller inputs
+ return switch( con._con ) {
+ case TypeInteger ti -> new IntARM(con);
+ case TypeFloat tf -> new FloatARM(con);
+ case TypeFunPtr tfp -> new TFPARM(con);
+ case TypeMemPtr tmp -> new ConstantNode(con);
+ case TypeNil tn -> throw Utils.TODO();
+ // TOP, BOTTOM, XCtrl, Ctrl, etc. Never any executable code.
+ case Type t -> new ConstantNode(con);
+ };
+ }
+
+ private Node sar(SarNode sar){
+ if( sar.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti)
+ return new AsrIARM(sar, ti);
+ return new AsrARM(sar);
+ }
+
+ private Node shl(ShlNode shl) {
+ if( shl.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti)
+ return new LslIARM(shl, ti);
+ return new LslARM(shl);
+
+ }
+
+ private Node shr(ShrNode shr) {
+ if( shr.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti)
+ return new LsrIARM(shr, ti);
+ return new LsrARM(shr);
+ }
+
+ private Node call(CallNode call){
+ if(call.fptr() instanceof ConstantNode con && con._con instanceof TypeFunPtr tfp)
+ return new CallARM(call, tfp);
+ return new CallRRARM(call);
+ }
+
+ private Node prj(ProjNode prj) {
+ return new ProjARM(prj);
+ }
+
+
+ private static int off;
+ private static Node idx;
+ private Node st(StoreNode st) {
+ int imm=0;
+ Node xval = st.val();
+ // e.g store this s.cs[0] = 67; // C
+ if( xval instanceof ConstantNode con && con._con instanceof TypeInteger ti ) {
+ xval = null;
+ imm = (int)ti.value();
+ assert imm == ti.value(); // In 32-bit range
+ }
+ return new StoreARM(address(st),st.ptr(),idx,off,imm,xval);
+ }
+
+ // Gather addressing mode bits prior to constructing. This is a builder
+ // pattern, but saving the bits in a *local* *global* here to keep mess
+ // contained.
+ private <N 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 ) {
+ off = (int)ti.value();
+ assert off == ti.value(); // In 32-bit range
+ } else {
+ idx = mop.off();
+ }
+ return mop;
+ }
+
+
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/i2f8.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/i2f8.java
new file mode 100644
index 00000000..e0792669
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/i2f8.java
@@ -0,0 +1,4 @@
+package com.seaofnodes.simple.node.cpus.arm;
+
+public class i2f8 {
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AddFRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AddFRISC.java
index bc959977..2417aeb6 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AddFRISC.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AddFRISC.java
@@ -1,6 +1,8 @@
package com.seaofnodes.simple.node.cpus.riscv;
import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.RegMask;
import com.seaofnodes.simple.node.*;
import com.seaofnodes.simple.type.TypeInteger;
import java.io.ByteArrayOutputStream;
@@ -12,8 +14,6 @@ public class AddFRISC extends MachConcreteNode implements MachNode{
@Override public RegMask regmap(int i) { assert i==1 || i==2; return riscv.FMASK; }
// Register mask allowed as a result. 0 for no register.
@Override public RegMask outregmap() { return riscv.FMASK; }
- // Output is same register as input#1
- @Override public int twoAddress() { return 1; }
// Encoding is appended into the byte array; size is returned
@Override public int encoding(ByteArrayOutputStream bytes) {
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AddIRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AddIRISC.java
index af4bf08d..e92a7b08 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AddIRISC.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AddIRISC.java
@@ -1,39 +1,11 @@
package com.seaofnodes.simple.node.cpus.riscv;
-import com.seaofnodes.simple.*;
-import com.seaofnodes.simple.node.*;
-import com.seaofnodes.simple.type.TypeInteger;
-import java.io.ByteArrayOutputStream;
-
-public class AddIRISC extends MachConcreteNode implements MachNode {
- final TypeInteger _ti;
- AddIRISC( Node add, TypeInteger ti ) {
- super(add);
- _inputs.pop();
- _ti = ti;
- }
-
- // Register mask allowed on input i.
- @Override public RegMask regmap(int i) {
- // assert i== i;
- return riscv.RMASK; }
- // Register mask allowed as a result. 0 for no register.
- @Override public RegMask outregmap() { return riscv.RMASK; }
- // Output is same register as input#1
- @Override public int twoAddress() { return 0; }
-
- // Encoding is appended into the byte array; size is returned
- @Override public int encoding(ByteArrayOutputStream bytes) {
- throw Utils.TODO();
- }
-
- // General form: "addi rd = rs1 + imm"
- @Override public void asm(CodeGen code, SB sb) {
- sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" + #");
- _ti.print(sb);
- }
-
- @Override public String op() {
- return _ti.value() == 1 ? "inc" : (_ti.value() == -1 ? "dec" : "addi");
- }
+import com.seaofnodes.simple.Utils;
+import com.seaofnodes.simple.node.Node;
+
+public class AddIRISC extends ImmRISC {
+ AddIRISC( Node add, int imm ) { super(add,imm); }
+ @Override int opcode() { throw Utils.TODO(); }
+ @Override public String glabel() { return "+"; }
+ @Override public String op() { return "addi"; }
}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AddRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AddRISC.java
index 482b7f11..344c217d 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AddRISC.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AddRISC.java
@@ -1,25 +1,20 @@
package com.seaofnodes.simple.node.cpus.riscv;
-
import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.RegMask;
import com.seaofnodes.simple.node.*;
import com.seaofnodes.simple.type.TypeInteger;
import java.io.ByteArrayOutputStream;
-
public class AddRISC extends MachConcreteNode implements MachNode {
-
AddRISC( Node add) {super(add); }
+ @Override public String op() { return "add"; }
// Register mask allowed on input i.
- @Override public RegMask regmap(int i) {
- assert i== 1 || i == 2 || i == 3;
- return riscv.RMASK;
- }
+ @Override public RegMask regmap(int i) { return riscv.RMASK; }
// Register mask allowed as a result. 0 for no register.
- @Override public RegMask outregmap() { return riscv.RMASK; }
- // Output is same register as input#1
- @Override public int twoAddress() { return 0; }
+ @Override public RegMask outregmap() { return riscv.WMASK; }
// Encoding is appended into the byte array; size is returned
@Override public int encoding(ByteArrayOutputStream bytes) {
@@ -31,6 +26,4 @@ public class AddRISC extends MachConcreteNode implements MachNode {
sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" + ").p(code.reg(in(2)));
}
- @Override public String op() {
- return "add";}
}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AndIRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AndIRISC.java
index 29c2a686..ddbf5aa4 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AndIRISC.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AndIRISC.java
@@ -1,40 +1,11 @@
package com.seaofnodes.simple.node.cpus.riscv;
-import com.seaofnodes.simple.*;
-import com.seaofnodes.simple.node.*;
-import com.seaofnodes.simple.type.Type;
-import com.seaofnodes.simple.type.TypeInteger;
-import java.io.ByteArrayOutputStream;
-import java.util.BitSet;
-import java.lang.StringBuilder;
-
-public class AndIRISC extends MachConcreteNode implements MachNode{
- final TypeInteger _ti;
- AndIRISC(AndNode and, TypeInteger ti) {
- super(and);
- _inputs.pop();
- _ti = ti;
- }
- // Register mask allowed on input i.
- // This is the normal calling convention
- @Override public RegMask regmap(int i) { assert i==1 || i == 2; return riscv.RMASK; }
- // Register mask allowed as a result. 0 for no register.
- @Override public RegMask outregmap() { return riscv.RMASK; }
-
- // Output is same register as input#1
- @Override public int twoAddress() { return 0; }
-
- // Encoding is appended into the byte array; size is returned
- @Override public int encoding(ByteArrayOutputStream bytes) {
- throw Utils.TODO();
- }
-
- // General form
- // General form: "andi rd = rs1 & imm"
- @Override public void asm(CodeGen code, SB sb) {
- sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" & #");
- _ti.print(sb);
- }
+import com.seaofnodes.simple.Utils;
+import com.seaofnodes.simple.node.Node;
+public class AndIRISC extends ImmRISC {
+ AndIRISC( Node and, int imm) { super(and,imm); }
+ @Override int opcode() { throw Utils.TODO(); }
+ @Override public String glabel() { return "&"; }
@Override public String op() { return "andi"; }
}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AndRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AndRISC.java
index e697de36..ca00bc98 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AndRISC.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AndRISC.java
@@ -1,9 +1,9 @@
package com.seaofnodes.simple.node.cpus.riscv;
-
import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.RegMask;
import com.seaofnodes.simple.node.*;
-import com.seaofnodes.simple.node.cpus.x86_64_v2.x86_64_v2;
import com.seaofnodes.simple.type.Type;
import com.seaofnodes.simple.type.TypeInteger;
import java.io.ByteArrayOutputStream;
@@ -23,10 +23,7 @@ public class AndRISC extends MachConcreteNode implements MachNode{
}
// Register mask allowed as a result. 0 for no register.
- @Override public RegMask outregmap() { return riscv.RMASK; }
-
- // Output is same register as input#1
- @Override public int twoAddress() { return 0; }
+ @Override public RegMask outregmap() { return riscv.WMASK; }
// Encoding is appended into the byte array; size is returned
@Override public int encoding(ByteArrayOutputStream bytes) {
@@ -36,6 +33,6 @@ public class AndRISC extends MachConcreteNode implements MachNode{
// General form
// General form: #rd = rs1 & rs2
@Override public void asm(CodeGen code, SB sb) {
- sb.p(code.reg(this)).p(" = ").p(code.reg(in(1)));
+ sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" & ").p(code.reg(in(2)));
}
}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/BranchRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/BranchRISC.java
new file mode 100644
index 00000000..6a1cf7dc
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/BranchRISC.java
@@ -0,0 +1,47 @@
+package com.seaofnodes.simple.node.cpus.riscv;
+
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.RegMask;
+import com.seaofnodes.simple.node.*;
+import java.io.ByteArrayOutputStream;
+import java.util.BitSet;
+
+// Conditional branch such as: BEQ
+public class BranchRISC extends IfNode implements MachNode {
+ final String _bop;
+ // label is obtained implicitly
+ BranchRISC( IfNode iff, String bop, Node n1, Node n2 ) {
+ super(iff);
+ _bop = bop;
+ _inputs.setX(1,n1);
+ _inputs.setX(2,n2);
+ }
+
+ @Override public String label() { return op(); }
+
+ @Override public RegMask regmap(int i) { return riscv.RMASK; }
+ @Override public RegMask outregmap() { return null; }
+
+ @Override public StringBuilder _print1(StringBuilder sb, BitSet visited) {
+ in(1)._print0(sb.append("if( "),visited).append(_bop);
+ if( in(2)==null ) sb.append("0");
+ else in(2)._print0(sb,visited);
+ return sb.append(" )");
+ }
+
+ // Encoding is appended into the byte array; size is returned
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+
+ @Override public void asm(CodeGen code, SB sb) {
+ sb.p(code.reg(in(1))).p(" ").p(_bop).p(" ").p(in(2)==null ? "#0" : code.reg(in(2)));
+ }
+
+ @Override public String op() { return "b"+_bop; }
+
+ @Override public String comment() {
+ return "L"+cproj(1)._nid+", L"+cproj(0)._nid;
+ }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CallEndRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CallEndRISC.java
new file mode 100644
index 00000000..fd1ba7c3
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CallEndRISC.java
@@ -0,0 +1,35 @@
+package com.seaofnodes.simple.node.cpus.riscv;
+
+import com.seaofnodes.simple.SB;
+import com.seaofnodes.simple.Utils;
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.RegMask;
+import com.seaofnodes.simple.node.CallEndNode;
+import com.seaofnodes.simple.node.MachNode;
+import com.seaofnodes.simple.type.TypeFloat;
+import com.seaofnodes.simple.type.TypeFunPtr;
+import java.io.ByteArrayOutputStream;
+
+public class CallEndRISC extends CallEndNode implements MachNode {
+ final TypeFunPtr _tfp;
+ CallEndRISC( CallEndNode cend ) {
+ super(cend);
+ _tfp = (TypeFunPtr)(cend.call().fptr()._type);
+ }
+
+ @Override public String label() { return op(); }
+ @Override public RegMask regmap(int i) { return null; }
+ @Override public RegMask outregmap() { return null; }
+ @Override public RegMask outregmap(int idx) { return idx == 2 ? riscv.retMask(_tfp,2) : null; }
+ @Override public RegMask killmap() { return riscv.riscCallerSave(); }
+
+ // Encoding is appended into the byte array; size is returned
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+
+ @Override public void asm(CodeGen code, SB sb) { }
+
+ @Override public String op() { return "cend"; }
+
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CallRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CallRISC.java
index 7f062104..54f4fba9 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CallRISC.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CallRISC.java
@@ -1,6 +1,8 @@
package com.seaofnodes.simple.node.cpus.riscv;
import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.RegMask;
import com.seaofnodes.simple.node.*;
import com.seaofnodes.simple.type.TypeFunPtr;
import java.io.ByteArrayOutputStream;
@@ -8,6 +10,7 @@ import java.io.ByteArrayOutputStream;
public class CallRISC extends CallNode implements MachNode{
final TypeFunPtr _tfp;
final String _name;
+
CallRISC( CallNode call, TypeFunPtr tfp ) {
super(call);
_inputs.pop(); // Pop constant target
@@ -18,11 +21,12 @@ public class CallRISC extends CallNode implements MachNode{
@Override public String label() { return op(); }
@Override public RegMask regmap(int i) {
- return riscv.callInMaskInt(i); // Normal argument
+ return riscv.callInMask(_tfp,i); // Normal argument
}
- @Override public RegMask outregmap() { return riscv.RET_MASK; }
+ @Override public RegMask outregmap() { return null; }
@Override public String name() { return _name; }
+ @Override public TypeFunPtr tfp() { return _tfp; }
// Encoding is appended into the byte array; size is returned
@Override public int encoding(ByteArrayOutputStream bytes) {
@@ -30,7 +34,10 @@ public class CallRISC extends CallNode implements MachNode{
}
@Override public void asm(CodeGen code, SB sb) {
- sb.p(_name);
+ sb.p(_name).p(" ");
+ for( int i=0; i<nargs(); i++ )
+ sb.p(code.reg(arg(i))).p(" ");
+ sb.unchar(2);
}
@Override public String op() { return "call"; }
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CallRRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CallRRISC.java
index 02850582..3bae656d 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CallRRISC.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CallRRISC.java
@@ -1,8 +1,9 @@
package com.seaofnodes.simple.node.cpus.riscv;
import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.RegMask;
import com.seaofnodes.simple.node.*;
-
import java.io.ByteArrayOutputStream;
public class CallRRISC extends CallNode implements MachNode{
@@ -12,10 +13,10 @@ public class CallRRISC extends CallNode implements MachNode{
@Override public RegMask regmap(int i) {
// Todo: float or int?
return i==_inputs._len
- ? riscv.RMASK // Function call target
- : riscv.callInMaskInt(i); // Normal argument
+ ? riscv.RMASK // Function call target
+ : riscv.callInMask(tfp(),i); // Normal argument
}
- @Override public RegMask outregmap() { return riscv.RET_MASK; }
+ @Override public RegMask outregmap() { return null; }
// Encoding is appended into the byte array; size is returned
@Override public int encoding(ByteArrayOutputStream bytes) {
@@ -23,7 +24,10 @@ public class CallRRISC extends CallNode implements MachNode{
}
@Override public void asm(CodeGen code, SB sb) {
- sb.p(code.reg(fptr()));
+ sb.p(code.reg(fptr())).p(" ");
+ for( int i=0; i<nargs(); i++ )
+ sb.p(code.reg(arg(i))).p(" ");
+ sb.unchar(2);
}
@Override public String op() { return "callr"; }
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CastRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CastRISC.java
new file mode 100644
index 00000000..a0d0df90
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CastRISC.java
@@ -0,0 +1,18 @@
+package com.seaofnodes.simple.node.cpus.riscv;
+
+import com.seaofnodes.simple.SB;
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.RegMask;
+import com.seaofnodes.simple.node.CastNode;
+import com.seaofnodes.simple.node.MachNode;
+import java.io.ByteArrayOutputStream;
+
+public class CastRISC extends CastNode implements MachNode {
+ CastRISC( CastNode cast ) { super(cast); }
+ @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 int encoding(ByteArrayOutputStream bytes) { return 0; /*no encoding*/ }
+ @Override public void asm(CodeGen code, SB sb) { _t.print(sb.p(code.reg(in(1))).p(" isa ")); }
+ @Override public String op() { return label(); }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CmpIRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CmpIRISC.java
deleted file mode 100644
index 39fa3a29..00000000
--- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CmpIRISC.java
+++ /dev/null
@@ -1,44 +0,0 @@
-package com.seaofnodes.simple.node.cpus.riscv;
-
-
-import com.seaofnodes.simple.*;
-import com.seaofnodes.simple.node.*;
-import com.seaofnodes.simple.type.Type;
-import com.seaofnodes.simple.type.TypeInteger;
-import java.io.ByteArrayOutputStream;
-
-// Compare immediate. Sets flags.(RFLAGS)
-public class CmpIRISC extends MachConcreteNode implements MachNode{
- final int _imm;
- final String _bop;
- CmpIRISC( BoolNode bool, TypeInteger ti ) {
- super(bool);
- _inputs.pop(); // Toss ideal ConstantNode away, embedded into machine op
- _bop = bool.op(); // One of <,<=,==
- _imm = (int)ti.value();
- assert _imm == ti.value();
- }
- CmpIRISC( Node cmp, double ignore ) {
- super(cmp);
- _bop = "==";
- _imm = 0;
- }
-
- @Override public RegMask regmap(int i) { assert i==1; return riscv.RMASK; }
- @Override public RegMask outregmap() { return riscv.FLAGS_MASK; }
-
- // Encoding is appended into the byte array; size is returned
- @Override public int encoding(ByteArrayOutputStream bytes) {
- throw Utils.TODO();
- }
-
- // General form: "cmp rs1, 1"
- @Override public void asm(CodeGen code, SB sb) {
- String dst = code.reg(this);
- if( dst!="FLAGS" ) sb.p(dst).p(" = ");
- sb.p(code.reg(in(1)));
- if( _imm != 0 ) sb.p(", #").p(_imm);
- }
-
- @Override public String op() { return _imm==0 ? "test" : "cmp"; }
-}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/DivFRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/DivFRISC.java
index deb2628d..d360bea6 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/DivFRISC.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/DivFRISC.java
@@ -1,6 +1,8 @@
package com.seaofnodes.simple.node.cpus.riscv;
import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.RegMask;
import com.seaofnodes.simple.node.*;
import java.io.ByteArrayOutputStream;
@@ -11,8 +13,6 @@ public class DivFRISC extends MachConcreteNode implements MachNode{
@Override public RegMask regmap(int i) { assert i==1 || i==2; return riscv.FMASK; }
// Register mask allowed as a result. 0 for no register.
@Override public RegMask outregmap() { return riscv.FMASK; }
- // Output is same register as input#1
- @Override public int twoAddress() { return 1; }
// Encoding is appended into the byte array; size is returned
@Override public int encoding(ByteArrayOutputStream bytes) {
@@ -22,7 +22,7 @@ public class DivFRISC extends MachConcreteNode implements MachNode{
// 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)));
+ sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" / ").p(code.reg(in(2)));
}
@Override public String op() { return "divf"; }
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/DivRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/DivRISC.java
index 98a8406f..ee49a883 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/DivRISC.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/DivRISC.java
@@ -1,8 +1,9 @@
package com.seaofnodes.simple.node.cpus.riscv;
import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.RegMask;
import com.seaofnodes.simple.node.*;
-import com.seaofnodes.simple.node.cpus.x86_64_v2.x86_64_v2;
import com.seaofnodes.simple.type.TypeInteger;
import java.io.ByteArrayOutputStream;
@@ -12,11 +13,9 @@ public class DivRISC extends MachConcreteNode implements MachNode{
// Register mask allowed on input i.
@Override public RegMask regmap(int i) {
assert i==1 || i==2;
- return riscv.FMASK; }
+ return riscv.RMASK; }
// Register mask allowed as a result. 0 for no register.
- @Override public RegMask outregmap() { return x86_64_v2.RMASK; }
- // Output is same register as input#1
- @Override public int twoAddress() { return 1; }
+ @Override public RegMask outregmap() { return riscv.WMASK; }
// Encoding is appended into the byte array; size is returned
@Override public int encoding(ByteArrayOutputStream bytes) {
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/FltRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/FltRISC.java
index e1637eec..2832ac52 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/FltRISC.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/FltRISC.java
@@ -1,24 +1,20 @@
package com.seaofnodes.simple.node.cpus.riscv;
-
-import com.seaofnodes.simple.CodeGen;
-import com.seaofnodes.simple.RegMask;
import com.seaofnodes.simple.SB;
import com.seaofnodes.simple.Utils;
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.RegMask;
import com.seaofnodes.simple.node.ConstantNode;
import com.seaofnodes.simple.node.MachNode;
-import com.seaofnodes.simple.node.cpus.x86_64_v2.x86_64_v2;
-
import java.io.ByteArrayOutputStream;
public class FltRISC extends ConstantNode implements MachNode{
- FltRISC(ConstantNode con) {
- super(con);
- }
+ FltRISC(ConstantNode con) { super(con); }
+ @Override public String op() { return "flw"; }
// Register mask allowed on input i. 0 for no register.
@Override public RegMask regmap(int i) { return null; }
- // General int registers
- @Override public RegMask outregmap() { return riscv.FMASK; }
+ // General int registers! Must-split into the floats
+ @Override public RegMask outregmap() { return riscv.RMASK; }
// Encoding is appended into the byte array; size is returned
@Override public int encoding(ByteArrayOutputStream bytes) {
@@ -32,8 +28,4 @@ public class FltRISC extends ConstantNode implements MachNode{
@Override public void asm(CodeGen code, SB sb) {
_con.print(sb.p(code.reg(this)).p(" #"));
}
-
- @Override public String op() {
- return "flw"; // Some fancier encoding
- }
}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/FunRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/FunRISC.java
index 66e32e29..3b7d9a34 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/FunRISC.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/FunRISC.java
@@ -1,14 +1,13 @@
package com.seaofnodes.simple.node.cpus.riscv;
-import com.seaofnodes.simple.RegMask;
import com.seaofnodes.simple.Utils;
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.RegMask;
import com.seaofnodes.simple.node.FunNode;
import com.seaofnodes.simple.node.MachNode;
-import com.seaofnodes.simple.node.cpus.x86_64_v2.FunX86;
-
import java.io.ByteArrayOutputStream;
-public class FunRISC extends FunNode implements MachNode{
+public class FunRISC extends FunNode implements MachNode {
FunRISC(FunNode fun) {super(fun);}
// Register mask allowed on input i. 0 for no register.
@Override public RegMask regmap(int i) { return null; }
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
index df005e86..560d1b38 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/I2F8RISC.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/I2F8RISC.java
@@ -1,8 +1,9 @@
package com.seaofnodes.simple.node.cpus.riscv;
import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.RegMask;
import com.seaofnodes.simple.node.*;
-import com.seaofnodes.simple.node.cpus.x86_64_v2.x86_64_v2;
import com.seaofnodes.simple.type.TypeInteger;
import java.io.ByteArrayOutputStream;
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
new file mode 100644
index 00000000..02191d05
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/ImmRISC.java
@@ -0,0 +1,40 @@
+package com.seaofnodes.simple.node.cpus.riscv;
+
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.RegMask;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.type.TypeInteger;
+import java.util.BitSet;
+import java.io.ByteArrayOutputStream;
+
+public abstract class ImmRISC extends MachConcreteNode implements MachNode {
+ final int _imm;
+ ImmRISC( Node add, int imm ) {
+ super(add);
+ _inputs.pop();
+ _imm = imm;
+ }
+
+ // Register mask allowed on input i.
+ @Override public RegMask regmap(int i) { return riscv.RMASK; }
+ // Register mask allowed as a result. 0 for no register.
+ @Override public RegMask outregmap() { return riscv.WMASK; }
+
+ @Override public StringBuilder _print1(StringBuilder sb, BitSet visited) {
+ in(1)._print0(sb.append("( "), visited);
+ return sb.append(String.format(" %s #%d )",glabel(),_imm));
+ }
+
+ abstract int opcode();
+
+ // Encoding is appended into the byte array; size is returned
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+
+ // General form: "addi rd = rs1 + imm"
+ @Override public void asm(CodeGen code, SB sb) {
+ sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" ").p(glabel()).p(" #").p(_imm);
+ }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/IntRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/IntRISC.java
index bf170b22..e792b055 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/IntRISC.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/IntRISC.java
@@ -1,22 +1,23 @@
package com.seaofnodes.simple.node.cpus.riscv;
import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.RegMask;
import com.seaofnodes.simple.node.ConstantNode;
import com.seaofnodes.simple.node.MachNode;
-import com.seaofnodes.simple.type.Type;
-import com.seaofnodes.simple.type.TypeInteger;
-
import java.io.ByteArrayOutputStream;
+
// Integer constants
public class IntRISC extends ConstantNode implements MachNode {
- IntRISC(ConstantNode con) {
- super(con);
- }
-
+ IntRISC(ConstantNode con) { super(con); }
+ @Override public String op() { return "ldi"; }
// Register mask allowed on input i. 0 for no register.
@Override public RegMask regmap(int i) { return null; }
// General int registers
- @Override public RegMask outregmap() { return riscv.RMASK; }
+ @Override public RegMask outregmap() { return riscv.WMASK; }
+
+ @Override public boolean isClone() { return true; }
+ @Override public IntRISC copy() { return new IntRISC(this); }
// Encoding is appended into the byte array; size is returned
@Override public int encoding(ByteArrayOutputStream bytes) {
@@ -29,15 +30,6 @@ public class IntRISC extends ConstantNode implements MachNode {
// General form: "op\tdst=src+src"
@Override public void asm(CodeGen code, SB sb) {
String reg = code.reg(this);
- if( _con == Type.NIL || _con == TypeInteger.ZERO )
- sb.p(reg).p(",").p(reg);
- else
- _con.print(sb.p(reg).p(" #"));
- }
-
- @Override public String op() {
- if( _con == Type.NIL || _con == TypeInteger.ZERO )
- return "xor";
- return "ldi"; // Some fancier encoding
+ _con.print(sb.p(reg).p(" = #"));
}
}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/LoadRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/LoadRISC.java
index 846e313c..3fe8f44e 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/LoadRISC.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/LoadRISC.java
@@ -1,22 +1,29 @@
package com.seaofnodes.simple.node.cpus.riscv;
-
import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.RegMask;
import com.seaofnodes.simple.node.*;
import com.seaofnodes.simple.type.TypeInteger;
import java.io.ByteArrayOutputStream;
-public class LoadRISC extends MemOpRISC{
- LoadRISC(LoadNode ld) {
- super(ld, ld);
+
+// Load memory addressing on RISC
+// Support imm, reg(direct), or reg+off(indirect) addressing
+// Base = base - base pointer, offset is added to base
+// idx = null
+// off = off - offset added to base
+
+public class LoadRISC extends MemOpRISC {
+ LoadRISC(LoadNode ld, Node base, Node idx, int off) {
+ super(ld, base, idx, off, 0);
}
@Override public RegMask regmap(int i) {
- // wider mask, encode it differently, immediate handled with hard split(in RA)
- return riscv.RMASK.or(riscv.FMASK);
+ return riscv.RMASK;
}
- // Register mask allowed as a result. 0 for no register.
- @Override public RegMask outregmap() { return riscv.RMASK; }
+ // Wide mask loads both ints and floats; encoding varies.
+ @Override public RegMask outregmap() { return riscv.MEM_MASK; }
// Encoding is appended into the byte array; size is returned
@@ -25,10 +32,9 @@ public class LoadRISC extends MemOpRISC{
}
@Override public void asm(CodeGen code, SB sb) {
+ sb.p(code.reg(this)).p(",");
+ asm_address(code,sb);
}
- @Override public String op() {
- return "lw" +_sz;
- }
+ @Override public String op() { return "ld" +_sz; }
}
-
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/MemOpRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/MemOpRISC.java
index 092143d6..bcd7924e 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/MemOpRISC.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/MemOpRISC.java
@@ -1,25 +1,43 @@
package com.seaofnodes.simple.node.cpus.riscv;
import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.RegMask;
import com.seaofnodes.simple.node.*;
-import com.seaofnodes.simple.node.cpus.x86_64_v2.x86_64_v2;
import com.seaofnodes.simple.type.*;
import java.io.ByteArrayOutputStream;
import java.lang.StringBuilder;
import java.util.BitSet;
-public class MemOpRISC extends MemOpNode implements MachNode{
+public class MemOpRISC extends MemOpNode implements MachNode {
+ final int _off; // Limit 32 bits
+ final int _imm; // Limit 32 bits
final char _sz = (char)('0'+(1<<_declaredType.log_size()));
- MemOpRISC(Node op, MemOpNode mop) {
- super(op, mop);
+ MemOpRISC( MemOpNode mop, Node base, Node idx, int off, int imm) {
+ super(mop,mop);
+ assert base._type instanceof TypeMemPtr && !(base instanceof AddNode);
+ assert ptr() == base;
+ _inputs.setX(3,idx);
+ _off = off;
+ _imm = imm;
}
+ // Store-based flavors have a value edge
+ MemOpRISC( MemOpNode mop, Node base, Node idx, int off, int imm, Node val ) {
+ this(mop,base,idx,off,imm);
+ _inputs.setX(4,val);
+ }
+
+ Node idx() { return in(3); }
+ Node val() { return in(4); } // Only for stores, including op-to-memory
+
@Override 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) {
throw Utils.TODO();
@@ -31,6 +49,9 @@ public class MemOpRISC extends MemOpNode implements MachNode{
SB asm_address(CodeGen code, SB sb) {
- return sb;
+ sb.p("[").p(code.reg(ptr())).p("+");
+ if( idx() != null ) sb.p(code.reg(idx()));
+ else sb.p(_off);
+ return sb.p("]");
}
}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/MulFRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/MulFRISC.java
index 43197b50..8a066ab1 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/MulFRISC.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/MulFRISC.java
@@ -1,6 +1,8 @@
package com.seaofnodes.simple.node.cpus.riscv;
import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.RegMask;
import com.seaofnodes.simple.node.*;
import java.io.ByteArrayOutputStream;
@@ -11,8 +13,6 @@ public class MulFRISC extends MachConcreteNode implements MachNode{
@Override public RegMask regmap(int i) { assert i==1 || i==2; return riscv.FMASK; }
// Register mask allowed as a result. 0 for no register.
@Override public RegMask outregmap() { return riscv.FMASK; }
- // Output is same register as input#1
- @Override public int twoAddress() { return 1; }
// Encoding is appended into the byte array; size is returned
@Override public int encoding(ByteArrayOutputStream bytes) {
@@ -22,7 +22,7 @@ public class MulFRISC extends MachConcreteNode implements MachNode{
// Default on double precision for now(64 bits)
// General form: "fmul.d rd = src1 * src2
@Override public void asm(CodeGen code, SB sb) {
- sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" + ").p(code.reg(in(2)));
+ sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" * ").p(code.reg(in(2)));
}
@Override public String op() { return "mulf"; }
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/MulRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/MulRISC.java
index 8713486e..0e2d35a3 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/MulRISC.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/MulRISC.java
@@ -1,8 +1,9 @@
package com.seaofnodes.simple.node.cpus.riscv;
import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.RegMask;
import com.seaofnodes.simple.node.*;
-import com.seaofnodes.simple.node.cpus.x86_64_v2.x86_64_v2;
import com.seaofnodes.simple.type.TypeInteger;
import java.io.ByteArrayOutputStream;
@@ -14,9 +15,7 @@ public class MulRISC extends MachConcreteNode implements MachNode{
// Register mask allowed on input i.
@Override public RegMask regmap(int i) { assert i==1 || i==2; return riscv.RMASK; }
// Register mask allowed as a result. 0 for no register.
- @Override public RegMask outregmap() { return riscv.RMASK; }
- // Output is same register as input#1
- @Override public int twoAddress() { return 1; }
+ @Override public RegMask outregmap() { return riscv.WMASK; }
// Encoding is appended into the byte array; size is returned
@Override public int encoding(ByteArrayOutputStream bytes) {
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/NewRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/NewRISC.java
index f050715d..53ad0af2 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/NewRISC.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/NewRISC.java
@@ -1,34 +1,19 @@
package com.seaofnodes.simple.node.cpus.riscv;
import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.RegMask;
import com.seaofnodes.simple.node.*;
-import com.seaofnodes.simple.node.cpus.x86_64_v2.x86_64_v2;
-
import java.io.ByteArrayOutputStream;
-
-public class NewRISC extends NewNode implements MachNode{
-
+public class NewRISC extends NewNode implements MachNode {
// A pre-zeroed chunk of memory.
- NewRISC( NewNode nnn ) { super(nnn); }
-
- // Register mask allowed on input i, the size
- @Override public RegMask regmap(int i) {
- // Size
- if( i==1 ) return riscv.RET_MASK;
- // All the memory alias edges
- return null;
- }
-
- @Override public RegMask outregmap() { throw Utils.TODO(); }
-
- // Register mask allowed as a result. Pointer result in standard calling
- // convention.
- @Override public RegMask outregmap(int i) {
- if( i == 1 ) return riscv.RET_MASK;
- // All the memory aliases edges
- return null;
- }
+ NewRISC( NewNode nnn ) { super(nnn); }
+ // Size and pointer result in standard calling convention; null for all the
+ // memory aliases edges
+ @Override public RegMask regmap(int i) { return i == 1 ? riscv.A0_MASK : null; }
+ @Override public RegMask outregmap(int i) { return i == 1 ? riscv.A0_MASK : null; }
+ @Override public RegMask outregmap() { return null; }
// Encoding is appended into the byte array; size is returned
@Override public int encoding(ByteArrayOutputStream bytes) {
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/NotRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/NotRISC.java
new file mode 100644
index 00000000..6d30cd1c
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/NotRISC.java
@@ -0,0 +1,25 @@
+package com.seaofnodes.simple.node.cpus.riscv;
+
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.LRG;
+import com.seaofnodes.simple.codegen.RegMask;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.node.MachConcreteNode;
+import com.seaofnodes.simple.type.TypeInteger;
+import java.io.ByteArrayOutputStream;
+
+public class NotRISC extends MachConcreteNode implements MachNode {
+ NotRISC(NotNode not) {super(not);}
+ @Override public RegMask regmap(int i) { return riscv.RMASK; }
+ @Override public RegMask outregmap() { return riscv.RMASK; }
+
+ @Override public int twoAddress( ) { return 0; }
+
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+
+ @Override public void asm(CodeGen code, SB sb) { sb.p(code.reg(this)); }
+ @Override public String op() { return "not"; }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/OrIRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/OrIRISC.java
index f3f79ed1..f41d7162 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/OrIRISC.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/OrIRISC.java
@@ -1,43 +1,11 @@
package com.seaofnodes.simple.node.cpus.riscv;
-import com.seaofnodes.simple.CodeGen;
-import com.seaofnodes.simple.RegMask;
-import com.seaofnodes.simple.SB;
import com.seaofnodes.simple.Utils;
-import com.seaofnodes.simple.node.MachConcreteNode;
-import com.seaofnodes.simple.node.MachNode;
import com.seaofnodes.simple.node.Node;
-import com.seaofnodes.simple.node.cpus.x86_64_v2.x86_64_v2;
-import com.seaofnodes.simple.type.Type;
-import com.seaofnodes.simple.type.TypeInteger;
-
-import java.io.ByteArrayOutputStream;
-
-public class OrIRISC extends MachConcreteNode implements MachNode{
- final TypeInteger _ti;
- OrIRISC(Node or, TypeInteger ti) {super(or); _inputs.pop(); _ti = ti;}
- // Register mask allowed on input i.
- // This is the normal calling convention
- @Override public RegMask regmap(int i) {
- assert i==1 || i==2;
- return riscv.RMASK;
- }
- // Register mask allowed as a result. 0 for no register.
- @Override public RegMask outregmap() { return riscv.RMASK; }
- // Output is same register as input#1
- @Override public int twoAddress() { return 0;}
-
- // Encoding is appended into the byte array; size is returned
- @Override public int encoding(ByteArrayOutputStream bytes) {
- throw Utils.TODO();
- }
-
- // General form
- // General form: "rd = rs1 | imm"
- @Override public void asm(CodeGen code, SB sb) {
- sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" | #");
- _ti.print(sb);
- }
+public class OrIRISC extends ImmRISC {
+ OrIRISC( Node and, int imm) { super(and,imm); }
+ @Override int opcode() { throw Utils.TODO(); }
+ @Override public String glabel() { return "|"; }
@Override public String op() { return "ori"; }
}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/OrRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/OrRISC.java
index 8f1e9305..7f808f90 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/OrRISC.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/OrRISC.java
@@ -1,14 +1,12 @@
package com.seaofnodes.simple.node.cpus.riscv;
-import com.seaofnodes.simple.node.MachConcreteNode;
-import com.seaofnodes.simple.node.MachNode;
-
-import com.seaofnodes.simple.CodeGen;
-import com.seaofnodes.simple.RegMask;
import com.seaofnodes.simple.SB;
import com.seaofnodes.simple.Utils;
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.RegMask;
+import com.seaofnodes.simple.node.MachConcreteNode;
+import com.seaofnodes.simple.node.MachNode;
import com.seaofnodes.simple.node.Node;
-import com.seaofnodes.simple.node.cpus.x86_64_v2.x86_64_v2;
import java.io.ByteArrayOutputStream;
public class OrRISC extends MachConcreteNode implements MachNode {
@@ -24,10 +22,7 @@ public class OrRISC extends MachConcreteNode implements MachNode {
}
// Register mask allowed as a result. 0 for no register.
- @Override public RegMask outregmap() { return riscv.RMASK; }
-
- // Output is same register as input#1
- @Override public int twoAddress() { return 0; }
+ @Override public RegMask outregmap() { return riscv.WMASK; }
// Encoding is appended into the byte array; size is returned
@Override public int encoding(ByteArrayOutputStream bytes) {
@@ -37,7 +32,8 @@ public class OrRISC extends MachConcreteNode implements MachNode {
// General form
// General form: # rd = rs1 | rs2
@Override public void asm(CodeGen code, SB sb) {
- sb.p(code.reg(this)).p(" = ").p(code.reg(in(1)));
+ sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" | ").p(code.reg(in(2)));
}
+ @Override public String op() { return "or"; }
}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/ParmRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/ParmRISC.java
index 2e20f96f..75674f37 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/ParmRISC.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/ParmRISC.java
@@ -1,22 +1,23 @@
package com.seaofnodes.simple.node.cpus.riscv;
-import com.seaofnodes.simple.RegMask;
import com.seaofnodes.simple.SB;
import com.seaofnodes.simple.Utils;
-import com.seaofnodes.simple.node.ParmNode;
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.RegMask;
import com.seaofnodes.simple.node.MachNode;
+import com.seaofnodes.simple.node.ParmNode;
import java.io.ByteArrayOutputStream;
-public class ParmRISC extends ParmNode implements MachNode{
+public class ParmRISC extends ParmNode implements MachNode {
final RegMask _rmask;
ParmRISC(ParmNode parm) {
super(parm);
// Assume int
- _rmask = riscv.callInMaskInt(_idx);
+ _rmask = riscv.callInMask(fun().sig(),_idx);
}
// Register mask allowed on input i. 0 for no register.
- @Override public RegMask regmap(int i) { return null; }
+ @Override public RegMask regmap(int i) { return _rmask; }
// Register mask allowed as a result. Calling convention register
@Override public RegMask outregmap() { return _rmask; }
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/ProjRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/ProjRISC.java
index a1312c4c..42d5dc9b 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/ProjRISC.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/ProjRISC.java
@@ -1,20 +1,22 @@
package com.seaofnodes.simple.node.cpus.riscv;
-
-import com.seaofnodes.simple.RegMask;
import com.seaofnodes.simple.SB;
import com.seaofnodes.simple.Utils;
-import com.seaofnodes.simple.node.ProjNode;
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.RegMask;
import com.seaofnodes.simple.node.MachNode;
+import com.seaofnodes.simple.node.ProjNode;
import java.io.ByteArrayOutputStream;
-public class ProjRISC extends ProjNode implements MachNode{
- ProjRISC(ProjNode p) {super(p);}
+public class ProjRISC extends ProjNode implements MachNode {
+ ProjRISC(ProjNode p) { super(p); }
// Register mask allowed on input i. 0 for no register.
@Override public RegMask regmap(int i) { return null; }
- // Register mask allowed as a result. 0 for no register.
- @Override public RegMask outregmap() { return null; }
+ // Register mask allowed as a result.
+ @Override public RegMask outregmap() {
+ return ((MachNode)in(0)).outregmap(_idx);
+ }
// Encoding is appended into the byte array; size is returned
@Override public int encoding(ByteArrayOutputStream bytes) {
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/RetRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/RetRISC.java
index c92e6989..c9fa7fac 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/RetRISC.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/RetRISC.java
@@ -1,26 +1,21 @@
package com.seaofnodes.simple.node.cpus.riscv;
import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.RegMask;
import com.seaofnodes.simple.node.FunNode;
import com.seaofnodes.simple.node.ReturnNode;
import com.seaofnodes.simple.node.MachNode;
+import com.seaofnodes.simple.type.TypeFloat;
import java.io.ByteArrayOutputStream;
public class RetRISC extends ReturnNode implements MachNode{
RetRISC(ReturnNode ret, FunNode fun) { super(ret, fun); fun.setRet(this); }
- // Todo: postSelect
+
// Register mask allowed on input i.
// This is the normal calling convention
- @Override public RegMask regmap(int i) {
- return switch( i ) {
- case 0 -> null;
- case 1 -> null;
- case 2 -> riscv.RET_MASK;
- case 3 -> null; // RPC is always on stack
- default -> throw Utils.TODO();
- };
- }
+ @Override public RegMask regmap(int i) { return riscv.retMask(_fun.sig(),i); }
// Register mask allowed as a result. 0 for no register.
@Override public RegMask outregmap() { return null; }
@@ -29,12 +24,17 @@ public class RetRISC extends ReturnNode implements MachNode{
throw Utils.TODO();
}
- // Human-readable form appended to the SB. Things like the encoding,
- // indentation, leading address or block labels not printed here.
- // Just something like "ld4\tR17=[R18+12] // Load array base".
- // General form: "op\tdst=src+src"
@Override public void asm(CodeGen code, SB sb) {
- sb.p(code.reg(in(2)));
+ // Post code-gen, just print the "ret"
+ if( code._phase.ordinal() <= CodeGen.Phase.RegAlloc.ordinal() )
+ // Prints return reg (either A0 or FA0), RPC (always R1) and then
+ // the callee-save registers.
+ for( int i=2; i<nIns(); i++ )
+ sb.p(code.reg(in(i))).p(" ");
+
+ // Post-allocation, if we did not get the expected return register, print what we got
+ else if( code._regAlloc.regnum(in(3)) != riscv.RPC )
+ sb.p("[").p(code.reg(in(3))).p("]");
}
// Correct Nodes outside the normal edges
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
new file mode 100644
index 00000000..3f9fc192
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SetFRISC.java
@@ -0,0 +1,33 @@
+package com.seaofnodes.simple.node.cpus.riscv;
+
+import com.seaofnodes.simple.SB;
+import com.seaofnodes.simple.Utils;
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.RegMask;
+import com.seaofnodes.simple.node.BoolNode;
+import com.seaofnodes.simple.node.MachConcreteNode;
+import com.seaofnodes.simple.node.MachNode;
+import java.io.ByteArrayOutputStream;
+
+// corresponds to slt,sltu,slti,sltiu, seqz
+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 RegMask regmap(int i) { return riscv.FMASK; }
+ @Override public RegMask outregmap() { return riscv.WMASK; }
+
+ // Encoding is appended into the byte array; size is returned
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+
+ @Override public void asm(CodeGen code, SB sb) {
+ sb.p(code.reg(in(1))).p(" ").p(_bop).p(" ").p(code.reg(in(2)));
+ }
+
+ @Override public String op() { return "set"+_bop; }
+}
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
new file mode 100644
index 00000000..3c0f76ae
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SetIRISC.java
@@ -0,0 +1,17 @@
+package com.seaofnodes.simple.node.cpus.riscv;
+
+import com.seaofnodes.simple.Utils;
+import com.seaofnodes.simple.node.BoolNode;
+
+// corresponds to slt,sltu,slti,sltiu, seqz
+public class SetIRISC extends ImmRISC {
+ final String _bop; // One of <,<=,==
+ SetIRISC( BoolNode bool, int imm ) {
+ super(bool,imm);
+ assert !bool.isFloat();
+ _bop = bool.op();
+ }
+ @Override int opcode() { throw Utils.TODO(); }
+ @Override public String glabel() { return _bop; }
+ @Override public String op() { return "set"+_bop; }
+}
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
index 1d8c9de8..fb7650c7 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SetRISC.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SetRISC.java
@@ -1,24 +1,24 @@
package com.seaofnodes.simple.node.cpus.riscv;
-import com.seaofnodes.simple.*;
-import com.seaofnodes.simple.node.*;
-import com.seaofnodes.simple.type.Type;
-import com.seaofnodes.simple.type.TypeInteger;
+import com.seaofnodes.simple.SB;
+import com.seaofnodes.simple.Utils;
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.RegMask;
+import com.seaofnodes.simple.node.BoolNode;
+import com.seaofnodes.simple.node.MachConcreteNode;
+import com.seaofnodes.simple.node.MachNode;
import java.io.ByteArrayOutputStream;
// corresponds to slt,sltu,slti,sltiu, seqz
-// RISCV doesn't have rflags.
-public class SetRISC extends MachConcreteNode implements MachNode{
+public class SetRISC extends MachConcreteNode implements MachNode {
final String _bop; // One of <,<=,==
- SetRISC( Node cmp, String bop ) {
- super(cmp);
- _inputs.setLen(1); // Pop the cmp inputs
- // Replace with the matched cmp
- _inputs.push(cmp);
- _bop = bop;
+ SetRISC( BoolNode bool ) {
+ super(bool);
+ assert !bool.isFloat();
+ _bop = bool.op();
}
- @Override public RegMask regmap(int i) { assert i==1; return riscv.FLAGS_MASK; }
- @Override public RegMask outregmap() { return riscv.RMASK; }
+ @Override public RegMask regmap(int i) { throw Utils.TODO(); }
+ @Override public RegMask outregmap() { return riscv.WMASK; }
// Encoding is appended into the byte array; size is returned
@Override public int encoding(ByteArrayOutputStream bytes) {
@@ -26,9 +26,7 @@ public class SetRISC extends MachConcreteNode implements MachNode{
}
@Override public void asm(CodeGen code, SB sb) {
- sb.p(code.reg(this));
- String src = code.reg(in(1));
- if( src!="FLAGS" ) sb.p(" = ").p(src);
+ sb.p(code.reg(in(1))).p(" ").p(_bop).p(" ").p(code.reg(in(2)));
}
@Override public String op() { return "set"+_bop; }
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SllIRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SllIRISC.java
index 8927c086..b5202908 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SllIRISC.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SllIRISC.java
@@ -1,40 +1,11 @@
package com.seaofnodes.simple.node.cpus.riscv;
-import com.seaofnodes.simple.*;
-import com.seaofnodes.simple.node.*;
-import com.seaofnodes.simple.node.cpus.x86_64_v2.x86_64_v2;
-import com.seaofnodes.simple.type.Type;
-import com.seaofnodes.simple.type.TypeInteger;
-import java.io.ByteArrayOutputStream;
-import java.util.BitSet;
-
-public class SllIRISC extends MachConcreteNode implements MachNode{
- final TypeInteger _ti;
- SllIRISC(Node sll, TypeInteger ti) {super(sll); _inputs.pop(); _ti = ti;}
-
- // Register mask allowed on input i.
- // This is the normal calling convention
- @Override public RegMask regmap(int i) {
- //assert i==1;
- return riscv.RMASK; }
-
- // Register mask allowed as a result. 0 for no register.
- @Override public RegMask outregmap() { return riscv.RMASK; }
-
- // Output is same register as input#1
- @Override public int twoAddress() { return 0; }
-
- // Encoding is appended into the byte array; size is returned
- @Override public int encoding(ByteArrayOutputStream bytes) {
- throw Utils.TODO();
- }
-
- // General form
- // General form: "slli rd, rs1, imm"
- @Override public void asm(CodeGen code, SB sb) {
- sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" << #");
- _ti.print(sb);
- }
+import com.seaofnodes.simple.Utils;
+import com.seaofnodes.simple.node.Node;
+public class SllIRISC extends ImmRISC {
+ SllIRISC( Node and, int imm) { super(and,imm); }
+ @Override int opcode() { throw Utils.TODO(); }
+ @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
index 69538433..447f604a 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SllRISC.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SllRISC.java
@@ -1,6 +1,8 @@
package com.seaofnodes.simple.node.cpus.riscv;
import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.RegMask;
import com.seaofnodes.simple.node.*;
import com.seaofnodes.simple.type.Type;
import com.seaofnodes.simple.type.TypeInteger;
@@ -16,10 +18,7 @@ public class SllRISC extends MachConcreteNode implements MachNode{
//assert i==1;
return riscv.RMASK; }
// Register mask allowed as a result. 0 for no register.
- @Override public RegMask outregmap() { return riscv.RMASK; }
-
- // Output is same register as input#1
- @Override public int twoAddress() { return 1; }
+ @Override public RegMask outregmap() { return riscv.WMASK; }
// Encoding is appended into the byte array; size is returned
@Override public int encoding(ByteArrayOutputStream bytes) {
@@ -29,7 +28,9 @@ public class SllRISC extends MachConcreteNode implements MachNode{
// General form
// General form: "sll rd, rs1, rs2"
@Override public void asm(CodeGen code, SB sb) {
- sb.p(code.reg(this)).p(" = ").p(code.reg(in(1)));
+
+ sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" << ").p(code.reg(in(2)));
+
}
@Override public String op() { return "sll"; }
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
new file mode 100644
index 00000000..b5076ba0
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SplitRISC.java
@@ -0,0 +1,21 @@
+package com.seaofnodes.simple.node.cpus.riscv;
+
+import com.seaofnodes.simple.Utils;
+import com.seaofnodes.simple.codegen.RegMask;
+import com.seaofnodes.simple.node.Node;
+import com.seaofnodes.simple.node.SplitNode;
+import java.io.ByteArrayOutputStream;
+
+public class SplitRISC extends SplitNode {
+ SplitRISC( String kind, byte round ) { super(kind,round, new Node[2]); }
+
+ // Register mask allowed on input i.
+ @Override public RegMask regmap(int i) { return riscv.SPLIT_MASK; }
+ // Register mask allowed as a result. 0 for no register.
+ @Override public RegMask outregmap() { return riscv.SPLIT_MASK; }
+
+ // Encoding is appended into the byte array; size is returned
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SraIRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SraIRISC.java
index 654dd2ae..d5ab5e08 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SraIRISC.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SraIRISC.java
@@ -1,43 +1,11 @@
package com.seaofnodes.simple.node.cpus.riscv;
-import com.seaofnodes.simple.*;
-import com.seaofnodes.simple.node.*;
-import com.seaofnodes.simple.node.cpus.x86_64_v2.x86_64_v2;
-import com.seaofnodes.simple.type.Type;
-import com.seaofnodes.simple.type.TypeInteger;
-import java.io.ByteArrayOutputStream;
-import java.util.BitSet;
-import java.lang.StringBuilder;
-
-
-// Right Shift Arithmetic(immediate)
-public class SraIRISC extends MachConcreteNode implements MachNode{
- final TypeInteger _ti;
- SraIRISC(Node sra, TypeInteger ti) {super(sra); _inputs.pop(); _ti = ti;}
-
- // Register mask allowed on input i.
- // This is the normal calling convention
- @Override public RegMask regmap(int i) {
- // assert i==1;
- return riscv.RMASK; }
-
- // Register mask allowed as a result. 0 for no register.
- @Override public RegMask outregmap() { return riscv.RMASK; }
-
- // Output is same register as input#1
- @Override public int twoAddress() { return 0; }
-
- // Encoding is appended into the byte array; size is returned
- @Override public int encoding(ByteArrayOutputStream bytes) {
- throw Utils.TODO();
- }
-
- // General form
- // General form: "srai rd, rs1, imm"
- @Override public void asm(CodeGen code, SB sb) {
- sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" >> #");
- _ti.print(sb);
- }
+import com.seaofnodes.simple.Utils;
+import com.seaofnodes.simple.node.Node;
+public class SraIRISC extends ImmRISC {
+ SraIRISC( Node and, int imm) { super(and,imm); }
+ @Override int opcode() { throw Utils.TODO(); }
+ @Override public String glabel() { return ">>"; }
@Override public String op() { return "srai"; }
}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SraRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SraRISC.java
index 2d50ceaa..0726e292 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SraRISC.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SraRISC.java
@@ -1,14 +1,13 @@
package com.seaofnodes.simple.node.cpus.riscv;
-import com.seaofnodes.simple.CodeGen;
-import com.seaofnodes.simple.RegMask;
import com.seaofnodes.simple.SB;
import com.seaofnodes.simple.Utils;
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.RegMask;
import com.seaofnodes.simple.node.MachConcreteNode;
import com.seaofnodes.simple.node.MachNode;
import com.seaofnodes.simple.node.Node;
import com.seaofnodes.simple.type.TypeInteger;
-
import java.io.ByteArrayOutputStream;
// Right Shift Arithmetic
@@ -22,10 +21,7 @@ public class SraRISC extends MachConcreteNode implements MachNode {
// assert i==1;
return riscv.RMASK; }
// Register mask allowed as a result. 0 for no register.
- @Override public RegMask outregmap() { return riscv.RMASK; }
-
- // Output is same register as input#1
- @Override public int twoAddress() { return 1; }
+ @Override public RegMask outregmap() { return riscv.WMASK; }
// Encoding is appended into the byte array; size is returned
@Override public int encoding(ByteArrayOutputStream bytes) {
@@ -35,7 +31,7 @@ public class SraRISC extends MachConcreteNode implements MachNode {
// General form
// General form: "sra rd, rs1, rs2"
@Override public void asm(CodeGen code, SB sb) {
- sb.p(code.reg(this)).p(" = ").p(code.reg(in(1)));
+ sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" >> ").p(code.reg(in(2)));
}
@Override public String op() { return "sra"; }
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SrlIRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SrlIRISC.java
index f7ad6c25..a4eb99bc 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SrlIRISC.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SrlIRISC.java
@@ -1,43 +1,11 @@
package com.seaofnodes.simple.node.cpus.riscv;
-import com.seaofnodes.simple.*;
-import com.seaofnodes.simple.node.*;
-import com.seaofnodes.simple.node.cpus.x86_64_v2.x86_64_v2;
-import com.seaofnodes.simple.type.Type;
-import com.seaofnodes.simple.type.TypeInteger;
-import java.io.ByteArrayOutputStream;
-import java.util.BitSet;
-import java.lang.StringBuilder;
-
-
-// Right Shift Logical Immediate
-public class SrlIRISC extends MachConcreteNode implements MachNode {
- final TypeInteger _ti;
- SrlIRISC(Node srl, TypeInteger ti) {super(srl); _inputs.pop(); _ti = ti;}
-
-
- // Register mask allowed on input i.
- // This is the normal calling convention
- @Override public RegMask regmap(int i) {
- // assert i==1;
- return riscv.RMASK; }
- // Register mask allowed as a result. 0 for no register.
- @Override public RegMask outregmap() { return riscv.RMASK; }
-
- // Output is same register as input#1
- @Override public int twoAddress() { return 1; }
-
- // Encoding is appended into the byte array; size is returned
- @Override public int encoding(ByteArrayOutputStream bytes) {
- throw Utils.TODO();
- }
-
- // General form
- // General form: "srli dst << #imm"
- @Override public void asm(CodeGen code, SB sb) {
- sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" >> #");
- _ti.print(sb);
- }
+import com.seaofnodes.simple.Utils;
+import com.seaofnodes.simple.node.Node;
+public class SrlIRISC extends ImmRISC {
+ SrlIRISC( Node and, int imm) { super(and,imm); }
+ @Override int opcode() { throw Utils.TODO(); }
+ @Override public String glabel() { return ">>>"; }
@Override public String op() { return "srli"; }
}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SrlRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SrlRISC.java
index 25d62688..d1435540 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SrlRISC.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SrlRISC.java
@@ -1,6 +1,8 @@
package com.seaofnodes.simple.node.cpus.riscv;
import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.RegMask;
import com.seaofnodes.simple.node.*;
import com.seaofnodes.simple.type.Type;
import com.seaofnodes.simple.type.TypeInteger;
@@ -18,10 +20,7 @@ public class SrlRISC extends MachConcreteNode implements MachNode {
// assert i==1;
return riscv.RMASK; }
// Register mask allowed as a result. 0 for no register.
- @Override public RegMask outregmap() { return riscv.RMASK; }
-
- // Output is same register as input#1
- @Override public int twoAddress() { return 1; }
+ @Override public RegMask outregmap() { return riscv.WMASK; }
// Encoding is appended into the byte array; size is returned
@Override public int encoding(ByteArrayOutputStream bytes) {
@@ -31,7 +30,8 @@ public class SrlRISC extends MachConcreteNode implements MachNode {
// General form
// General form: "srl rd, rs1, rs2"
@Override public void asm(CodeGen code, SB sb) {
- sb.p(code.reg(this)).p(" = ").p(code.reg(in(1)));
+
+ sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" >>> ").p(code.reg(in(2)));
}
@Override public String op() { return "srl"; }
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/StoreRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/StoreRISC.java
index 9fbd7c12..c57cf11d 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/StoreRISC.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/StoreRISC.java
@@ -1,26 +1,40 @@
package com.seaofnodes.simple.node.cpus.riscv;
-import com.seaofnodes.simple.CodeGen;
-import com.seaofnodes.simple.RegMask;
import com.seaofnodes.simple.SB;
import com.seaofnodes.simple.Utils;
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.RegMask;
import com.seaofnodes.simple.node.MachConcreteNode;
import com.seaofnodes.simple.node.MachNode;
import com.seaofnodes.simple.node.Node;
import com.seaofnodes.simple.node.StoreNode;
-
import java.io.ByteArrayOutputStream;
import java.util.BitSet;
+// Store memory addressing on ARM
+// Support imm, reg(direct), or reg+off(indirect) addressing
+// Base = base - base pointer, offset is added to base
+// idx = null
+// off = off - offset added to base)
+// imm = imm - immediate value to store
+// val = Node of immediate value to store(null if its a constant immediate)
+
+//e.g s.cs[0] = 67; // C
+// base = s.cs, off = 4, imm = 67, val = null
// sw rs2,offset(rs1)
public class StoreRISC extends MemOpRISC {
- StoreRISC( StoreNode st) {
- super(st, st);
+ StoreRISC( StoreNode st, Node base, Node idx, int off, int imm, Node val ) {
+ super(st, base, idx, off, imm, val);
}
+ // Wider mask to store both GPRs and FPRs
@Override public RegMask regmap(int i) {
- return riscv.RMASK;
+ if( i==1 ) return riscv.MEM_MASK;
+ if( i==2 ) return riscv.RMASK;
+ if( i==3 ) return riscv.RMASK;
+ if( i==4 ) return riscv.RMASK;
+ throw Utils.TODO();
}
@@ -33,7 +47,9 @@ public class StoreRISC extends MemOpRISC {
}
@Override public void asm(CodeGen code, SB sb) {
-
+ asm_address(code,sb).p(",");
+ if( val()==null ) sb.p("#").p(_imm);
+ else sb.p(code.reg(val()));
}
@Override public String op() { return "st"+_sz; }
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SubFRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SubFRISC.java
index e6a0b8e4..e6fe3012 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SubFRISC.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SubFRISC.java
@@ -1,6 +1,8 @@
package com.seaofnodes.simple.node.cpus.riscv;
import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.RegMask;
import com.seaofnodes.simple.node.*;
import com.seaofnodes.simple.type.TypeInteger;
import java.io.ByteArrayOutputStream;
@@ -12,8 +14,6 @@ public class SubFRISC extends MachConcreteNode implements MachNode{
@Override public RegMask regmap(int i) { assert i==1 || i==2; return riscv.FMASK; }
// Register mask allowed as a result. 0 for no register.
@Override public RegMask outregmap() { return riscv.FMASK; }
- // Output is same register as input#1
- @Override public int twoAddress() { return 0; }
// Encoding is appended into the byte array; size is returned
@Override public int encoding(ByteArrayOutputStream bytes) {
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SubRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SubRISC.java
index 6fb0e0ef..c37aea98 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SubRISC.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SubRISC.java
@@ -1,6 +1,8 @@
package com.seaofnodes.simple.node.cpus.riscv;
import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.RegMask;
import com.seaofnodes.simple.node.*;
import com.seaofnodes.simple.type.TypeInteger;
import java.io.ByteArrayOutputStream;
@@ -11,9 +13,7 @@ public class SubRISC extends MachConcreteNode implements MachNode {
// Register mask allowed on input i.
@Override public RegMask regmap(int i) { assert i==1 || i==2; return riscv.RMASK; }
// Register mask allowed as a result. 0 for no register.
- @Override public RegMask outregmap() { return riscv.RMASK; }
- // Output is same register as input#1
- @Override public int twoAddress() { return 0; }
+ @Override public RegMask outregmap() { return riscv.WMASK; }
// Encoding is appended into the byte array; size is returned
@Override public int encoding(ByteArrayOutputStream bytes) {
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/TFPRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/TFPRISC.java
index b508541a..23fb6a74 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/TFPRISC.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/TFPRISC.java
@@ -1,22 +1,24 @@
package com.seaofnodes.simple.node.cpus.riscv;
-
import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.RegMask;
import com.seaofnodes.simple.node.ConstantNode;
import com.seaofnodes.simple.node.MachNode;
-import com.seaofnodes.simple.node.cpus.x86_64_v2.TFPX86;
-import com.seaofnodes.simple.node.cpus.x86_64_v2.x86_64_v2;
import com.seaofnodes.simple.type.Type;
import com.seaofnodes.simple.type.TypeFunPtr;
-
import java.io.ByteArrayOutputStream;
public class TFPRISC extends ConstantNode implements MachNode{
TFPRISC(ConstantNode con) {super(con);}
+ @Override public String op() { return _con == Type.NIL ? "xor" : "ldx"; }
// Register mask allowed on input i. 0 for no register.
@Override public RegMask regmap(int i) { return null; }
// General int registers
- @Override public RegMask outregmap() { return x86_64_v2.RMASK; }
+ @Override public RegMask outregmap() { return riscv.WMASK; }
+
+ @Override public boolean isClone() { return true; }
+ @Override public TFPRISC copy() { return new TFPRISC(this); }
// Encoding is appended into the byte array; size is returned
@Override public int encoding(ByteArrayOutputStream bytes) {
@@ -34,10 +36,4 @@ public class TFPRISC extends ConstantNode implements MachNode{
else
_con.print(sb.p(reg).p(" #"));
}
-
- @Override public String op() {
- if( _con == Type.NIL )
- return "xor";
- return "ldx"; // Some fancier encoding
- }
}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/XorIRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/XorIRISC.java
index 3d53ee64..a2e32f06 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/XorIRISC.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/XorIRISC.java
@@ -1,45 +1,11 @@
package com.seaofnodes.simple.node.cpus.riscv;
-import com.seaofnodes.simple.CodeGen;
-import com.seaofnodes.simple.RegMask;
-import com.seaofnodes.simple.SB;
import com.seaofnodes.simple.Utils;
-import com.seaofnodes.simple.node.MachConcreteNode;
-import com.seaofnodes.simple.node.MachNode;
import com.seaofnodes.simple.node.Node;
-import com.seaofnodes.simple.node.cpus.x86_64_v2.x86_64_v2;
-import com.seaofnodes.simple.type.TypeInteger;
-
-import java.io.ByteArrayOutputStream;
-
-public class XorIRISC extends MachConcreteNode implements MachNode{
- final TypeInteger _ti;
- XorIRISC(Node xor, TypeInteger ti) {super(xor); _inputs.pop(); _ti = ti;}
-
- // Register mask allowed on input i.
- // This is the normal calling convention
- @Override public RegMask regmap(int i) {
- assert i==1 || i==2;
-
- return riscv.RMASK; }
-
- // Register mask allowed as a result. 0 for no register.
- @Override public RegMask outregmap() { return riscv.RMASK; }
-
- // Output is same register as input#1
- @Override public int twoAddress() { return 1; }
-
- // Encoding is appended into the byte array; size is returned
- @Override public int encoding(ByteArrayOutputStream bytes) {
- throw Utils.TODO();
- }
-
- // General form
- // General form: "rd = rs1 ^ imm"
- @Override public void asm(CodeGen code, SB sb) {
- sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" ^ #");
- _ti.print(sb);
- }
+public class XorIRISC extends ImmRISC {
+ XorIRISC( Node and, int imm) { super(and,imm); }
+ @Override int opcode() { throw Utils.TODO(); }
+ @Override public String glabel() { return "^"; }
@Override public String op() { return "xori"; }
}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/XorRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/XorRISC.java
index 2f34529e..f4ecb430 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/XorRISC.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/XorRISC.java
@@ -1,14 +1,13 @@
package com.seaofnodes.simple.node.cpus.riscv;
-import com.seaofnodes.simple.CodeGen;
-import com.seaofnodes.simple.RegMask;
import com.seaofnodes.simple.SB;
import com.seaofnodes.simple.Utils;
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.RegMask;
import com.seaofnodes.simple.node.MachConcreteNode;
import com.seaofnodes.simple.node.MachNode;
import com.seaofnodes.simple.node.Node;
import com.seaofnodes.simple.type.TypeInteger;
-
import java.io.ByteArrayOutputStream;
public class XorRISC extends MachConcreteNode implements MachNode {
@@ -25,10 +24,7 @@ public class XorRISC extends MachConcreteNode implements MachNode {
}
// Register mask allowed as a result. 0 for no register.
- @Override public RegMask outregmap() { return riscv.RMASK; }
-
- // Output is same register as input#1
- @Override public int twoAddress() { return 0; }
+ @Override public RegMask outregmap() { return riscv.WMASK; }
// Encoding is appended into the byte array; size is returned
@Override public int encoding(ByteArrayOutputStream bytes) {
@@ -38,6 +34,8 @@ public class XorRISC extends MachConcreteNode implements MachNode {
// General form
// General form: # # rd = rs1 ^ rs2
@Override public void asm(CodeGen code, SB sb) {
- sb.p(code.reg(this)).p(" = ").p(code.reg(in(1)));
+
+ sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" ^ ").p(code.reg(in(2)));
+
}
}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/riscv.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/riscv.java
index 399eebca..83706cbf 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/riscv.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/riscv.java
@@ -1,63 +1,84 @@
package com.seaofnodes.simple.node.cpus.riscv;
-import com.seaofnodes.simple.Machine;
-import com.seaofnodes.simple.RegMask;
import com.seaofnodes.simple.Utils;
+import com.seaofnodes.simple.codegen.LRG;
+import com.seaofnodes.simple.codegen.Machine;
+import com.seaofnodes.simple.codegen.RegMask;
import com.seaofnodes.simple.node.*;
-import com.seaofnodes.simple.node.cpus.x86_64_v2.*;
import com.seaofnodes.simple.type.*;
-public class riscv extends Machine{
+public class riscv extends Machine {
@Override public String name() {return "riscv";}
// Using ABI names instead of register names
- public static int ZERO = 0, RA = 1, SP = 2, GP = 3, TP = 4, T0 = 5, T1 = 6, T2 = 7;
- public static int S0 = 8, S1 = 9, A0 = 10, A1 = 11, A2 = 12, A3 = 13, A4 = 14, A5 = 15;
- public static int A6 = 16, A7 = 17, S2 = 18, S3 = 19, S4 = 20, S5 = 21, S6 = 22, S7 = 23;
- public static int S8 = 24, S9 = 25, S10 = 26, S11 = 27, T3 = 28, T4 = 29, T5 = 30, T6 = 31;
+ static final int RPC= 1, SP = 2, GP = 3, TP = 4, T0 = 5, T1 = 6, T2 = 7;
+ static final int S0 = 8, S1 = 9, A0 = 10, A1 = 11, A2 = 12, A3 = 13, A4 = 14, A5 = 15;
+ static final int A6 = 16, A7 = 17, S2 = 18, S3 = 19, S4 = 20, S5 = 21, S6 = 22, S7 = 23;
+ static final int S8 = 24, S9 = 25, S10 = 26, S11 = 27, T3 = 28, T4 = 29, T5 = 30, T6 = 31;
// FP registers
- public static int F0 = 32, F1 = 33, F2 = 34, F3 = 35, F4 = 36, F5 = 37, F6 = 38, F7 = 39;
- public static int FS0 = 40, FS1 = 41, FA0 = 42, FA1 = 43, FA2 = 44, FA3 = 45, FA4 = 46, FA5 = 47;
- public static int FA6 = 48, FA7 = 49, FS2 = 50, FS3 = 51, FS4 = 52, FS5 = 53, FS6 = 54, FS7 = 55;
- public static int FS8 = 56, FS9 = 57, FS10 = 58, FS11 = 59, FT8 = 60, FT9 = 61, FT10 = 62, FT11 = 63;
+ static final int F0 = 32, F1 = 33, F2 = 34, F3 = 35, F4 = 36, F5 = 37, F6 = 38, F7 = 39;
+ static final int FS0 = 40, FS1 = 41, FA0 = 42, FA1 = 43, FA2 = 44, FA3 = 45, FA4 = 46, FA5 = 47;
+ static final int FA6 = 48, FA7 = 49, FS2 = 50, FS3 = 51, FS4 = 52, FS5 = 53, FS6 = 54, FS7 = 55;
+ static final int FS8 = 56, FS9 = 57, FS10 = 58, FS11 = 59, FT8 = 60, FT9 = 61, FT10 = 62, FT11 = 63;
+
+ static final int MAX_REG = 61;
+
+ static final String[] REGS = new String[] {
+ "zero", "r1" , "rsp" , "r3" , "r4" , "r5" , "r6" , "r7" ,
+ "r8" , "r9" , "r10" , "r11" , "r12" , "r13" , "r14" , "r15" ,
+ "r16" , "r17" , "r18" , "r19" , "r20" , "r21" , "r22" , "r23" ,
+ "r24" , "r25" , "r26" , "r27" , "r28" , "r29" , "r30" , "r31" ,
+ "f0" , "f1" , "f2" , "f3" , "f4" , "f5" , "f6" , "f7" ,
+ "fs0" , "fs1" , "fa0" , "fa1" , "fa2" , "fa3" , "fa4" , "fa5" ,
+ "fa6" , "fa7" , "fs2" , "fs3" , "fs4" , "fs5" , "fs6" , "fs7" ,
+ "fs8" , "fs9" , "fs10", "fs11", "ft8" , "ft9" , "ft10", "ft11"
+ };
+ @Override public String reg( int reg ) {
+ return reg < REGS.length ? REGS[reg] : "[rsp+"+(reg-REGS.length)*4+"]";
+ }
- public static int FLAGS = 64;
// General purpose register mask: pointers and ints, not floats
- public static RegMask RMASK = new RegMask(0b11111111111111111111111111111111);
- // Float mask from(ft0–ft11)
- public static RegMask FMASK = new RegMask(0b11111111111111111111111111111111);
+ static final long RD_BITS = 0b11111111111111111111111111111111L; // All the GPRs
+ static final RegMask RMASK = new RegMask(RD_BITS);
+
+ static final long WR_BITS = 0b11111111111111111111111111111010L; // All the GPRs, minus ZERO and SP
+ static final RegMask WMASK = new RegMask(WR_BITS);
+ // Float mask from(f0-ft10). TODO: ft10,ft11 needs a larger RegMask
+ static final long FP_BITS = 0b11111111111111111111111111111111L<<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 );
- // Return single int/ptr register
- public static RegMask RET_MASK = new RegMask(A0);
// Arguments masks
- public static RegMask A0_MASK = new RegMask(1L<<A0);
- public static RegMask A1_MASK = new RegMask(1L<<A1);
- public static RegMask A2_MASK = new RegMask(1L<<A2);
- public static RegMask A3_MASK = new RegMask(1L<<A3);
- public static RegMask A4_MASK = new RegMask(1L<<A4);
- public static RegMask A5_MASK = new RegMask(1L<<A5);
- public static RegMask A6_MASK = new RegMask(1L<<A6);
- public static RegMask A7_MASK = new RegMask(1L<<A7);
+ static final RegMask A0_MASK = new RegMask(A0);
+ static final RegMask A1_MASK = new RegMask(A1);
+ static final RegMask A2_MASK = new RegMask(A2);
+ static final RegMask A3_MASK = new RegMask(A3);
+ static final RegMask A4_MASK = new RegMask(A4);
+ static final RegMask A5_MASK = new RegMask(A5);
+ static final RegMask A6_MASK = new RegMask(A6);
+ static final RegMask A7_MASK = new RegMask(A7);
- public static RegMask FLAGS_MASK = new RegMask(1L << FLAGS);
+ static final RegMask RPC_MASK = new RegMask(RPC);
// Float arguments masks
- public static RegMask FA0_MASK = new RegMask(1L<<FA0);
- public static RegMask FA1_MASK = new RegMask(1L<<FA1);
- public static RegMask FA2_MASK = new RegMask(1L<<FA2);
- public static RegMask FA3_MASK = new RegMask(1L<<FA3);
- public static RegMask FA4_MASK = new RegMask(1L<<FA4);
- public static RegMask FA5_MASK = new RegMask(1L<<FA5);
- public static RegMask FA6_MASK = new RegMask(1L<<FA6);
- public static RegMask FA7_MASK = new RegMask(1L<<FA7);
+ static final RegMask FA0_MASK = new RegMask(FA0);
+ static final RegMask FA1_MASK = new RegMask(FA1);
+ static final RegMask FA2_MASK = new RegMask(FA2);
+ static final RegMask FA3_MASK = new RegMask(FA3);
+ static final RegMask FA4_MASK = new RegMask(FA4);
+ static final RegMask FA5_MASK = new RegMask(FA5);
+ static final RegMask FA6_MASK = new RegMask(FA6);
+ static final RegMask FA7_MASK = new RegMask(FA7);
// Int arguments calling conv
- static RegMask[] CALLINMASK_RISCV_INT = new RegMask[] {
- null,
- null,
+ static RegMask[] CALLINMASK = new RegMask[] {
A0_MASK,
A1_MASK,
A2_MASK,
@@ -68,31 +89,7 @@ public class riscv extends Machine{
A7_MASK
};
- static int[] CALLINARG_RISCV_INT = new int[] {
- 0, // Control, no register
- 0, // Memory, no register
- A0,
- A1,
- A2,
- A3,
- A4,
- A5,
- A6,
- A7
- };
-
- static int callInArgInt(int idx) {
- return CALLINARG_RISCV_INT[idx];
- }
-
- static RegMask callInMaskInt(int idx) {
- return CALLINMASK_RISCV_INT[idx];
- }
-
- // Float arguments
- static RegMask[] CALLINMASK_RISCV_FLOAT = new RegMask[] {
- null,
- null,
+ static RegMask[] XMMS = new RegMask[] {
FA0_MASK,
FA1_MASK,
FA2_MASK,
@@ -103,108 +100,130 @@ public class riscv extends Machine{
FA7_MASK
};
-
- static int[] CALLINARG_RISCV_FLOAT = new int[] {
- 0, // Control, no register
- 0, // Memory, no register
- FA0,
- FA1,
- FA2,
- FA3,
- FA4,
- FA5,
- FA6,
- FA7
- };
-
-
- static int callInArgFloat(int idx) {
- return CALLINARG_RISCV_FLOAT[idx];
+ static RegMask callInMask( TypeFunPtr tfp, int idx ) {
+ if( idx==0 ) return RPC_MASK;
+ if( idx==1 ) return null;
+ // Count floats in signature up to index
+ int fcnt=0;
+ for( int i=2; i<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
}
- static RegMask callInMaskFloat(int idx) {
- return CALLINMASK_RISCV_FLOAT[idx];
- }
- // caller saved(riscv)
- //public static final long RISCV_CALLER_SAVED= TBD
// callee saved(riscv)
- public static final long RISCV_CALLEE_SAVED =
- (1L << FS0) | (1L << FS1) | (1L << FS2) | (1L << FS3) | (1L << FS4)
- | (1L << FS5) | (1L << FS6) | (1L << FS7) | (1L << FS8) | (1L << FS9) | (1L << FS10);
-
-
- // Calling conv metadata
- public int GPR_COUNT_CONV_RISCV = 7; // A0, A1, A2, A3, A4, A5, A6, A7
- public int FLOAT_COUNT_CONV_RISCV = 7; // FA0, FA1, FA2, FA3, FA4, FA5, FA6, FA7
-
- public static final String[] REGS = new String[] {
- "zero", "ra" , "sp" , "gp" , "tp" , "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",
- "flags"
- };
+ static final long CALLEE_SAVE =
+ (1L<< S0) | (1L<< S1) | (1L<< S2 ) | (1L<< S3 ) |
+ (1L<< S4) | (1L<< S5) | (1L<< S6 ) | (1L<< S7 ) |
+ (1L<< S8) | (1L<< S9) | (1L<< S10) | (1L<< S11) |
+ (1L<<FS0) | (1L<<FS1) | (1L<<FS2 ) | (1L<<FS3 ) |
+ (1L<<FS4) | (1L<<FS5) | (1L<<FS6 ) | (1L<<FS7 ) |
+ (1L<<FS8) | (1L<<FS9) | (1L<<FS10) | (1L<<FS11);
+
+ static final RegMask CALLER_SAVE_MASK;
+ static {
+ long caller = ~CALLEE_SAVE;
+ caller &= ~(1L<<SP);
+ CALLER_SAVE_MASK = new RegMask(caller,0);
+ }
+ static RegMask riscCallerSave() { return CALLER_SAVE_MASK; }
+ @Override public RegMask callerSave() { return riscCallerSave(); }
+
+ static final RegMask CALLEE_SAVE_MASK = new RegMask(CALLEE_SAVE);
+ static RegMask riscCalleeSave() { return CALLEE_SAVE_MASK; }
+ @Override public RegMask calleeSave() { return riscCalleeSave(); }
+
+ // Return single int/ptr register. Used by CallEnd output and Return input.
+ static final RegMask[] RET_MASKS;
+ static {
+ int nSaves = CALLEE_SAVE_MASK.size();
+ RET_MASKS = new RegMask[4 + nSaves];
+ RET_MASKS[0] = null;
+ RET_MASKS[1] = null; // Memory
+ RET_MASKS[2] = null; // Varies, either XMM0 or RAX
+ RET_MASKS[3] = RMASK; // Expected R1 but could be any register really
+ short reg = CALLEE_SAVE_MASK.firstReg();
+ for( int i=0; i<nSaves; i++ ) {
+ RET_MASKS[i+4] = new RegMask(reg);
+ reg = CALLEE_SAVE_MASK.nextReg(reg);
+ }
+ }
+ static RegMask retMask( TypeFunPtr tfp, int i ) {
+ return i==2
+ ? (tfp.ret() instanceof TypeFloat ? FA0_MASK : A0_MASK)
+ : RET_MASKS[i];
+ }
+
- // General purpose register mask:
- @Override public String reg( int reg ) { return REGS[reg]; }
// Return a MachNode unconditional branch
@Override public CFGNode jump() {
throw Utils.TODO();
}
// Create a split op; any register to any register, including stack slots
- @Override public Node split() {
- throw Utils.TODO();
- }
+ @Override public SplitNode split(String kind, byte round, LRG lrg) { return new SplitRISC(kind,round); }
// Break an infinite loop
@Override public IfNode never( CFGNode ctrl ) {
throw Utils.TODO();
}
+ // True if signed 12-bit immediate
+ private static boolean imm12(TypeInteger ti) {
+ // 64-12==52
+ return ti.isConstant() && ((ti.value()<<52)>>52) == ti.value();
+ }
+
+
@Override public Node instSelect( Node n ) {
return switch (n) {
- case AddFNode addf -> addf(addf);
- case AddNode add -> add(add);
- case AndNode and -> and(and);
- case BoolNode bool -> cmp(bool);
- case CallEndNode cend -> new CallEndNode((CallNode) cend.in(0));
- case CallNode call -> call(call);
- case CProjNode c -> new CProjNode(c);
- case ConstantNode con -> con(con);
- case DivFNode divf -> new DivFRISC(divf);
- case DivNode div -> new DivRISC(div);
- case FunNode fun -> new FunRISC(fun);
- case IfNode iff -> jmp(iff);
- case LoadNode ld -> ld(ld);
- case MemMergeNode mem -> new MemMergeNode(mem);
- case MulFNode mulf -> new MulFRISC(mulf);
- case MulNode mul -> mul(mul);
- case NewNode nnn -> new NewRISC(nnn);
- case OrNode or -> or(or);
- case ParmNode parm -> new ParmRISC(parm);
- case PhiNode phi -> new PhiNode(phi);
- case ProjNode prj -> prj(prj);
- case ReadOnlyNode read -> new ReadOnlyNode(read);
- case ReturnNode ret -> new RetRISC(ret, ret.fun());
- case SarNode sar -> sra(sar);
- case ShlNode shl -> sll(shl);
- case ShrNode shr -> srl(shr);
- case StartNode start -> new StartNode(start);
- case StopNode stop -> new StopNode(stop);
- case StoreNode st -> st(st);
- case SubFNode subf -> new SubFRISC(subf);
- case SubNode sub -> sub(sub);
- case ToFloatNode tfn -> i2f8(tfn);
- case XorNode xor -> xor(xor);
-
- case LoopNode loop -> new LoopNode(loop);
- case RegionNode region -> new RegionNode(region);
- default -> throw Utils.TODO();
+ case AddFNode addf -> addf(addf);
+ case AddNode add -> add(add);
+ case AndNode and -> and(and);
+ case BoolNode bool -> cmp(bool);
+ case CallNode call -> call(call);
+ case CastNode cast -> new CastRISC(cast);
+ case CallEndNode cend -> new CallEndRISC(cend);
+ case CProjNode c -> new CProjNode(c);
+ case ConstantNode con -> con(con);
+ case DivFNode divf -> new DivFRISC(divf);
+ case DivNode div -> new DivRISC(div);
+ case FunNode fun -> new FunRISC(fun);
+ case IfNode iff -> jmp(iff);
+ case LoadNode ld -> ld(ld);
+ case MemMergeNode mem -> new MemMergeNode(mem);
+ case MulFNode mulf -> new MulFRISC(mulf);
+ case MulNode mul -> new MulRISC(mul);
+ case NewNode nnn -> new NewRISC(nnn);
+ case OrNode or -> or(or);
+ case ParmNode parm -> new ParmRISC(parm);
+ case PhiNode phi -> new PhiNode(phi);
+ case ProjNode prj -> prj(prj);
+ case ReadOnlyNode read -> new ReadOnlyNode(read);
+ case ReturnNode ret -> new RetRISC(ret, ret.fun());
+ case SarNode sar -> sra(sar);
+ case ShlNode shl -> sll(shl);
+ case ShrNode shr -> srl(shr);
+ case StartNode start -> new StartNode(start);
+ case StopNode stop -> new StopNode(stop);
+ case StoreNode st -> st(st);
+ case SubFNode subf -> new SubFRISC(subf);
+ case SubNode sub -> sub(sub);
+ case ToFloatNode tfn -> i2f8(tfn);
+ case XorNode xor -> xor(xor);
+
+ case LoopNode loop -> new LoopNode(loop);
+ case RegionNode region -> new RegionNode(region);
+ default -> throw Utils.TODO();
};
}
@@ -213,94 +232,86 @@ public class riscv extends Machine{
}
private Node add(AddNode add) {
- Node rhs = add.in(2);
- if( rhs instanceof ConstantNode off && off._con instanceof TypeInteger toff ) {
- return new AddIRISC(add, toff);
- }
+ if( add.in(2) instanceof ConstantNode off && off._con instanceof TypeInteger ti && imm12(ti) )
+ return new AddIRISC(add, (int)ti.value());
return new AddRISC(add);
}
private Node and(AndNode and) {
- if( and.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti )
- return new AndIRISC(and, ti);
+ if( and.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti && imm12(ti) )
+ return new AndIRISC(and, (int)ti.value());
return new AndRISC(and);
}
- private Node cmp(BoolNode bool) {
- Node cmp = _cmp(bool);
- return new SetRISC(cmp, bool.op());
- }
-
private Node call(CallNode call) {
if( call.fptr() instanceof ConstantNode con && con._con instanceof TypeFunPtr tfp )
return new CallRISC(call, tfp);
return new CallRRISC(call);
}
- private Node _cmp(BoolNode bool) {
- Node rhs = bool.in(2);
-
- return rhs instanceof ConstantNode con && con._con instanceof TypeInteger ti
- ? new CmpIRISC(bool, ti)
- : new CmpRISC(bool);
+ private Node cmp(BoolNode bool) {
+ if( bool.in(2) instanceof ConstantNode off && off._con instanceof TypeInteger toff )
+ return new SetIRISC(bool, (int)toff.value());
+ // Float variant
+ if( bool.isFloat() )
+ return new SetFRISC(bool);
+ return new SetRISC(bool);
}
private Node con( ConstantNode con ) {
+ if( !con._con.isConstant() ) return new ConstantNode( con ); // Default unknown caller inputs
return switch( con._con ) {
- case TypeInteger ti -> new IntRISC(con);
- case TypeFloat tf -> new IntRISC(con);
- case TypeFunPtr tfp -> new TFPRISC(con);
- case TypeMemPtr tmp -> new ConstantNode(con);
- case TypeNil tn -> throw Utils.TODO();
- // TOP, BOTTOM, XCtrl, Ctrl, etc. Never any executable code.
- case Type t -> new ConstantNode(con);
+ case TypeInteger ti -> new IntRISC(con);
+ case TypeFloat tf -> new FltRISC(con);
+ case TypeFunPtr tfp -> new TFPRISC(con);
+ case TypeMemPtr tmp -> throw Utils.TODO();
+ case TypeNil tn -> throw Utils.TODO();
+ // TOP, BOTTOM, XCtrl, Ctrl, etc. Never any executable code.
+ case Type t -> t==Type.NIL ? new IntRISC(con) : new ConstantNode(con);
};
}
private Node jmp( IfNode iff ) {
- // If/Bool combos will match to a Cmp/Set which sets flags.
- // Most general arith ops will also set flags, which the Jmp needs directly.
- // Loads do not set the flags, and will need an explicit TEST
- if( !(iff.in(1) instanceof BoolNode) )
- iff.setDef(1,new BoolNode.EQ(iff.in(1),new ConstantNode(TypeInteger.ZERO)));
- return new CBranchRISC(iff, ((BoolNode)iff.in(1)).op());
+ if( iff.in(1) instanceof BoolNode bool && !bool.isFloat() )
+ return new BranchRISC(iff, bool.op(), bool.in(1), bool.in(2));
+ // Vs zero
+ return new BranchRISC(iff, "==", iff.in(1), null);
}
private Node or(OrNode or) {
- if(or.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti)
- return new OrIRISC(or, ti);
-
+ if( or.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti && imm12(ti) )
+ return new OrIRISC(or, (int)ti.value());
return new OrRISC(or);
}
private Node xor(XorNode xor) {
- if(xor.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti)
- return new XorIRISC(xor, ti);
+ if( xor.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti && imm12(ti) )
+ return new XorIRISC(xor, (int)ti.value());
return new XorRISC(xor);
}
private Node sra(SarNode sar) {
- if( sar.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti)
- return new SraIRISC(sar, ti);
+ if( sar.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti && imm12(ti) )
+ return new SraIRISC(sar, (int)ti.value());
return new SraRISC(sar);
}
private Node srl(ShrNode shr) {
- if( shr.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti)
- return new SrlIRISC(shr, ti);
+ if( shr.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti && imm12(ti) )
+ return new SrlIRISC(shr, (int)ti.value());
return new SrlRISC(shr);
}
private Node sll(ShlNode sll) {
- if( sll.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti)
- return new SllIRISC(sll, ti);
+ if( sll.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti && imm12(ti) )
+ return new SllIRISC(sll, (int)ti.value());
return new SllRISC(sll);
}
private Node sub(SubNode sub) {
- return sub.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti
- ? new AddIRISC(sub, TypeInteger.constant(-ti.value()))
- : new SubRISC(sub);
+ return sub.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti && imm12(ti)
+ ? new AddIRISC(sub, (int)(-ti.value()))
+ : new SubRISC(sub);
}
private Node i2f8(ToFloatNode tfn) {
@@ -313,17 +324,38 @@ public class riscv extends Machine{
}
private Node ld(LoadNode ld) {
- return new LoadRISC(ld);
+ return new LoadRISC(address(ld),ld.ptr(),idx,off);
}
private Node st(StoreNode st) {
- return new StoreRISC(st);
+ int imm=0;
+ Node xval = st.val();
+ if( xval instanceof ConstantNode con && con._con instanceof TypeInteger ti && imm12(ti) ) {
+ xval = null;
+ imm = (int)ti.value();
+ assert imm == ti.value(); // In 32-bit range
+ }
+ return new StoreRISC(address(st),st.ptr(),idx,off,imm,xval);
}
- private Node mul(MulNode mul) {
- return mul.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti
- ? new MulIRISC(mul, ti)
- : new MulRISC(mul);
+ // Gather addressing mode bits prior to constructing. This is a builder
+ // pattern, but saving the bits in a *local* *global* here to keep mess
+ // contained.
+ private static int off;
+ private static Node idx;
+ private <N 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 && imm12(ti) ) {
+ off = (int)ti.value();
+ } else {
+ idx = mop.off();
+ }
+ return mop;
}
-}
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddFMemX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddFMemX86.java
index 4bb24fa3..ffc6966e 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddFMemX86.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddFMemX86.java
@@ -1,9 +1,9 @@
package com.seaofnodes.simple.node.cpus.x86_64_v2;
-import com.seaofnodes.simple.CodeGen;
-import com.seaofnodes.simple.Utils;
-import com.seaofnodes.simple.RegMask;
import com.seaofnodes.simple.SB;
+import com.seaofnodes.simple.Utils;
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.RegMask;
import com.seaofnodes.simple.node.*;
public class AddFMemX86 extends MemOpX86 {
@@ -12,10 +12,10 @@ public class AddFMemX86 extends MemOpX86 {
}
@Override public RegMask regmap(int i) {
- if( i==1 ) return null; // Memory
- if( i==2 ) return x86_64_v2.RMASK; // base
- if( i==3 ) return x86_64_v2.RMASK; // index
- if( i==4 ) return x86_64_v2.XMASK; // value
+ if( i==1 ) return null; // Memory
+ if( i==2 ) return x86_64_v2.RMASK; // base
+ if( i==3 ) return x86_64_v2.RMASK; // index
+ if( i==4 ) return x86_64_v2.XMASK; // value
throw Utils.TODO();
}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddFX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddFX86.java
index a3f5c7b3..d4ea9162 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddFX86.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddFX86.java
@@ -1,6 +1,8 @@
package com.seaofnodes.simple.node.cpus.x86_64_v2;
import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.RegMask;
import com.seaofnodes.simple.node.*;
import com.seaofnodes.simple.type.TypeInteger;
import java.io.ByteArrayOutputStream;
@@ -14,6 +16,8 @@ public class AddFX86 extends MachConcreteNode implements MachNode {
@Override public RegMask outregmap() { return x86_64_v2.XMASK; }
// Output is same register as input#1
@Override public int twoAddress() { return 1; }
+ // Ok to swap arguments
+ @Override public boolean commutes() { return true; }
// Encoding is appended into the byte array; size is returned
@Override public int encoding(ByteArrayOutputStream bytes) {
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddIX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddIX86.java
index 6a332ff5..c7f92ff1 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddIX86.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddIX86.java
@@ -1,6 +1,8 @@
package com.seaofnodes.simple.node.cpus.x86_64_v2;
import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.RegMask;
import com.seaofnodes.simple.node.*;
import com.seaofnodes.simple.type.TypeInteger;
import java.io.ByteArrayOutputStream;
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddMemX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddMemX86.java
index 7d45d757..3d4fac41 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddMemX86.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddMemX86.java
@@ -1,8 +1,8 @@
package com.seaofnodes.simple.node.cpus.x86_64_v2;
-import com.seaofnodes.simple.CodeGen;
-import com.seaofnodes.simple.RegMask;
import com.seaofnodes.simple.SB;
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.RegMask;
import com.seaofnodes.simple.node.*;
public class AddMemX86 extends MemOpX86 {
@@ -14,7 +14,8 @@ public class AddMemX86 extends MemOpX86 {
// Register mask allowed as a result. 0 for no register.
@Override public RegMask outregmap() { return x86_64_v2.WMASK; }
-
+ // Output is same register as val()
+ @Override public int twoAddress() { return 4; }
// General form: "add dst = src + [base + idx<<2 + 12]"
@Override public void asm(CodeGen code, SB sb) {
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddX86.java
index 61aa5a9e..9553d499 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddX86.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddX86.java
@@ -1,11 +1,13 @@
package com.seaofnodes.simple.node.cpus.x86_64_v2;
import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.RegMask;
import com.seaofnodes.simple.node.*;
import com.seaofnodes.simple.type.TypeInteger;
import java.io.ByteArrayOutputStream;
-public class AddX86 extends MachConcreteNode implements MachNode {
+public class AddX86 extends MachConcreteNode {
AddX86( Node add ) { super(add); }
// Register mask allowed on input i.
@@ -14,6 +16,8 @@ public class AddX86 extends MachConcreteNode implements MachNode {
@Override public RegMask outregmap() { return x86_64_v2.WMASK; }
// Output is same register as input#1
@Override public int twoAddress() { return 1; }
+ // Ok to swap arguments
+ @Override public boolean commutes() { return true; }
// Encoding is appended into the byte array; size is returned
@Override public int encoding(ByteArrayOutputStream bytes) {
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AndIX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AndIX86.java
index f2ee1c66..a7f1801e 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AndIX86.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AndIX86.java
@@ -1,6 +1,8 @@
package com.seaofnodes.simple.node.cpus.x86_64_v2;
import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.RegMask;
import com.seaofnodes.simple.node.*;
import com.seaofnodes.simple.type.Type;
import com.seaofnodes.simple.type.TypeInteger;
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AndX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AndX86.java
index 45cf2b22..d7fb3613 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AndX86.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AndX86.java
@@ -1,6 +1,8 @@
package com.seaofnodes.simple.node.cpus.x86_64_v2;
import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.RegMask;
import com.seaofnodes.simple.node.*;
import java.io.ByteArrayOutputStream;
@@ -23,11 +25,10 @@ public class AndX86 extends MachConcreteNode implements MachNode{
}
// General form
- // General form: "and dst & #imm"
+ // General form: "and dst & src"
@Override public void asm(CodeGen code, SB sb) {
- sb.p(code.reg(this)).p(" = ").p(code.reg(in(1)));
+ sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" & ").p(code.reg(in(2)));;
}
@Override public String op() { return "and"; }
}
-
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CallEndX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CallEndX86.java
index 55ab90c9..b9cc5659 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CallEndX86.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CallEndX86.java
@@ -2,8 +2,8 @@ package com.seaofnodes.simple.node.cpus.x86_64_v2;
import com.seaofnodes.simple.SB;
import com.seaofnodes.simple.Utils;
-import com.seaofnodes.simple.CodeGen;
-import com.seaofnodes.simple.RegMask;
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.RegMask;
import com.seaofnodes.simple.node.CallEndNode;
import com.seaofnodes.simple.node.MachNode;
import com.seaofnodes.simple.type.TypeFloat;
@@ -14,16 +14,14 @@ public class CallEndX86 extends CallEndNode implements MachNode {
final TypeFunPtr _tfp;
CallEndX86( CallEndNode cend ) {
super(cend);
- _tfp = (TypeFunPtr)cend.call().fptr()._type;
+ _tfp = (TypeFunPtr)(cend.call().fptr()._type);
}
@Override public String label() { return op(); }
@Override public RegMask regmap(int i) { return null; }
- @Override public RegMask outregmap() { throw Utils.TODO(); }
- @Override public RegMask outregmap(int idx) {
- if( idx != 2 ) return null;
- return _tfp._ret instanceof TypeFloat ? x86_64_v2.RET_FMASK : x86_64_v2.RET_MASK;
- }
+ @Override public RegMask outregmap() { return null; }
+ @Override public RegMask outregmap(int idx) { return idx == 2 ? x86_64_v2.retMask(_tfp,2) : null; }
+ @Override public RegMask killmap() { return x86_64_v2.x86CallerSave(); }
// Encoding is appended into the byte array; size is returned
@Override public int encoding(ByteArrayOutputStream bytes) {
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CallRX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CallRX86.java
index 99260a27..ac35d17b 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CallRX86.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CallRX86.java
@@ -1,9 +1,11 @@
package com.seaofnodes.simple.node.cpus.x86_64_v2;
-import com.seaofnodes.simple.*;
-import com.seaofnodes.simple.node.*;
-import com.seaofnodes.simple.type.Type;
-import com.seaofnodes.simple.type.TypeFunPtr;
+import com.seaofnodes.simple.SB;
+import com.seaofnodes.simple.Utils;
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.RegMask;
+import com.seaofnodes.simple.node.CallNode;
+import com.seaofnodes.simple.node.MachNode;
import java.io.ByteArrayOutputStream;
public class CallRX86 extends CallNode implements MachNode {
@@ -15,17 +17,24 @@ public class CallRX86 extends CallNode implements MachNode {
? x86_64_v2.WMASK // Function call target
: x86_64_v2.callInMask(tfp(),i); // Normal argument
}
- @Override public RegMask outregmap() { return x86_64_v2.RET_MASK; }
+ @Override public RegMask outregmap() { return null; }
// Encoding is appended into the byte array; size is returned
@Override public int encoding(ByteArrayOutputStream bytes) {
- throw Utils.TODO();
+ bytes.write(0xe8);
+ int beforeSize = bytes.size();
+ // address
+ //TODO: relocs
+ bytes.write(0x00);
+ return bytes.size() - beforeSize;
}
@Override public void asm(CodeGen code, SB sb) {
- sb.p(code.reg(fptr()));
+ sb.p(code.reg(fptr())).p(" ");
+ for( int i=0; i<nargs(); i++ )
+ sb.p(code.reg(arg(i))).p(" ");
+ sb.unchar(2);
}
@Override public String op() { return "callr"; }
-
}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CallX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CallX86.java
index b3e3bbe8..e2f68c1f 100644
--- 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
@@ -1,11 +1,13 @@
package com.seaofnodes.simple.node.cpus.x86_64_v2;
-import com.seaofnodes.simple.*;
-import com.seaofnodes.simple.node.*;
-import com.seaofnodes.simple.type.Type;
+import com.seaofnodes.simple.SB;
+import com.seaofnodes.simple.Utils;
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.RegMask;
+import com.seaofnodes.simple.node.CallNode;
+import com.seaofnodes.simple.node.MachNode;
import com.seaofnodes.simple.type.TypeFunPtr;
import java.io.ByteArrayOutputStream;
-import java.util.BitSet;
public class CallX86 extends CallNode implements MachNode {
final TypeFunPtr _tfp;
@@ -20,20 +22,30 @@ public class CallX86 extends CallNode implements MachNode {
@Override public String label() { return op(); }
@Override public RegMask regmap(int i) {
- return x86_64_v2.callInMask(tfp(),i); // Normal argument
+ return x86_64_v2.callInMask(_tfp,i); // Normal argument
}
- @Override public RegMask outregmap() { return x86_64_v2.RET_MASK; }
+ @Override public RegMask outregmap() { return null; }
+ @Override public int nargs() { return nIns()-2; } // Minus control, memory, fptr
@Override public String name() { return _name; }
@Override public TypeFunPtr tfp() { return _tfp; }
// Encoding is appended into the byte array; size is returned
@Override public int encoding(ByteArrayOutputStream bytes) {
- throw Utils.TODO();
+ // linker will fix this up
+ bytes.write(0xe8);
+ int beforeSize = bytes.size();
+ // TODO: relocs
+ // address
+ bytes.write(0x00);
+ return bytes.size() - beforeSize;
}
@Override public void asm(CodeGen code, SB sb) {
- sb.p(_name);
+ sb.p(_name).p(" ");
+ for( int i=0; i<nargs(); i++ )
+ sb.p(code.reg(in(i+2))).p(" ");
+ sb.unchar(2);
}
@Override public String op() { return "call"; }
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CastX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CastX86.java
new file mode 100644
index 00000000..b620b89c
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CastX86.java
@@ -0,0 +1,18 @@
+package com.seaofnodes.simple.node.cpus.x86_64_v2;
+
+import com.seaofnodes.simple.SB;
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.RegMask;
+import com.seaofnodes.simple.node.CastNode;
+import com.seaofnodes.simple.node.MachNode;
+import java.io.ByteArrayOutputStream;
+
+public class CastX86 extends CastNode implements MachNode {
+ CastX86( CastNode cast ) { super(cast); }
+ @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 int encoding(ByteArrayOutputStream bytes) { return 0; /*no encoding*/ }
+ @Override public void asm(CodeGen code, SB sb) { _t.print(sb.p(code.reg(in(1))).p(" isa ")); }
+ @Override public String op() { return label(); }
+}
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
new file mode 100644
index 00000000..e9b0dacd
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CmpFX86.java
@@ -0,0 +1,29 @@
+package com.seaofnodes.simple.node.cpus.x86_64_v2;
+
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.RegMask;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.type.TypeInteger;
+import java.io.ByteArrayOutputStream;
+
+public class CmpFX86 extends MachConcreteNode implements MachNode {
+ CmpFX86( Node cmp ) { super(cmp); }
+
+ @Override public RegMask regmap(int i) { assert i==1 || i==2; return x86_64_v2.XMASK; }
+ @Override public RegMask outregmap() { return x86_64_v2.FLAGS_MASK; }
+
+ // Encoding is appended into the byte array; size is returned
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+
+ // General form: "cmp 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)));
+ }
+
+ @Override public String op() { return "cmpf"; }
+}
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
index ea526f33..fcba397d 100644
--- 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
@@ -1,12 +1,14 @@
package com.seaofnodes.simple.node.cpus.x86_64_v2;
import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.RegMask;
import com.seaofnodes.simple.node.*;
import com.seaofnodes.simple.type.Type;
import com.seaofnodes.simple.type.TypeInteger;
import java.io.ByteArrayOutputStream;
-// Compare immediate. Sets flags.(RFLAGS)
+// Compare immediate. Sets flags.
public class CmpIX86 extends MachConcreteNode implements MachNode {
final int _imm;
final String _bop;
@@ -17,14 +19,22 @@ public class CmpIX86 extends MachConcreteNode implements MachNode {
_imm = (int)ti.value();
assert _imm == ti.value();
}
- CmpIX86( Node cmp, double ignore ) {
+ // Copy constructor does not set reverse edges in, because the basic block
+ // is changing from the original node - and it must be properly placed in a
+ // new block.
+ CmpIX86( CmpIX86 cmp ) {
super(cmp);
- _bop = "==";
- _imm = 0;
+ _bop = cmp._bop;
+ _imm = cmp._imm;
+ // While in(0) will be handled by the caller, all other edges must be
+ // handled in the constructor.
+ cmp.in(1)._outputs.push(this);
}
@Override public RegMask regmap(int i) { assert i==1; 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); }
// Encoding is appended into the byte array; size is returned
@Override public int encoding(ByteArrayOutputStream bytes) {
@@ -33,7 +43,7 @@ public class CmpIX86 extends MachConcreteNode implements MachNode {
@Override public void asm(CodeGen code, SB sb) {
String dst = code.reg(this);
- if( dst!="FLAGS" ) sb.p(dst).p(" = ");
+ 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
index 873ca9df..f9ace702 100644
--- 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
@@ -1,8 +1,8 @@
package com.seaofnodes.simple.node.cpus.x86_64_v2;
-import com.seaofnodes.simple.CodeGen;
-import com.seaofnodes.simple.RegMask;
import com.seaofnodes.simple.SB;
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.RegMask;
import com.seaofnodes.simple.node.*;
public class CmpMemX86 extends MemOpX86 {
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
index d8ebb12d..41ce33d2 100644
--- 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
@@ -1,11 +1,12 @@
package com.seaofnodes.simple.node.cpus.x86_64_v2;
import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.RegMask;
import com.seaofnodes.simple.node.*;
import com.seaofnodes.simple.type.TypeInteger;
import java.io.ByteArrayOutputStream;
-// Compare. Sets flags.(RFLAGS)
public class CmpX86 extends MachConcreteNode implements MachNode {
CmpX86( Node cmp ) { super(cmp); }
@@ -17,7 +18,7 @@ public class CmpX86 extends MachConcreteNode implements MachNode {
throw Utils.TODO();
}
- // General form: "cmp reg1, reg2"
+ // General form: "add dst += src"
@Override public void asm(CodeGen code, SB sb) {
String dst = code.reg(this);
if( dst!="FLAGS" ) sb.p(dst).p(" = ");
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
index d73bfd5b..f74b7348 100644
--- 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
@@ -1,6 +1,8 @@
package com.seaofnodes.simple.node.cpus.x86_64_v2;
import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.RegMask;
import com.seaofnodes.simple.node.*;
import com.seaofnodes.simple.type.TypeInteger;
import java.io.ByteArrayOutputStream;
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/DivX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/DivX86.java
index 1dc20017..54372629 100644
--- 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
@@ -1,6 +1,8 @@
package com.seaofnodes.simple.node.cpus.x86_64_v2;
import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.RegMask;
import com.seaofnodes.simple.node.*;
import com.seaofnodes.simple.type.TypeInteger;
import java.io.ByteArrayOutputStream;
@@ -10,12 +12,13 @@ public class DivX86 extends MachConcreteNode implements MachNode {
// Register mask allowed on input i.
@Override public RegMask regmap(int i) {
- assert i==1 || i==2;
- return x86_64_v2.RMASK; }
+ return (i==1) ? x86_64_v2.RAX_MASK : x86_64_v2.RMASK;
+ }
// Register mask allowed as a result. 0 for no register.
- @Override public RegMask outregmap() { return x86_64_v2.WMASK; }
- // Output is same register as input#1
- @Override public int twoAddress() { return 1; }
+ @Override public RegMask outregmap() { return x86_64_v2.RAX_MASK; }
+
+ // DIV encodes a CQO which sign extends RAX into RDX, killing RDX.
+ @Override public RegMask killmap() { return x86_64_v2.RDX_MASK; }
// Encoding is appended into the byte array; size is returned
@Override public int encoding(ByteArrayOutputStream bytes) {
@@ -28,4 +31,5 @@ public class DivX86 extends MachConcreteNode implements MachNode {
}
@Override public String op() { return "div"; }
+ @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
index 5e1190da..47f7149d 100644
--- 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
@@ -1,21 +1,23 @@
package com.seaofnodes.simple.node.cpus.x86_64_v2;
-import com.seaofnodes.simple.CodeGen;
-import com.seaofnodes.simple.RegMask;
import com.seaofnodes.simple.SB;
import com.seaofnodes.simple.Utils;
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.RegMask;
import com.seaofnodes.simple.node.ConstantNode;
import com.seaofnodes.simple.node.MachNode;
-
import java.io.ByteArrayOutputStream;
public class FltX86 extends ConstantNode implements MachNode {
FltX86(ConstantNode con ) { super(con); }
+ @Override public String op() { return "fld"; }
// Register mask allowed on input i. 0 for no register.
@Override public RegMask regmap(int i) { return null; }
// General int registers
@Override public RegMask outregmap() { return x86_64_v2.XMASK; }
+ @Override public boolean isClone() { return true; }
+ @Override public FltX86 copy() { return new FltX86(this); }
// Encoding is appended into the byte array; size is returned
@Override public int encoding(ByteArrayOutputStream bytes) {
@@ -30,7 +32,4 @@ public class FltX86 extends ConstantNode implements MachNode {
_con.print(sb.p(code.reg(this)).p(" #"));
}
- @Override public String op() {
- return "fld"; // Some fancier encoding
- }
}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/FunX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/FunX86.java
index 8b0f016a..7346ae05 100644
--- 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
@@ -1,6 +1,7 @@
package com.seaofnodes.simple.node.cpus.x86_64_v2;
-import com.seaofnodes.simple.RegMask;
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.RegMask;
import com.seaofnodes.simple.Utils;
import com.seaofnodes.simple.node.FunNode;
import com.seaofnodes.simple.node.MachNode;
@@ -16,6 +17,6 @@ public class FunX86 extends FunNode implements MachNode {
// Encoding is appended into the byte array; size is returned
@Override public int encoding(ByteArrayOutputStream bytes) {
- throw Utils.TODO();
+ return 0;
}
}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/I2f8X86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/I2f8X86.java
index 983f817d..d99c92a8 100644
--- 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
@@ -1,6 +1,8 @@
package com.seaofnodes.simple.node.cpus.x86_64_v2;
import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.RegMask;
import com.seaofnodes.simple.node.*;
import com.seaofnodes.simple.type.TypeInteger;
import java.io.ByteArrayOutputStream;
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/IntX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/IntX86.java
index 566a7293..fcd39e3f 100644
--- 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
@@ -1,8 +1,11 @@
package com.seaofnodes.simple.node.cpus.x86_64_v2;
import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.RegMask;
import com.seaofnodes.simple.node.ConstantNode;
import com.seaofnodes.simple.node.MachNode;
+import com.seaofnodes.simple.node.Node;
import com.seaofnodes.simple.type.Type;
import com.seaofnodes.simple.type.TypeInteger;
@@ -10,9 +13,10 @@ import java.io.ByteArrayOutputStream;
// Integer constants
public class IntX86 extends ConstantNode implements MachNode {
+ IntX86( ConstantNode con ) { super(con); }
- IntX86( ConstantNode con ) {
- super(con);
+ @Override public String op() {
+ return _con == Type.NIL || _con == TypeInteger.ZERO ? "xor" : "ldi";
}
// Register mask allowed on input i. 0 for no register.
@@ -20,6 +24,14 @@ public class IntX86 extends ConstantNode implements MachNode {
// General int registers
@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 boolean isClone() { return true; }
+ @Override public Node copy() { return new IntX86(this); }
+
// Encoding is appended into the byte array; size is returned
@Override public int encoding(ByteArrayOutputStream bytes) {
throw Utils.TODO();
@@ -34,12 +46,6 @@ public class IntX86 extends ConstantNode implements MachNode {
if( _con == Type.NIL || _con == TypeInteger.ZERO )
sb.p(reg).p(",").p(reg);
else
- _con.print(sb.p(reg).p(" #"));
- }
-
- @Override public String op() {
- if( _con == Type.NIL || _con == TypeInteger.ZERO )
- return "xor";
- return "ldi"; // Some fancier encoding
+ _con.print(sb.p(reg).p(" = #"));
}
}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/JmpX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/JmpX86.java
index 2262e847..173f9ee7 100644
--- 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
@@ -1,6 +1,8 @@
package com.seaofnodes.simple.node.cpus.x86_64_v2;
import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.RegMask;
import com.seaofnodes.simple.node.*;
import com.seaofnodes.simple.type.Type;
import com.seaofnodes.simple.type.TypeInteger;
@@ -20,7 +22,7 @@ public class JmpX86 extends IfNode implements MachNode {
Node set = in(1);
Node cmp = set.in(1);
// Bypass an expected Set and just reference the cmp directly
- if( set instanceof SetX86 && (cmp instanceof CmpX86 || cmp instanceof CmpIX86 || cmp instanceof CmpMemX86) )
+ if( set instanceof SetX86 && (cmp instanceof CmpX86 || cmp instanceof CmpIX86 || cmp instanceof CmpMemX86 || cmp instanceof CmpFX86) )
_inputs.set(1,cmp);
else
throw Utils.TODO();
@@ -35,7 +37,7 @@ public class JmpX86 extends IfNode implements MachNode {
@Override public void asm(CodeGen code, SB sb) {
String src = code.reg(in(1));
- if( src!="FLAGS" ) sb.p(src);
+ if( src!="flags" ) sb.p(src);
}
@Override public String op() { return "j"+_bop; }
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
index a49b8d7f..229c0151 100644
--- 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
@@ -1,6 +1,8 @@
package com.seaofnodes.simple.node.cpus.x86_64_v2;
import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.RegMask;
import com.seaofnodes.simple.node.*;
import com.seaofnodes.simple.type.TypeInteger;
import java.io.ByteArrayOutputStream;
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/LoadX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/LoadX86.java
index 02a9a4fe..893772a6 100644
--- 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
@@ -1,8 +1,8 @@
package com.seaofnodes.simple.node.cpus.x86_64_v2;
-import com.seaofnodes.simple.CodeGen;
-import com.seaofnodes.simple.RegMask;
import com.seaofnodes.simple.SB;
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.RegMask;
import com.seaofnodes.simple.node.LoadNode;
import com.seaofnodes.simple.node.Node;
@@ -14,7 +14,7 @@ public class LoadX86 extends MemOpX86 {
@Override public String op() { return "ld"+_sz; }
// Register mask allowed as a result. 0 for no register.
- @Override public RegMask outregmap() { return x86_64_v2.WMASK; }
+ @Override public RegMask outregmap() { return x86_64_v2.MEM_MASK; }
// General form: "ldN dst,[base + idx<<2 + 12]"
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
index a6cdbd05..6d39a2b4 100644
--- 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
@@ -1,8 +1,8 @@
package com.seaofnodes.simple.node.cpus.x86_64_v2;
-import com.seaofnodes.simple.CodeGen;
-import com.seaofnodes.simple.RegMask;
import com.seaofnodes.simple.SB;
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.RegMask;
import com.seaofnodes.simple.node.*;
public class MemAddX86 extends MemOpX86 {
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
index b94ea5ae..dee4afd4 100644
--- 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
@@ -1,6 +1,8 @@
package com.seaofnodes.simple.node.cpus.x86_64_v2;
import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.RegMask;
import com.seaofnodes.simple.node.*;
import com.seaofnodes.simple.type.*;
import java.io.ByteArrayOutputStream;
@@ -49,9 +51,11 @@ public abstract class MemOpX86 extends MemOpNode implements MachNode {
}
Node idx() { return in(3); }
- Node val() { return in(4); }
+ 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 StringBuilder _printMach(StringBuilder sb, BitSet visited) {
+ return sb.append(".").append(_name);
+ }
@Override public String label() { return op(); }
@Override public Type compute() { throw Utils.TODO(); }
@@ -59,14 +63,12 @@ public abstract class MemOpX86 extends MemOpNode implements MachNode {
// Register mask allowed on input i.
@Override public RegMask regmap(int i) {
- if( i==1 ) return null; // Memory
- if( i==2 ) return x86_64_v2.RMASK; // base
- if( i==3 ) return x86_64_v2.RMASK; // index
- if( i==4 ) return x86_64_v2.RMASK; // value
+ 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 x86_64_v2.MEM_MASK; // value in GPR or XMM
throw Utils.TODO();
}
- // Register mask allowed as a result. 0 for no register.
- @Override public RegMask outregmap() { throw Utils.TODO(); }
@Override public int encoding(ByteArrayOutputStream bytes) { throw Utils.TODO(); }
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MulFX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MulFX86.java
index aef813e1..9e996130 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MulFX86.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MulFX86.java
@@ -1,6 +1,8 @@
package com.seaofnodes.simple.node.cpus.x86_64_v2;
import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.RegMask;
import com.seaofnodes.simple.node.*;
import com.seaofnodes.simple.type.TypeFloat;
import com.seaofnodes.simple.type.TypeInteger;
@@ -15,6 +17,8 @@ public class MulFX86 extends MachConcreteNode implements MachNode {
@Override public RegMask outregmap() { return x86_64_v2.XMASK; }
// Output is same register as input#1
@Override public int twoAddress() { return 1; }
+ // Ok to swap arguments
+ @Override public boolean commutes() { return true; }
// Encoding is appended into the byte array; size is returned
@Override public int encoding(ByteArrayOutputStream bytes) {
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MulIX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MulIX86.java
index e9eb17fc..b41a5720 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MulIX86.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MulIX86.java
@@ -1,7 +1,12 @@
package com.seaofnodes.simple.node.cpus.x86_64_v2;
-import com.seaofnodes.simple.*;
-import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.SB;
+import com.seaofnodes.simple.Utils;
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.RegMask;
+import com.seaofnodes.simple.node.MachConcreteNode;
+import com.seaofnodes.simple.node.MachNode;
+import com.seaofnodes.simple.node.Node;
import com.seaofnodes.simple.type.TypeInteger;
import java.io.ByteArrayOutputStream;
@@ -12,7 +17,7 @@ public class MulIX86 extends MachConcreteNode implements MachNode {
// Register mask allowed on input i.
// This is the normal calling convention
@Override public RegMask regmap(int i) {
- assert i==1;
+ // assert i==1;
return x86_64_v2.WMASK; }
// Register mask allowed as a result. 0 for no register.
@Override public RegMask outregmap() { return x86_64_v2.WMASK; }
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MulX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MulX86.java
index 15fd9496..d880208e 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MulX86.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MulX86.java
@@ -1,12 +1,13 @@
package com.seaofnodes.simple.node.cpus.x86_64_v2;
import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.RegMask;
import com.seaofnodes.simple.node.*;
import com.seaofnodes.simple.type.TypeInteger;
import java.io.ByteArrayOutputStream;
public class MulX86 extends MachConcreteNode implements MachNode {
-
MulX86( Node mul ) { super(mul); }
// Register mask allowed on input i.
@@ -15,6 +16,8 @@ public class MulX86 extends MachConcreteNode implements MachNode {
@Override public RegMask outregmap() { return x86_64_v2.WMASK; }
// Output is same register as input#1
@Override public int twoAddress() { return 1; }
+ // Ok to swap arguments
+ @Override public boolean commutes() { return true; }
// Encoding is appended into the byte array; size is returned
@Override public int encoding(ByteArrayOutputStream bytes) {
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/NewX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/NewX86.java
index f9778845..cd47c64b 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/NewX86.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/NewX86.java
@@ -1,30 +1,19 @@
package com.seaofnodes.simple.node.cpus.x86_64_v2;
import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.RegMask;
import com.seaofnodes.simple.node.*;
import java.io.ByteArrayOutputStream;
public class NewX86 extends NewNode implements MachNode {
-
// A pre-zeroed chunk of memory.
- NewX86( NewNode nnn ) { super(nnn); }
-
- // Register mask allowed on input i, the size
- @Override public RegMask regmap(int i) {
- // Size
- if( i==1 ) return x86_64_v2.RDI_MASK;
- // All the memory alias edges
- return null;
- }
- @Override public RegMask outregmap() { throw Utils.TODO(); }
-
- // Register mask allowed as a result. Pointer result in standard calling
- // convention.
- @Override public RegMask outregmap(int i) {
- if( i == 1 ) return x86_64_v2.RET_MASK;
- // All the memory aliases edges
- return null;
- }
+ NewX86( NewNode nnn ) { super(nnn); }
+ // Size and pointer result in standard calling convention; null for all the
+ // memory aliases edges
+ @Override public RegMask regmap(int i) { return i == 1 ? x86_64_v2.RDI_MASK : null; }
+ @Override public RegMask outregmap(int i) { return i == 1 ? x86_64_v2.RAX_MASK : null; }
+ @Override public RegMask outregmap() { return null; }
// Encoding is appended into the byte array; size is returned
@Override public int encoding(ByteArrayOutputStream bytes) {
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/NotX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/NotX86.java
new file mode 100644
index 00000000..a4b82de8
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/NotX86.java
@@ -0,0 +1,26 @@
+package com.seaofnodes.simple.node.cpus.x86_64_v2;
+
+import com.seaofnodes.simple.SB;
+import com.seaofnodes.simple.Utils;
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.LRG;
+import com.seaofnodes.simple.codegen.RegMask;
+import com.seaofnodes.simple.node.*;
+import java.io.ByteArrayOutputStream;
+
+public class NotX86 extends MachConcreteNode implements MachNode {
+ NotX86(NotNode not) {super(not);}
+ @Override public RegMask regmap(int i) { return x86_64_v2.RMASK; }
+ @Override public RegMask outregmap() { return x86_64_v2.RMASK; }
+
+ @Override public int twoAddress( ) { return 0; }
+
+ // NotNode(BoolNode) = Inverted BoolNode(1)
+ // NotNode(NotNode(BoolNode)) = BoolNode(2)
+ // anything else to test & setz(3)
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+ @Override public void asm(CodeGen code, SB sb) { sb.p(code.reg(this)); }
+ @Override public String op() { return "not"; }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/OrIX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/OrIX86.java
index d3940838..949ac6c2 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/OrIX86.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/OrIX86.java
@@ -1,14 +1,13 @@
package com.seaofnodes.simple.node.cpus.x86_64_v2;
-import com.seaofnodes.simple.CodeGen;
-import com.seaofnodes.simple.RegMask;
import com.seaofnodes.simple.SB;
import com.seaofnodes.simple.Utils;
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.RegMask;
import com.seaofnodes.simple.node.MachConcreteNode;
import com.seaofnodes.simple.node.MachNode;
import com.seaofnodes.simple.node.Node;
import com.seaofnodes.simple.type.TypeInteger;
-
import java.io.ByteArrayOutputStream;
public class OrIX86 extends MachConcreteNode implements MachNode {
@@ -19,7 +18,7 @@ public class OrIX86 extends MachConcreteNode implements MachNode {
// Register mask allowed on input i.
// This is the normal calling convention
@Override public RegMask regmap(int i) {
- assert i==1;
+ //assert i==1;
return x86_64_v2.WMASK; }
// Register mask allowed as a result. 0 for no register.
@Override public RegMask outregmap() { return x86_64_v2.WMASK; }
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/OrX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/OrX86.java
index 7c25bdae..6d99fe3b 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/OrX86.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/OrX86.java
@@ -1,6 +1,8 @@
package com.seaofnodes.simple.node.cpus.x86_64_v2;
import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.RegMask;
import com.seaofnodes.simple.node.*;
import java.io.ByteArrayOutputStream;
@@ -24,7 +26,7 @@ public class OrX86 extends MachConcreteNode implements MachNode{
}
// General form
- // General form: "and dst & #imm"
+ // General form: "or dst | src"
@Override public void asm(CodeGen code, SB sb) {
sb.p(code.reg(this)).p(" = ").p(code.reg(in(1)));
}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ParmX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ParmX86.java
index 0fcc922d..69ebb74e 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ParmX86.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ParmX86.java
@@ -1,23 +1,27 @@
package com.seaofnodes.simple.node.cpus.x86_64_v2;
-import com.seaofnodes.simple.RegMask;
-import com.seaofnodes.simple.node.ParmNode;
+import com.seaofnodes.simple.SB;
+import com.seaofnodes.simple.Utils;
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.RegMask;
import com.seaofnodes.simple.node.MachNode;
+import com.seaofnodes.simple.node.ParmNode;
import java.io.ByteArrayOutputStream;
public class ParmX86 extends ParmNode implements MachNode {
final RegMask _rmask;
ParmX86( ParmNode parm ) {
super(parm);
- // default to int
_rmask = x86_64_v2.callInMask(fun().sig(),_idx);
}
// Register mask allowed on input i. 0 for no register.
- @Override public RegMask regmap(int i) { return null; }
+ @Override public RegMask regmap(int i) { return _rmask; }
// Register mask allowed as a result. Calling convention register
@Override public RegMask outregmap() { return _rmask; }
// Encoding is appended into the byte array. Returns size
- @Override public int encoding(ByteArrayOutputStream bytes) { return 0; }
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ return 0;
+ }
}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ProjX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ProjX86.java
index 77fd7d34..7ac014e6 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ProjX86.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ProjX86.java
@@ -1,14 +1,12 @@
package com.seaofnodes.simple.node.cpus.x86_64_v2;
-import com.seaofnodes.simple.RegMask;
-import com.seaofnodes.simple.SB;
import com.seaofnodes.simple.Utils;
-import com.seaofnodes.simple.node.ProjNode;
+import com.seaofnodes.simple.codegen.RegMask;
import com.seaofnodes.simple.node.MachNode;
+import com.seaofnodes.simple.node.ProjNode;
import java.io.ByteArrayOutputStream;
public class ProjX86 extends ProjNode implements MachNode {
-
ProjX86( ProjNode p ) { super(p); }
// Register mask allowed on input i. 0 for no register.
@@ -20,6 +18,6 @@ public class ProjX86 extends ProjNode implements MachNode {
// Encoding is appended into the byte array; size is returned
@Override public int encoding(ByteArrayOutputStream bytes) {
- throw Utils.TODO();
+ return 0;
}
}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/RetX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/RetX86.java
index 21db209c..433f64cc 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/RetX86.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/RetX86.java
@@ -1,9 +1,12 @@
package com.seaofnodes.simple.node.cpus.x86_64_v2;
import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.RegMask;
import com.seaofnodes.simple.node.FunNode;
import com.seaofnodes.simple.node.ReturnNode;
import com.seaofnodes.simple.node.MachNode;
+import com.seaofnodes.simple.type.TypeFloat;
import java.io.ByteArrayOutputStream;
// Return
@@ -19,29 +22,27 @@ public class RetX86 extends ReturnNode implements MachNode {
// Register mask allowed on input i.
// This is the normal calling convention
- @Override public RegMask regmap(int i) {
- return switch( i ) {
- case 0 -> null;
- case 1 -> null;
- case 2 -> x86_64_v2.RET_MASK;
- case 3 -> null; // RPC is always on stack
- default -> throw Utils.TODO();
- };
- }
+ @Override public RegMask regmap(int i) { return x86_64_v2.retMask(_fun.sig(),i); }
// Register mask allowed as a result. 0 for no register.
@Override public RegMask outregmap() { return null; }
// Encoding is appended into the byte array; size is returned
@Override public int encoding(ByteArrayOutputStream bytes) {
- throw Utils.TODO();
+ // Without imm form
+ // Near return
+ int beforeSize = bytes.size();
+ bytes.write(0xC3); // opcode
+
+ return bytes.size() - beforeSize;
}
- // Human-readable form appended to the SB. Things like the encoding,
- // indentation, leading address or block labels not printed here.
- // Just something like "ld4\tR17=[R18+12] // Load array base".
- // General form: "op\tdst=src+src"
@Override public void asm(CodeGen code, SB sb) {
- sb.p(code.reg(in(2)));
+ // Post code-gen, just print the "ret"
+ if( code._phase.ordinal() <= CodeGen.Phase.RegAlloc.ordinal() )
+ // Prints return reg (either RAX or XMM0), RPC (always [rsp-4]) and
+ // then the callee-save registers.
+ for( int i=2; i<nIns(); i++ )
+ sb.p(code.reg(in(i))).p(" ");
}
@Override public String op() { return "ret"; }
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SarIX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SarIX86.java
index f5337ee8..9f942cc6 100644
--- 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
@@ -1,6 +1,8 @@
package com.seaofnodes.simple.node.cpus.x86_64_v2;
import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.RegMask;
import com.seaofnodes.simple.node.*;
import com.seaofnodes.simple.type.TypeInteger;
import java.io.ByteArrayOutputStream;
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SarX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SarX86.java
index cb0b18bf..ee7a2f56 100644
--- 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
@@ -1,6 +1,8 @@
package com.seaofnodes.simple.node.cpus.x86_64_v2;
import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.RegMask;
import com.seaofnodes.simple.node.*;
import com.seaofnodes.simple.type.Type;
import com.seaofnodes.simple.type.TypeInteger;
@@ -8,19 +10,18 @@ import java.io.ByteArrayOutputStream;
import java.util.BitSet;
import java.lang.StringBuilder;
-public class SarX86 extends MachConcreteNode implements MachNode{
- SarX86(Node sar) {super(sar);}
+
+public class SarX86 extends MachConcreteNode implements MachNode {
+ SarX86(Node sar) { super(sar); }
// Register mask allowed on input i.
// This is the normal calling convention
-
- // CL register must be used for input(2)
@Override public RegMask regmap(int i) {
if(i == 1) return x86_64_v2.WMASK;
if(i == 2) return x86_64_v2.RCX_MASK;
throw Utils.TODO();
}
-
+ // Register mask allowed as a result. 0 for no register.
@Override public RegMask outregmap() { return x86_64_v2.WMASK; }
// Output is same register as input#1
@@ -32,11 +33,9 @@ public class SarX86 extends MachConcreteNode implements MachNode{
}
// General form
- // General form: "sar dst >> src"
@Override public void asm(CodeGen code, SB sb) {
- sb.p(code.reg(this)).p(" = ").p(code.reg(in(1)));
+ sb.p(code.reg(this)).p(" >> ").p(code.reg(in(1)));
}
@Override public String op() { return "sar"; }
-
}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SetX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SetX86.java
index 25367e03..53237538 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SetX86.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SetX86.java
@@ -1,6 +1,8 @@
package com.seaofnodes.simple.node.cpus.x86_64_v2;
import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.RegMask;
import com.seaofnodes.simple.node.*;
import com.seaofnodes.simple.type.Type;
import com.seaofnodes.simple.type.TypeInteger;
@@ -31,7 +33,7 @@ public class SetX86 extends MachConcreteNode implements MachNode {
@Override public void asm(CodeGen code, SB sb) {
sb.p(code.reg(this));
String src = code.reg(in(1));
- if( src!="FLAGS" ) sb.p(" = ").p(src);
+ if( src!="flags" ) sb.p(" = ").p(src);
}
@Override public String op() { return "set"+_bop; }
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShlIX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShlIX86.java
index 61e0d262..c18007cd 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShlIX86.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShlIX86.java
@@ -1,6 +1,8 @@
package com.seaofnodes.simple.node.cpus.x86_64_v2;
import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.RegMask;
import com.seaofnodes.simple.node.*;
import com.seaofnodes.simple.type.Type;
import com.seaofnodes.simple.type.TypeInteger;
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShlX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShlX86.java
index b95f9f46..a3de5347 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShlX86.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShlX86.java
@@ -1,13 +1,13 @@
package com.seaofnodes.simple.node.cpus.x86_64_v2;
import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.RegMask;
import com.seaofnodes.simple.node.*;
import java.io.ByteArrayOutputStream;
public class ShlX86 extends MachConcreteNode implements MachNode {
- ShlX86(Node shl) {
- super(shl);
- };
+ ShlX86(Node shl) { super(shl); };
// Register mask allowed on input i.
// This is the normal calling convention
@@ -32,7 +32,7 @@ public class ShlX86 extends MachConcreteNode implements MachNode {
// General form
// General form: "shl dst << src"
@Override public void asm(CodeGen code, SB sb) {
- sb.p(code.reg(this)).p(" = ").p(code.reg(in(1)));
+ sb.p(code.reg(this)).p(" << ").p(code.reg(in(1)));
}
@Override public String op() { return "shl"; }
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShrIX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShrIX86.java
index 80115ff2..d17ee734 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShrIX86.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShrIX86.java
@@ -1,6 +1,8 @@
package com.seaofnodes.simple.node.cpus.x86_64_v2;
import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.RegMask;
import com.seaofnodes.simple.node.*;
import com.seaofnodes.simple.type.Type;
import com.seaofnodes.simple.type.TypeInteger;
@@ -8,7 +10,6 @@ import java.io.ByteArrayOutputStream;
import java.util.BitSet;
import java.lang.StringBuilder;
-// Logical Right Shift
public class ShrIX86 extends MachConcreteNode implements MachNode{
final TypeInteger _ti;
ShrIX86(Node shr, TypeInteger ti) {super(shr); _inputs.pop(); _ti = ti;}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShrX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShrX86.java
index 43fc730b..757d9b8e 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShrX86.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShrX86.java
@@ -1,6 +1,8 @@
package com.seaofnodes.simple.node.cpus.x86_64_v2;
import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.RegMask;
import com.seaofnodes.simple.node.*;
import java.io.ByteArrayOutputStream;
@@ -31,7 +33,7 @@ public class ShrX86 extends MachConcreteNode implements MachNode {
// General form
// General form: "shr dst >>> src"
@Override public void asm(CodeGen code, SB sb) {
- sb.p(code.reg(this)).p(" = ").p(code.reg(in(1)));
+ sb.p(code.reg(this)).p(" >>> ").p(code.reg(in(1)));
}
@Override public String op() { return "shr"; }
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SplitX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SplitX86.java
new file mode 100644
index 00000000..ea283a87
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SplitX86.java
@@ -0,0 +1,21 @@
+package com.seaofnodes.simple.node.cpus.x86_64_v2;
+
+import com.seaofnodes.simple.Utils;
+import com.seaofnodes.simple.codegen.RegMask;
+import com.seaofnodes.simple.node.Node;
+import com.seaofnodes.simple.node.SplitNode;
+import java.io.ByteArrayOutputStream;
+
+public class SplitX86 extends SplitNode {
+ SplitX86( String kind, byte round ) { super(kind,round, new Node[2]); }
+
+ // Register mask allowed on input i.
+ @Override public RegMask regmap(int i) { return x86_64_v2.SPLIT_MASK; }
+ // Register mask allowed as a result. 0 for no register.
+ @Override public RegMask outregmap() { return x86_64_v2.SPLIT_MASK; }
+
+ // Encoding is appended into the byte array; size is returned
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/StoreX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/StoreX86.java
index 734f39de..024ef904 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/StoreX86.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/StoreX86.java
@@ -1,8 +1,8 @@
package com.seaofnodes.simple.node.cpus.x86_64_v2;
-import com.seaofnodes.simple.CodeGen;
-import com.seaofnodes.simple.RegMask;
import com.seaofnodes.simple.SB;
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.RegMask;
import com.seaofnodes.simple.node.Node;
import com.seaofnodes.simple.node.StoreNode;
import java.util.BitSet;
@@ -15,7 +15,11 @@ public class StoreX86 extends MemOpX86 {
@Override public String op() { return "st"+_sz; }
@Override public StringBuilder _printMach(StringBuilder sb, BitSet visited) {
- return sb.append(".").append(_name).append("=").append(val()==null ? _imm : val()).append(";");
+ 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.
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SubFX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SubFX86.java
index ed19ad79..ceaf0636 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SubFX86.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SubFX86.java
@@ -1,6 +1,8 @@
package com.seaofnodes.simple.node.cpus.x86_64_v2;
import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.RegMask;
import com.seaofnodes.simple.node.*;
import java.io.ByteArrayOutputStream;
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SubX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SubX86.java
index cac59d3b..da44a838 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SubX86.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SubX86.java
@@ -1,6 +1,8 @@
package com.seaofnodes.simple.node.cpus.x86_64_v2;
import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.RegMask;
import com.seaofnodes.simple.node.*;
import com.seaofnodes.simple.type.TypeInteger;
import java.io.ByteArrayOutputStream;
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/TFPX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/TFPX86.java
index c7edfcc4..2b4de2ca 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/TFPX86.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/TFPX86.java
@@ -1,6 +1,8 @@
package com.seaofnodes.simple.node.cpus.x86_64_v2;
import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.RegMask;
import com.seaofnodes.simple.node.ConstantNode;
import com.seaofnodes.simple.node.MachNode;
import com.seaofnodes.simple.type.Type;
@@ -11,10 +13,13 @@ import java.io.ByteArrayOutputStream;
// Function constants
public class TFPX86 extends ConstantNode implements MachNode {
TFPX86( ConstantNode con ) { super(con); }
+ @Override public String op() { return _con == Type.NIL ? "xor" : "ldx"; }
// Register mask allowed on input i. 0 for no register.
@Override public RegMask regmap(int i) { return null; }
// General int registers
@Override public RegMask outregmap() { return x86_64_v2.WMASK; }
+ @Override public boolean isClone() { return true; }
+ @Override public TFPX86 copy() { return new TFPX86(this); }
// Encoding is appended into the byte array; size is returned
@Override public int encoding(ByteArrayOutputStream bytes) {
@@ -32,10 +37,4 @@ public class TFPX86 extends ConstantNode implements MachNode {
else
_con.print(sb.p(reg).p(" #"));
}
-
- @Override public String op() {
- if( _con == Type.NIL )
- return "xor";
- return "ldx"; // Some fancier encoding
- }
}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/XorIX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/XorIX86.java
index d71a49dc..47c6be73 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/XorIX86.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/XorIX86.java
@@ -1,17 +1,16 @@
package com.seaofnodes.simple.node.cpus.x86_64_v2;
-import com.seaofnodes.simple.CodeGen;
-import com.seaofnodes.simple.RegMask;
import com.seaofnodes.simple.SB;
import com.seaofnodes.simple.Utils;
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.RegMask;
import com.seaofnodes.simple.node.MachConcreteNode;
import com.seaofnodes.simple.node.MachNode;
import com.seaofnodes.simple.node.Node;
import com.seaofnodes.simple.type.TypeInteger;
-
import java.io.ByteArrayOutputStream;
-public class XorIX86 extends MachConcreteNode implements MachNode {
+public class XorIX86 extends MachConcreteNode implements MachNode {
final TypeInteger _ti;
XorIX86(Node xor, TypeInteger ti) {super(xor); _inputs.pop(); _ti = ti;}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/XorX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/XorX86.java
new file mode 100644
index 00000000..3322d951
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/XorX86.java
@@ -0,0 +1,36 @@
+package com.seaofnodes.simple.node.cpus.x86_64_v2;
+
+import com.seaofnodes.simple.SB;
+import com.seaofnodes.simple.Utils;
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.LRG;
+import com.seaofnodes.simple.codegen.RegMask;
+import com.seaofnodes.simple.node.MachConcreteNode;
+import com.seaofnodes.simple.node.MachNode;
+import com.seaofnodes.simple.node.Node;
+import com.seaofnodes.simple.type.TypeInteger;
+import java.io.ByteArrayOutputStream;
+
+public class XorX86 extends MachConcreteNode implements MachNode{
+ XorX86(Node xor) {super(xor);}
+
+ @Override public RegMask regmap(int i) { return x86_64_v2.WMASK; }
+ @Override public RegMask outregmap() { return x86_64_v2.WMASK; }
+
+ // Output is same register as input#1
+ @Override public int twoAddress() { return 1; }
+
+ // Encoding is appended into the byte array; size is returned
+ @Override public int encoding(ByteArrayOutputStream bytes) {
+ throw Utils.TODO();
+ }
+
+ // General form
+ // General form: "xor ^ reg
+ @Override public void asm(CodeGen code, SB sb) {
+ sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" ^ ").p(code.reg(in(2)));
+ }
+
+ @Override public String op() { return "xor"; }
+
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/x86_64_v2.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/x86_64_v2.java
index ddb2dcd6..aacd2352 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/x86_64_v2.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/x86_64_v2.java
@@ -1,9 +1,10 @@
package com.seaofnodes.simple.node.cpus.x86_64_v2;
-import com.seaofnodes.simple.CodeGen;
-import com.seaofnodes.simple.Machine;
-import com.seaofnodes.simple.RegMask;
import com.seaofnodes.simple.Utils;
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.LRG;
+import com.seaofnodes.simple.codegen.Machine;
+import com.seaofnodes.simple.codegen.RegMask;
import com.seaofnodes.simple.node.*;
import com.seaofnodes.simple.type.*;
@@ -11,119 +12,66 @@ public class x86_64_v2 extends Machine {
// X86-64 V2. Includes e.g. SSE4.2 and POPCNT.
@Override public String name() { return "x86_64_v2"; }
- public static int RAX = 0, RCX = 1, RDX = 2, RBX = 3, RSP = 4, RBP = 5, RSI = 6, RDI = 7;
- public static int R08 = 8, R09 = 9, R10 = 10, R11 = 11, R12 = 12, R13 = 13, R14 = 14, R15 = 15;
- public static int FLAGS = 32;
+ static final int RAX = 0, RCX = 1, RDX = 2, RBX = 3, RSP = 4, RBP = 5, RSI = 6, RDI = 7;
+ static final int R08 = 8, R09 = 9, R10 = 10, R11 = 11, R12 = 12, R13 = 13, R14 = 14, R15 = 15;
- public static int XMM0 = 16, XMM1 = 17, XMM2 = 18, XMM3 = 19, XMM4 = 20, XMM5 = 21, XMM6 = 22, XMM7 = 23;
- public static int XMM8 = 24, XMM9 = 25, XMM10 = 26, XMM11 = 27, XMM12 = 28, XMM13 = 29, XMM14 = 30, XMM15 = 31;
+ static final int XMM0 = 16, XMM1 = 17, XMM2 = 18, XMM3 = 19, XMM4 = 20, XMM5 = 21, XMM6 = 22, XMM7 = 23;
+ static final int XMM8 = 24, XMM9 = 25, XMM10 = 26, XMM11 = 27, XMM12 = 28, XMM13 = 29, XMM14 = 30, XMM15 = 31;
+ static final int FLAGS = 32;
+ static final int MAX_REG = 33;
+ static final int RPC = 33;
// General purpose register mask: pointers and ints, not floats
- public static RegMask RMASK = new RegMask(0b1111111111111111);
+ static final long RD_BITS = 0b1111111111111111L; // All the GPRs
+ static RegMask RMASK = new RegMask(RD_BITS);
// No RSP in the *write* general set.
- public static RegMask WMASK = new RegMask(0b1111111111101111);
+ static final long WR_BITS = 0b1111111111101111L; // All the GPRs minus RSP
+ static RegMask WMASK = new RegMask(WR_BITS);
// Xmm register mask
- public static RegMask XMASK = new RegMask( 0b1111111111111111L << XMM0);
-
- public static RegMask FLAGS_MASK = new RegMask(1L<<FLAGS);
-
- // Return single int/ptr register
- public static RegMask RET_MASK = new RegMask(1<<RAX);
- public static RegMask RET_FMASK = new RegMask(1<<XMM0);
-
- public static RegMask RDI_MASK = new RegMask(1L<<RDI);
- public static RegMask RCX_MASK = new RegMask(1L<<RCX);
- public static RegMask RDX_MASK = new RegMask(1L<<RDX);
- public static RegMask R08_MASK = new RegMask(1L<<R08);
- public static RegMask R09_MASK = new RegMask(1L<<R09);
- public static RegMask RSI_MASK = new RegMask(1L<<RSI);
-
- public static RegMask XMM0_MASK = new RegMask(1L<<XMM0);
- public static RegMask XMM1_MASK = new RegMask(1L<<XMM1);
- public static RegMask XMM2_MASK = new RegMask(1L<<XMM2);
- public static RegMask XMM3_MASK = new RegMask(1L<<XMM3);
- public static RegMask XMM4_MASK = new RegMask(1L<<XMM4);
- public static RegMask XMM5_MASK = new RegMask(1L<<XMM5);
- public static RegMask XMM6_MASK = new RegMask(1L<<XMM6);
- public static RegMask XMM7_MASK = new RegMask(1L<<XMM7);
- public static RegMask[] XMMS = new RegMask[]{
- XMM0_MASK, XMM1_MASK, XMM2_MASK, XMM3_MASK,
- XMM4_MASK, XMM5_MASK, XMM6_MASK, XMM7_MASK,
- };
+ static final long FP_BITS = 0b1111111111111111L << XMM0; // All the XMMs
+ static final RegMask XMASK = new RegMask(FP_BITS);
+ static final RegMask FLAGS_MASK = new RegMask(FLAGS);
+ static final RegMask RPC_MASK = new RegMask(RPC);
+
+ 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);
- // Calling conv metadata
- public int GPR_COUNT_CONV_WIN64 = 4; // RCX, RDX, R9, R9
- public int XMM_COUNT_CONV_WIN64 = 4; // XMM0L, XMM1L, XMM2L, XMM3L
+ static final RegMask RAX_MASK = new RegMask(RAX);
+ static final RegMask RCX_MASK = new RegMask(RCX);
+ static final RegMask RDX_MASK = new RegMask(RDX);
+ static final RegMask RDI_MASK = new RegMask(RDI);
+ static final RegMask R08_MASK = new RegMask(R08);
+ static final RegMask R09_MASK = new RegMask(R09);
+ static final RegMask RSI_MASK = new RegMask(RSI);
+
+ static final RegMask XMM0_MASK = new RegMask(XMM0);
- public int GPR_COUNT_CONV_SYSTEM_V = 6; // RDI, RSI, RDX, RCX, R8, R9
- public int XMM_COUNT_CONV_SYSTEM_V = 4; // XMM0, XMM1, XMM2, XMM3 ....
// Human-readable name for a register number, e.g. "RAX".
// Hard crash for bad register number, fix yer bugs!
- public static final String[] REGS = new String[] {
+ 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",
+ "flags","[rsp-4]",
};
@Override public String reg( int reg ) {
return reg < REGS.length ? REGS[reg] : "[rsp+"+(reg-REGS.length)*4+"]";
}
- // 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 int callInArg( TypeFunPtr tfp, int idx ) {
- int[] cargs = switch( CodeGen.CODE._callingConv ) {
- case CodeGen.CallingConv.SystemV -> CALLINARG_SYSTEMV_INT;
- case CodeGen.CallingConv.Win64 -> CALLINARG_WIN64_INT;
- };
- if( idx < 2 ) return -1;
- if( idx >= cargs.length )
- throw Utils.TODO(); // Pass on stack slot
- if( tfp.arg(idx-2) instanceof TypeFloat )
- return XMM0 + (idx-2);
- return cargs[idx];
- }
-
// WIN64(param passing)
- static int[] CALLINARG_WIN64_INT = new int[] {
- -1, // Control, no register
- -1, // Memory, no register
- RCX,
- RDX,
- R08,
- R09,
- };
- static RegMask[] CALLINARG_WIN64_MASK = new RegMask[] {
- null, // Control, no register
- null, // Memory, no register
+ static RegMask[] CALLINMASK_WIN64 = new RegMask[] {
RCX_MASK,
RDX_MASK,
R08_MASK,
R09_MASK,
};
- // caller saved(win64)
- public static final long WIN64_ABI_CALLER_SAVED =
- (1L << RAX) | (1L << RCX) | (1L << RDX) | (1L << R08) | (1L << R09) | (1L << R10) | (1L << R11);
-
- // callee saved(win64)
- public static final long WIN64_ABI_CALLEE_SAVED = ~WIN64_ABI_CALLER_SAVED;
-
- static int[] CALLINARG_SYSTEMV_INT = new int[] {
- -1, // Control, no register
- -1, // Memory, no register
- RDI,
- RSI,
- RDX,
- RCX,
- R08,
- R09,
- };
- static RegMask[] CALLINARG_SYSTEMV_MASK = new RegMask[] {
- null, // Control, no register
- null, // Memory, no register
+ // SystemV(param passing)
+ static RegMask[] CALLINMASK_SYSTEMV = new RegMask[] {
RDI_MASK,
RSI_MASK,
RDX_MASK,
@@ -131,32 +79,128 @@ public class x86_64_v2 extends Machine {
R08_MASK,
R09_MASK,
};
+
+ // Limit of float args passed in registers
+ static RegMask[] XMMS4 = new RegMask[]{
+ new RegMask(XMM0), new RegMask(XMM1), new RegMask(XMM2), new RegMask(XMM3),
+ };
+
+ // Map from function signature and argument index to register.
+ // Used to set input registers to CallNodes, and ParmNode outputs.
+ static RegMask callInMask( TypeFunPtr tfp, int idx ) {
+ if( idx==0 ) return RPC_MASK;
+ if( idx==1 ) return null;
+ // Count floats in signature up to index
+ int fcnt=0;
+ for( int i=2; i<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 < XMMS4.length )
+ return XMMS4[fcnt];
+ } else {
+ RegMask[] cargs = switch( CodeGen.CODE._callingConv ) {
+ case "SystemV" -> CALLINMASK_SYSTEMV;
+ case "Win64" -> CALLINMASK_WIN64;
+ default -> throw new IllegalArgumentException("Unknown calling convention: "+CodeGen.CODE._callingConv);
+ };
+ if( idx-2-fcnt < cargs.length )
+ return cargs[idx-2-fcnt];
+ }
+ throw Utils.TODO(); // Pass on stack slot
+ }
+
// caller saved(systemv)
+ static final long SYSTEM5_CALLER_SAVE =
+ (1L<< RAX) | (1L<< RCX) | (1L<< RDX) |
+ (1L<< RDI) | (1L<< RSI) |
+ (1L<< R08) | (1L<< R09) | (1L<< R10) | (1L<< R11) |
+ (1L<<FLAGS)| // Flags are killed
+ // All FP regs are killed
+ FP_BITS;
+ static final RegMask SYSTEM5_CALLER_SAVE_MASK = new RegMask(SYSTEM5_CALLER_SAVE);
+
// caller saved(win64)
- public static final long SYSTEMV_ABI_CALLER_SAVED =
- (1L << RAX) | (1L << RDI) | (1L << RSI) | (1L << RCX) | (1L << RDX) | (1L << R08) | (1L << R09) << (1L << R10) << (1L << R11);
- // callee saved(systemv)
- public static final long SYSTEMV_ABI_CALLE_SAVED = ~SYSTEMV_ABI_CALLER_SAVED;
+ static 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);
+ static final RegMask WIN64_CALLER_SAVE_MASK = new RegMask(WIN64_CALLER_SAVE);
+
+ static RegMask x86CallerSave() {
+ return switch( CodeGen.CODE._callingConv ) {
+ case "SystemV" -> SYSTEM5_CALLER_SAVE_MASK;
+ case "Win64" -> WIN64_CALLER_SAVE_MASK;
+ default -> throw new IllegalArgumentException("Unknown calling convention: "+CodeGen.CODE._callingConv);
+ };
+ }
+ @Override public RegMask callerSave() { return x86CallerSave(); }
+
+ static final RegMask SYSTEM5_CALLEE_SAVE_MASK;
+ static final RegMask WIN64_CALLEE_SAVE_MASK;
+ static {
+ long callee = ~SYSTEM5_CALLER_SAVE;
+ // Remove the spills
+ callee &= (1L<<MAX_REG)-1;
+ callee &= ~(1L<<FLAGS);
+ callee &= ~(1L<<RSP);
+ SYSTEM5_CALLEE_SAVE_MASK = new RegMask(callee);
+
+ callee = ~WIN64_CALLER_SAVE;
+ // Remove the spills
+ callee &= (1L<<MAX_REG)-1;
+ callee &= ~(1L<<FLAGS);
+ callee &= ~(1L<<RSP);
+ WIN64_CALLEE_SAVE_MASK = new RegMask(callee);
+ }
+ static RegMask x86CalleeSave() {
+ return switch( CodeGen.CODE._callingConv ) {
+ case "SystemV" -> SYSTEM5_CALLEE_SAVE_MASK;
+ case "Win64" -> WIN64_CALLEE_SAVE_MASK;
+ default -> throw new IllegalArgumentException("Unknown calling convention: "+CodeGen.CODE._callingConv);
+ };
+ }
+ @Override public RegMask calleeSave() { return x86CalleeSave(); }
- static RegMask callInMask( TypeFunPtr tfp, int idx ) {
- RegMask[] cargs = switch( CodeGen.CODE._callingConv ) {
- case CodeGen.CallingConv.SystemV -> CALLINARG_SYSTEMV_MASK;
- case CodeGen.CallingConv.Win64 -> CALLINARG_WIN64_MASK;
+ static final RegMask[] WIN64_RET_MASKS, SYS5_RET_MASKS;
+ static {
+ WIN64_RET_MASKS = makeRetMasks( WIN64_CALLEE_SAVE_MASK);
+ SYS5_RET_MASKS = makeRetMasks(SYSTEM5_CALLEE_SAVE_MASK);
+ }
+ private static RegMask[] makeRetMasks(RegMask mask) {
+ int nSaves = mask.size();
+ RegMask[] masks = new RegMask[4 + nSaves];
+ masks[0] = null;
+ masks[1] = null; // Memory
+ masks[2] = null; // Varies, either XMM0 or RAX
+ masks[3] = RPC_MASK;
+ short reg = mask.firstReg();
+ for( int i=0; i<nSaves; i++ ) {
+ masks[i+4] = new RegMask(reg);
+ reg = mask.nextReg(reg);
+ }
+ return masks;
+ }
+
+ // Return single int/ptr register. Used by CallEnd output and Return input.
+ static RegMask retMask( TypeFunPtr tfp, int i ) {
+ if( i==2 )
+ return tfp.ret() instanceof TypeFloat ? XMM0_MASK : RAX_MASK;
+ RegMask[] masks = switch( CodeGen.CODE._callingConv ) {
+ case "SystemV" -> SYS5_RET_MASKS;
+ case "Win64" -> WIN64_RET_MASKS;
+ default -> throw new IllegalArgumentException("Unknown calling convention: "+CodeGen.CODE._callingConv);
};
- if( idx < 2 ) return null;
- if( idx >= cargs.length )
- throw Utils.TODO(); // Pass on stack slot
- if( tfp.arg(idx-2) instanceof TypeFloat )
- return XMMS[idx-2];
- return cargs[idx];
+ return masks[i];
}
// Create a split op; any register to any register, including stack slots
- @Override
- public Node split() {
- throw Utils.TODO();
- }
+ @Override public SplitNode split(String kind, byte round, LRG lrg) { return new SplitX86(kind,round); }
// Return a MachNode unconditional branch
@Override public CFGNode jump() {
@@ -177,6 +221,7 @@ public class x86_64_v2 extends Machine {
case BoolNode bool -> cmp(bool);
case CallEndNode cend -> new CallEndX86(cend);
case CallNode call -> call(call);
+ case CastNode cast -> new CastX86(cast);
case CProjNode c -> new CProjNode(c);
case ConstantNode con -> con(con);
case DivFNode divf -> new DivFX86(divf);
@@ -285,6 +330,10 @@ public class x86_64_v2 extends Machine {
return new SetX86(cmp,bool.op());
}
private Node _cmp( BoolNode bool ) {
+ // Float variant
+ if( bool.isFloat() )
+ return new CmpFX86(bool);
+
Node lhs = bool.in(1);
Node rhs = bool.in(2);
if( lhs instanceof LoadNode ld && ld.nOuts()==1 )
@@ -293,20 +342,23 @@ public class x86_64_v2 extends Machine {
if( rhs instanceof LoadNode ld && ld.nOuts()==1 )
return new CmpMemX86(bool,address(ld),ld.ptr(),idx,off,scale, imm(lhs),val,true);
- return rhs instanceof ConstantNode con && con._con instanceof TypeInteger ti
- ? new CmpIX86(bool, ti)
- : new CmpX86(bool);
+ // Vs immediate
+ if( rhs instanceof ConstantNode con && con._con instanceof TypeInteger ti )
+ return new CmpIX86(bool, ti);
+ // x vs y
+ return new CmpX86(bool);
}
private Node con( ConstantNode con ) {
+ if( !con._con.isConstant() ) return new ConstantNode( con ); // Default unknown caller inputs
return switch( con._con ) {
case TypeInteger ti -> new IntX86(con);
case TypeFloat tf -> new FltX86(con);
case TypeFunPtr tfp -> new TFPX86(con);
- case TypeMemPtr tmp -> new ConstantNode(con);
+ case TypeMemPtr tmp -> throw Utils.TODO();
case TypeNil tn -> throw Utils.TODO();
// TOP, BOTTOM, XCtrl, Ctrl, etc. Never any executable code.
- case Type t -> new ConstantNode(con);
+ case Type t -> t==Type.NIL ? new IntX86(con) : new ConstantNode(con);
};
}
@@ -425,6 +477,7 @@ public class x86_64_v2 extends Machine {
}
return mop;
}
+
private int imm( Node xval ) {
assert val==null && imm==0;
if( xval instanceof ConstantNode con && con._con instanceof TypeInteger ti ) {
diff --git a/src/main/java/com/seaofnodes/simple/ASMPrinter.java b/src/main/java/com/seaofnodes/simple/print/ASMPrinter.java
index 5cc13210..1a233aa2 100644
--- a/src/main/java/com/seaofnodes/simple/ASMPrinter.java
+++ b/src/main/java/com/seaofnodes/simple/print/ASMPrinter.java
@@ -1,7 +1,8 @@
-package com.seaofnodes.simple;
+package com.seaofnodes.simple.print;
-import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.codegen.CodeGen;
import com.seaofnodes.simple.SB;
+import com.seaofnodes.simple.node.*;
import com.seaofnodes.simple.type.TypeFunPtr;
import com.seaofnodes.simple.type.TypeMem;
import com.seaofnodes.simple.type.TypeRPC;
@@ -23,6 +24,7 @@ public abstract class ASMPrinter {
private static int print(int iadr, SB sb, CodeGen code, FunNode fun, int cfgidx) {
// Function header
sb.nl().p("---");
+ if( fun._name != null ) sb.p(fun._name).p(" ");
fun.sig().print(sb);
sb.p("---------------------------").nl();
@@ -37,39 +39,60 @@ public abstract class ASMPrinter {
return iadr;
}
+ static private final int encWidth = 8;
+ 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) {
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("L").p(bb._nid).p(":").nl();
+ sb.p(bb instanceof LoopNode ? "LOOP" : "L").p(bb._nid).p(":").nl();
+ if( bb instanceof CallNode ) return iadr;
+ final boolean postAlloc = code._phase.ordinal() > CodeGen.Phase.RegAlloc.ordinal();
+
+ // Count Phis
+ int nPhi=0;
+ for( ; nPhi<bb.nOuts(); nPhi++ )
+ if( !(bb.out(nPhi) instanceof PhiNode) )
+ break;
+
+ if( nPhi>0 ) {
+ // Post-alloc phi prints all on one line
+ if( postAlloc ) {
+ sb.fix(4," ").p(" ").fix(encWidth,"").p(" ");
+ for( int i=0; i<nPhi; i++ ) {
+ PhiNode phi = (PhiNode)bb.out(i);
+ if( !(phi._type instanceof TypeMem || phi._type instanceof TypeRPC) ) // Nothing for the hidden ones
+ sb.p(phi._label).p(':').p(code.reg(phi)).p(',');
+ }
+ sb.unchar().nl();
+
+ } else {
+ for( int j=0; j<nPhi; j++ ) {
+ PhiNode phi = (PhiNode)bb.out(j);
+ if( phi._type instanceof TypeMem || phi._type instanceof TypeRPC ) continue; // Nothing for the hidden ones
+ sb.fix(4," ").p(" ").fix(encWidth,"").p(" ").fix(opWidth,phi._label).p(" ");
+ sb.p(code.reg(phi));
+ if( !(phi instanceof ParmNode) ) {
+ 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( !(bb instanceof CallNode) )
- for( Node n : bb._outputs )
- iadr = doInst(iadr, sb,code,fun,bb,n);
+ // All the non-phis
+ for( int i=nPhi; i<bb.nOuts(); i++ )
+ iadr = doInst(iadr, sb,code,fun,bb,bb.out(i),postAlloc );
return iadr;
}
- static int doInst(int iadr, SB sb, CodeGen code, FunNode fun, CFGNode bb, Node n) {
- final int encWidth = 8;
- final int opWidth = 5;
- final int argWidth = 30;
-
- if( n instanceof CProjNode ) return iadr;
-
- // Phis are useful before (and during) reg alloc
- if( n instanceof PhiNode phi ) {
- if( phi._type instanceof TypeMem || phi._type instanceof TypeRPC ) return iadr; // Nothing for the hidden ones
- sb.fix(4," ").p(" ").fix(encWidth,"").p(" ").fix(opWidth,phi._label).p(" ");
- sb.p(code.reg(n));
- if( !(n instanceof ParmNode) ) {
- sb.p(" = phi( ");
- for( int i=1; i<n.nIns(); i++ )
- sb.p("N").p(n.in(i)._nid).p(",");
- sb.unchar().p(" )");
- }
- sb.nl();
- return iadr;
- }
+ static int doInst(int iadr, SB sb, CodeGen code, FunNode fun, CFGNode bb, Node n, boolean postAlloc ) {
+ if( n instanceof CProjNode ) return iadr;
+ if( n instanceof CalleeSaveNode && postAlloc ) return iadr;
// 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
@@ -131,7 +154,7 @@ public abstract class ASMPrinter {
if( !(n instanceof CFGNode) && n instanceof MultiNode ) {
for( Node proj : n._outputs ) {
assert proj instanceof ProjNode;
- doInst(iadr,sb,code,fun,bb,proj);
+ doInst(iadr,sb,code,fun,bb,proj,postAlloc);
}
}
diff --git a/src/main/java/com/seaofnodes/simple/GraphVisualizer.java b/src/main/java/com/seaofnodes/simple/print/GraphVisualizer.java
index 30e25916..e4cf2c31 100644
--- a/src/main/java/com/seaofnodes/simple/GraphVisualizer.java
+++ b/src/main/java/com/seaofnodes/simple/print/GraphVisualizer.java
@@ -1,9 +1,8 @@
-package com.seaofnodes.simple;
+package com.seaofnodes.simple.print;
+import com.seaofnodes.simple.Parser;
import com.seaofnodes.simple.node.*;
-
import java.util.*;
-
import static com.seaofnodes.simple.Utils.TODO;
/**
diff --git a/src/main/java/com/seaofnodes/simple/IRPrinter.java b/src/main/java/com/seaofnodes/simple/print/IRPrinter.java
index 3cddacfd..c91f844b 100644
--- a/src/main/java/com/seaofnodes/simple/IRPrinter.java
+++ b/src/main/java/com/seaofnodes/simple/print/IRPrinter.java
@@ -1,5 +1,9 @@
-package com.seaofnodes.simple;
+package com.seaofnodes.simple.print;
+import com.seaofnodes.simple.Ary;
+import com.seaofnodes.simple.SB;
+import com.seaofnodes.simple.Utils;
+import com.seaofnodes.simple.codegen.CodeGen;
import com.seaofnodes.simple.node.*;
import com.seaofnodes.simple.type.TypeFunPtr;
import java.util.ArrayList;
@@ -53,19 +57,22 @@ public abstract class IRPrinter {
boolean gap=false;
for( int i=rpos.size()-1; i>=0; i-- ) {
Node n = rpos.get(i);
- if( n instanceof CFGNode || n.isMultiHead() ) {
+ 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(" "),false).p("----------------------\n");
printLine( n, sb ); // Print head
while( --i >= 0 ) {
Node t = rpos.get(i);
- if( !t.isMultiTail() ) { i++; break; }
+ if( !(t.in(0) instanceof MultiNode) ) { i++; break; }
printLine( t, sb );
}
if( n instanceof ReturnNode ret ) {
FunNode fun = ret.fun();
- fun.sig().print(sb.p("--- ").p(fun._name==null ? "" : fun._name).p(" "),false).p("----------------------\n");
+ sb.p("--- ");
+ if( fun != null )
+ fun.sig().print(sb.p(fun._name==null ? "" : fun._name).p(" "),false);
+ sb.p("----------------------\n");
}
if( !(n instanceof CallNode) ) {
sb.p("\n"); // Blank after multitail
@@ -137,7 +144,7 @@ public abstract class IRPrinter {
// Toss things past the limit except multi-heads
while( idx < _bfs.size() ) {
Node n = _bfs.get(idx);
- if( n.isMultiHead() ) idx++;
+ if( n instanceof MultiNode ) idx++;
else del(idx);
}
// Root set is any node with no inputs in the visited set
@@ -170,6 +177,48 @@ public abstract class IRPrinter {
}
}
+ 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 )
+ 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.
diff --git a/src/main/java/com/seaofnodes/simple/JSViewer.java b/src/main/java/com/seaofnodes/simple/print/JSViewer.java
index fcb0c28a..a2b674c9 100644
--- a/src/main/java/com/seaofnodes/simple/JSViewer.java
+++ b/src/main/java/com/seaofnodes/simple/print/JSViewer.java
@@ -1,12 +1,15 @@
-package com.seaofnodes.simple;
+package com.seaofnodes.simple.print;
+import com.seaofnodes.simple.SB;
+import com.seaofnodes.simple.Utils;
+import com.seaofnodes.simple.codegen.CodeGen;
import com.seaofnodes.simple.node.*;
import com.seaofnodes.simple.type.*;
import java.io.IOException;
-import java.util.Stack;
import java.nio.file.Paths;
import java.util.Collection;
-import static com.seaofnodes.simple.CodeGen.CODE;
+import java.util.Stack;
+import static com.seaofnodes.simple.codegen.CodeGen.CODE;
public class JSViewer implements AutoCloseable {
// Display programs in an endless loop
@@ -69,7 +72,7 @@ public class JSViewer implements AutoCloseable {
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() )
+ if( CODE._phase == null )
return;
boolean midParse = CODE._phase == CodeGen.Phase.Parse;
Stack<ScopeNode> xScopes = midParse ? CODE.P._xScopes : null;
diff --git a/src/main/java/com/seaofnodes/simple/SimpleWebSocket.java b/src/main/java/com/seaofnodes/simple/print/SimpleWebSocket.java
index 1ce766ff..7655213d 100644
--- a/src/main/java/com/seaofnodes/simple/SimpleWebSocket.java
+++ b/src/main/java/com/seaofnodes/simple/print/SimpleWebSocket.java
@@ -1,5 +1,6 @@
-package com.seaofnodes.simple;
+package com.seaofnodes.simple.print;
+import com.seaofnodes.simple.Utils;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
diff --git a/src/main/java/com/seaofnodes/simple/type/TypeFunPtr.java b/src/main/java/com/seaofnodes/simple/type/TypeFunPtr.java
index be801c85..167a931c 100644
--- a/src/main/java/com/seaofnodes/simple/type/TypeFunPtr.java
+++ b/src/main/java/com/seaofnodes/simple/type/TypeFunPtr.java
@@ -43,7 +43,8 @@ public class TypeFunPtr extends TypeNil {
public static TypeFunPtr BOT = make((byte)3,TypeTuple.BOT,Type.BOTTOM,-1);
public static TypeFunPtr TEST = make((byte)2,TypeTuple.TEST,TypeInteger.BOT,1);
public static TypeFunPtr TEST0 = make((byte)3,TypeTuple.TEST,TypeInteger.BOT,3);
- public static void gather(ArrayList<Type> ts) { ts.add(TEST); ts.add(TEST0); ts.add(BOT); }
+ public static TypeFunPtr MAIN = make((byte)3,TypeTuple.MAIN,TypeInteger.BOT,-1);
+ public static void gather(ArrayList<Type> ts) { ts.add(TEST); ts.add(TEST0); ts.add(BOT); ts.add(MAIN); }
@Override
Type xmeet(Type t) {
@@ -68,6 +69,7 @@ public class TypeFunPtr extends TypeNil {
public long fidxs() { return _fidxs; }
public Type ret() { return _ret; }
public int nargs() { return _sig._types.length; }
+ public int fidx() { assert Long.bitCount(_fidxs)==1; return Long.numberOfTrailingZeros(_fidxs); }
@Override
int hash() { return Utils.fold(_sig.hashCode() ^ _ret.hashCode() ^ _fidxs ^ super.hash()); }
diff --git a/src/main/java/com/seaofnodes/simple/type/TypeStruct.java b/src/main/java/com/seaofnodes/simple/type/TypeStruct.java
index 6c5cb8d4..e981b5dc 100644
--- a/src/main/java/com/seaofnodes/simple/type/TypeStruct.java
+++ b/src/main/java/com/seaofnodes/simple/type/TypeStruct.java
@@ -184,6 +184,7 @@ public class TypeStruct extends Type {
public SB print(SB sb) {
sb.p(_name);
if( _fields == null ) return sb; // Forward reference struct, just print the name
+ if( isAry() ) return sb; // Skip printing generic array fields
sb.p(" {");
for( Field f : _fields )
f._type.print(sb).p(f._final ? " " : " !").p(f._fname).p("; ");
diff --git a/src/main/java/com/seaofnodes/simple/type/TypeTuple.java b/src/main/java/com/seaofnodes/simple/type/TypeTuple.java
index 6d3a2b69..95e35e99 100644
--- a/src/main/java/com/seaofnodes/simple/type/TypeTuple.java
+++ b/src/main/java/com/seaofnodes/simple/type/TypeTuple.java
@@ -55,6 +55,8 @@ public class TypeTuple extends Type {
return make(ts);
}
+ public Type ret() { assert _types.length==3; return _types[2]; }
+
@Override public String str() { return print(new SB()).toString(); }
@Override public SB print(SB sb) {