summaryrefslogtreecommitdiff
path: root/src/main/java
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java')
-rw-r--r--src/main/java/com/seaofnodes/simple/IterPeeps.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/Parser.java24
-rw-r--r--src/main/java/com/seaofnodes/simple/SB.java23
-rw-r--r--src/main/java/com/seaofnodes/simple/codegen/BuildLRG.java5
-rw-r--r--src/main/java/com/seaofnodes/simple/codegen/Coalesce.java105
-rw-r--r--src/main/java/com/seaofnodes/simple/codegen/CodeGen.java184
-rw-r--r--src/main/java/com/seaofnodes/simple/codegen/ElfExpr.java87
-rw-r--r--src/main/java/com/seaofnodes/simple/codegen/ElfFile.java382
-rw-r--r--src/main/java/com/seaofnodes/simple/codegen/Encoding.java475
-rw-r--r--src/main/java/com/seaofnodes/simple/codegen/GlobalCodeMotion.java9
-rw-r--r--src/main/java/com/seaofnodes/simple/codegen/IFG.java60
-rw-r--r--src/main/java/com/seaofnodes/simple/codegen/LRG.java19
-rw-r--r--src/main/java/com/seaofnodes/simple/codegen/LinkMem.java23
-rw-r--r--src/main/java/com/seaofnodes/simple/codegen/ListScheduler.java3
-rw-r--r--src/main/java/com/seaofnodes/simple/codegen/Machine.java89
-rw-r--r--src/main/java/com/seaofnodes/simple/codegen/RIPRelSize.java14
-rw-r--r--src/main/java/com/seaofnodes/simple/codegen/RegAlloc.java138
-rw-r--r--src/main/java/com/seaofnodes/simple/codegen/RegMask.java35
-rw-r--r--src/main/java/com/seaofnodes/simple/codegen/RegMaskRW.java36
-rw-r--r--src/main/java/com/seaofnodes/simple/node/AddNode.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/BoolNode.java12
-rw-r--r--src/main/java/com/seaofnodes/simple/node/BulkMemPhiNode.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/CFGNode.java25
-rw-r--r--src/main/java/com/seaofnodes/simple/node/CProjNode.java14
-rw-r--r--src/main/java/com/seaofnodes/simple/node/CallEndNode.java51
-rw-r--r--src/main/java/com/seaofnodes/simple/node/CallNode.java15
-rw-r--r--src/main/java/com/seaofnodes/simple/node/CalleeSaveNode.java6
-rw-r--r--src/main/java/com/seaofnodes/simple/node/CastNode.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/ConstantNode.java4
-rw-r--r--src/main/java/com/seaofnodes/simple/node/FRefNode.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/FunNode.java57
-rw-r--r--src/main/java/com/seaofnodes/simple/node/IfNode.java43
-rw-r--r--src/main/java/com/seaofnodes/simple/node/LoadNode.java161
-rw-r--r--src/main/java/com/seaofnodes/simple/node/LoopNode.java53
-rw-r--r--src/main/java/com/seaofnodes/simple/node/MachNode.java11
-rw-r--r--src/main/java/com/seaofnodes/simple/node/MemMergeNode.java5
-rw-r--r--src/main/java/com/seaofnodes/simple/node/MemOpNode.java52
-rw-r--r--src/main/java/com/seaofnodes/simple/node/MemPhiNode.java4
-rw-r--r--src/main/java/com/seaofnodes/simple/node/NeverNode.java3
-rw-r--r--src/main/java/com/seaofnodes/simple/node/NewNode.java39
-rw-r--r--src/main/java/com/seaofnodes/simple/node/Node.java54
-rw-r--r--src/main/java/com/seaofnodes/simple/node/PhiNode.java28
-rw-r--r--src/main/java/com/seaofnodes/simple/node/ProjNode.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/RegionNode.java45
-rw-r--r--src/main/java/com/seaofnodes/simple/node/ReturnNode.java40
-rw-r--r--src/main/java/com/seaofnodes/simple/node/ShlNode.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/SplitNode.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/StopNode.java3
-rw-r--r--src/main/java/com/seaofnodes/simple/node/StoreNode.java23
-rw-r--r--src/main/java/com/seaofnodes/simple/node/StructNode.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/AddARM.java29
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/AddFARM.java24
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/AddIARM.java53
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/AndARM.java34
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/AndIARM.java57
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/AsrARM.java33
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/AsrIARM.java55
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/BranchARM.java63
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/CallARM.java35
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/CallEndARM.java29
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/CallRRARM.java19
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/CastARM.java18
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/CmpARM.java19
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/CmpFARM.java21
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/CmpIARM.java35
-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.java27
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/DivFARM.java22
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/F8ToIARM.java23
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/FloatARM.java34
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/FunARM.java27
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/I2F8ARM.java27
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/IntARM.java16
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/LoadARM.java27
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/LslARM.java33
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/LslIARM.java58
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/LsrARM.java32
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/LsrIARM.java53
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/MemOpARM.java67
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/MulARM.java28
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/MulFARM.java21
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/MulIARM.java43
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/NegARM.java25
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/NewARM.java44
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/NotARM.java27
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/OrARM.java39
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/OrIARM.java49
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/ParmARM.java14
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/ProjARM.java12
-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.java22
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/SplitARM.java63
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/StoreARM.java46
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/SubARM.java26
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/SubFARM.java27
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/SubIARM.java32
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/TFPARM.java47
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/UJmpARM.java47
-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.java51
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/arm.java438
-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/AUIPC.java42
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/AddFRISC.java26
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/AddIRISC.java17
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/AddRISC.java26
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/AndIRISC.java8
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/AndRISC.java36
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/BranchRISC.java78
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/CallEndRISC.java29
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/CallRISC.java45
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/CallRRISC.java25
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/CastRISC.java8
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/DivFRISC.java22
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/DivRISC.java27
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/F8ToIRISC.java19
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/FltRISC.java51
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/FunRISC.java24
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/I2F8RISC.java24
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/ImmRISC.java48
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/IntRISC.java30
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/LUI.java33
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/LoadRISC.java34
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/MemOpRISC.java81
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/MulFRISC.java26
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/MulRISC.java25
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/NegRISC.java21
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/NewRISC.java50
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/NotRISC.java29
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/OrIRISC.java6
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/OrRISC.java35
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/ParmRISC.java17
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/ProjRISC.java14
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/RetRISC.java48
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/SetFRISC.java25
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/SetIRISC.java24
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/SetRISC.java35
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/SllIRISC.java6
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/SllRISC.java36
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/SplitRISC.java62
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/SraIRISC.java6
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/SraRISC.java33
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/SrlIRISC.java6
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/SrlRISC.java33
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/StoreRISC.java44
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/SubFRISC.java26
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/SubRISC.java21
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/TFPRISC.java54
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/UJmpRISC.java48
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/XorIRISC.java6
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/XorRISC.java37
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/riscv.java395
-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.java21
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddIX86.java42
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddMemX86.java16
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddX86.java32
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AndIX86.java45
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AndX86.java35
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CallEndX86.java29
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CallRX86.java22
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CallX86.java38
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CastX86.java8
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CmpFX86.java17
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CmpIX86.java43
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CmpMemX86.java37
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CmpX86.java23
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/DivFX86.java27
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/DivX86.java20
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/F8ToIX86.java18
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/FltX86.java43
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/FunX86.java29
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/I2f8X86.java19
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ImmX86.java31
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/IntX86.java25
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/JmpX86.java60
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/LeaX86.java24
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/LoadX86.java32
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MemAddX86.java17
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MemOpX86.java25
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MulFX86.java22
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MulIX86.java49
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MulX86.java19
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/NegX86.java19
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/NewX86.java26
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/NotX86.java23
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/OrIX86.java42
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/OrX86.java36
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ParmX86.java18
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ProjX86.java12
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/RegX86.java21
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/RetX86.java48
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SarIX86.java38
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SarX86.java46
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SetX86.java29
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShlIX86.java47
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShlX86.java44
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShrIX86.java43
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShrX86.java45
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SplitX86.java49
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/StoreX86.java23
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SubFX86.java20
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SubX86.java31
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/TFPX86.java44
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/UJmpX86.java40
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/XorIX86.java40
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/XorX86.java35
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/x86_64_v2.java616
-rw-r--r--src/main/java/com/seaofnodes/simple/print/ASMPrinter.java32
-rw-r--r--src/main/java/com/seaofnodes/simple/print/SimpleGraphAdapter.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/print/SimpleGraphObserver.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/type/Type.java3
-rw-r--r--src/main/java/com/seaofnodes/simple/type/TypeFunPtr.java1
-rw-r--r--src/main/java/com/seaofnodes/simple/type/TypeMemPtr.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/type/TypeStruct.java7
-rw-r--r--src/main/java/com/seaofnodes/simple/type/TypeTuple.java16
216 files changed, 5138 insertions, 3791 deletions
diff --git a/src/main/java/com/seaofnodes/simple/IterPeeps.java b/src/main/java/com/seaofnodes/simple/IterPeeps.java
index a21fb517..7c2f1b0c 100644
--- a/src/main/java/com/seaofnodes/simple/IterPeeps.java
+++ b/src/main/java/com/seaofnodes/simple/IterPeeps.java
@@ -53,6 +53,7 @@ public class IterPeeps {
_workInline = new WorkList<>(seed);
}
+ @SuppressWarnings("unchecked")
public <N extends Node> N add( N n ) { return (N)_work.push(n); }
public void addAll( Ary<Node> ary ) { _work.addAll(ary); }
@@ -147,6 +148,7 @@ public class IterPeeps {
* Classic WorkList, with a fast add/remove, dup removal, random pull.
* The Node's nid is used to check membership in the worklist.
*/
+ @SuppressWarnings("unchecked")
public static class WorkList<E extends Node> {
private Node[] _es;
diff --git a/src/main/java/com/seaofnodes/simple/Parser.java b/src/main/java/com/seaofnodes/simple/Parser.java
index 697e84ce..6f307785 100644
--- a/src/main/java/com/seaofnodes/simple/Parser.java
+++ b/src/main/java/com/seaofnodes/simple/Parser.java
@@ -142,7 +142,25 @@ public class Parser {
// Parse whole program, as-if function header "{ int arg -> body }"
parseFunctionBody(_code._main,loc(),"arg");
- _code.link(_code._main)._name = "main";
+
+ // Kill an empty default main. Keep only if it was explicitly defined
+ // (programmer asked for a "main") or it has stuff (i.e. beginner
+ // default main).
+ FunNode main = _code.link(_code._main);
+ StopNode stop = _code._stop;
+ if( main.ret().expr() instanceof ConstantNode && main.ret().mem().in(0)==main && stop.nIns() > 1 ) {
+ // Kill an empty default main; so it does not attempt to put a
+ // "main" in any final ELF file
+ stop.delDef(stop._inputs.find(main.ret()));
+ } else {
+ // We have a non-empty default main.
+ // Check for an explicit main
+ for( Node n : stop._inputs )
+ if( n instanceof FunNode fun && fun._name.equals("main") )
+ // Found an explicit "main" AND we have a default "main"
+ throw error("Cannot define both an explicit main and a default main");
+ main._name = "main";
+ }
if( !_lexer.isEOF() ) throw _errorSyntax("unexpected");
@@ -152,7 +170,7 @@ public class Parser {
for( StructNode init : INITS.values() )
init.unkeep().kill();
INITS.clear();
- _code._stop.peephole();
+ stop.peephole();
}
/**
@@ -1431,6 +1449,7 @@ public class Parser {
// Post-call setup
CallEndNode cend = (CallEndNode)new CallEndNode(call).peephole();
+ call.peephole(); // Rerun peeps after CallEnd, allows early inlining
// Control from CallEnd
ctrl(new CProjNode(cend,0,ScopeNode.CTRL).peephole());
// A call may update any alias, so its memory result is the new whole state.
@@ -1690,6 +1709,7 @@ public class Parser {
while( Character.isDigit(c=nextChar()) ) ;
if( !(c=='e' || c=='.') )
return --_position - old;
+ if( peek() == '-' ) nextChar();
while( Character.isDigit(c=nextChar()) || c=='e' || c=='.' ) ;
return -(--_position - old);
}
diff --git a/src/main/java/com/seaofnodes/simple/SB.java b/src/main/java/com/seaofnodes/simple/SB.java
index a45f0ddf..978fbed9 100644
--- a/src/main/java/com/seaofnodes/simple/SB.java
+++ b/src/main/java/com/seaofnodes/simple/SB.java
@@ -29,21 +29,28 @@ public final class SB {
public SB p( int s ) { _sb.append(s); return this; }
public SB p( long s ) { _sb.append(s); return this; }
public SB p( boolean s) { _sb.append(s); return this; }
- // 4 hex digits
- public SB hex4(int s) {
- assert (s>>4*4)==0; // Fits in 16 bits
- for( int i=0; i<4; i++ ) {
- int digit = (s>>((3-i)*4)) & 0xf;
- _sb.append((char)((digit <= 9 ? '0' : ('A'-10))+digit));
- }
+ // 1 byte, 2 hex digits, 8 bits
+ public SB hex1(int s) {
+ int digit = (s>>4) & 0xf;
+ _sb.append((char)((digit <= 9 ? '0' : ('A'-10))+digit));
+ digit = s & 0xf;
+ _sb.append((char)((digit <= 9 ? '0' : ('A'-10))+digit));
return this;
}
+ // 2 bytes, 4 hex digits, 16 bits, Big Endian
+ public SB hex2(int s) { return hex1(s>> 8).hex1(s); }
+ // 4 bytes, 8 hex digits, 32 bits, Big Endian
+ public SB hex4(int s) { return hex2(s>>16).hex2(s); }
+ // 8 bytes, 16 hex digits, 64 bits, Big Endian
+ public SB hex8(long s) { return hex4((int)(s>>32)).hex4((int)s); }
+
// Fixed width field
public SB fix( int sz, String s ) {
for( int i=0; i<sz; i++ )
_sb.append( i < s.length() ? s.charAt(i) : ' ');
return this;
}
+ public char at(int idx ) { return _sb.charAt(idx); }
// Not spelled "p" on purpose: too easy to accidentally say "p(1.0)" and
// suddenly call the autoboxed version.
@@ -83,6 +90,8 @@ public final class SB {
//
public SB unchar() { return unchar(1); }
public SB unchar(int x) { _sb.setLength(_sb.length()-x); return this; }
+ public SB setLen(int len) { _sb.setLength(len); return this; }
+ public String subString(int start, int end ) { return _sb.substring(start,end); }
public SB clear() { _sb.setLength(0); return this; }
public int len() { return _sb.length(); }
diff --git a/src/main/java/com/seaofnodes/simple/codegen/BuildLRG.java b/src/main/java/com/seaofnodes/simple/codegen/BuildLRG.java
index 746a0e6a..2319714b 100644
--- a/src/main/java/com/seaofnodes/simple/codegen/BuildLRG.java
+++ b/src/main/java/com/seaofnodes/simple/codegen/BuildLRG.java
@@ -1,6 +1,5 @@
package com.seaofnodes.simple.codegen;
-import com.seaofnodes.simple.Utils;
import com.seaofnodes.simple.node.*;
import com.seaofnodes.simple.type.Type;
import com.seaofnodes.simple.type.TypeMem;
@@ -30,7 +29,7 @@ abstract public class BuildLRG {
break;
// If none, make one.
if( lrg==null ) lrg = alloc.newLRG(n);
- if( phi instanceof MachNode mach ) defLRG(alloc,n);
+ if( phi instanceof MachNode ) defLRG(alloc,n);
// Pass 2: everybody uses the same LRG
lrg=alloc.union(lrg,phi);
for( int i=phi instanceof ParmNode ? 2 : 1; i<n.nIns(); i++ )
@@ -60,7 +59,7 @@ abstract public class BuildLRG {
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);
+ RegMask use_mask = mach.regmap(i); // use_mask is also null for anti-dep
if( use_mask!=null && !lrg2.machUse(mach,(short)i,use_mask.size1()).and(use_mask) )
alloc.fail(lrg2); // Empty register mask, must split
}
diff --git a/src/main/java/com/seaofnodes/simple/codegen/Coalesce.java b/src/main/java/com/seaofnodes/simple/codegen/Coalesce.java
new file mode 100644
index 00000000..e2fac779
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/codegen/Coalesce.java
@@ -0,0 +1,105 @@
+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;
+
+// Coalesce copies
+abstract public class Coalesce {
+
+ public static boolean coalesce( int round, RegAlloc alloc ) {
+
+ // Walk all the splits, looking for coalesce chances
+ boolean progress = false;
+ for( CFGNode bb : alloc._code._cfg ) {
+ outer:
+ for( int j=0; j<bb.nOuts(); j++ ) {
+ Node n = bb.out(j);
+ if( !(n instanceof SplitNode) ) continue;
+ LRG v1 = alloc.lrg(n);
+ LRG v2 = alloc.lrg(n.in(1));
+ if( v1 != v2 ) {
+ LRG ov1 = v1, ov2 = v2;
+ // Get the smaller neighbor count in v1
+ if( v1.nadj() > v2.nadj() )
+ { v1 = v2; v2 = alloc.lrg(n); }
+ int v2len = v2._adj==null ? 0 : v2._adj._len;
+ // See that they do not conflict (coalescing would make a self-conflict)
+ if( v1._adj!=null && v2._adj!=null && v1._mask.overlap(v2._mask) ) {
+ // Walk the smaller neighbors, and add to the larger.
+ // Check for direct conflicts along the way.
+ for( LRG v3 : v1._adj ) {
+ if( v3==v2 ) {
+ // Direct conflict, skip this LRG pair
+ v2._adj.setLen(v2len);
+ continue outer;
+ }
+ // Add missing LRGs from V1 to V2
+ if( v2._adj.find(v3)== -1 )
+ v2._adj.push(v3);
+ }
+ }
+ // Most constrained mask
+ RegMask mask = v1._mask==v2._mask ? v1._mask : v1._mask.copy().and(v2._mask);
+ // Check for capacity
+ if( v2.nadj() >= mask.size() ) {
+ // Fails capacity, will not be trivial colorable
+ if( v2._adj!=null ) v2._adj.setLen(v2len);
+ continue;
+ }
+
+ // Union lrgs.
+ progress = true;
+
+ // Since `n` is dying, remove from the LRGS BEFORE doing
+ // the union; this will let the union code keep the
+ // matching bits from the other side, preserving some
+ // info for biased coloring
+ if( ov1. _machDef==n ) ov1. _machDef = null;
+ if( ov1._splitDef==n ) ov1._splitDef = null;
+ if( ov2. _machUse==n ) ov2. _machUse = null;
+ if( ov2._splitUse==n ) ov2._splitUse = null;
+ // Keep larger adjacency list.
+ Ary<LRG> larger = v2._adj;
+ v2._adj = null;
+
+ // Union lrgs. Swaps again, based on small lrg
+ LRG vWin = v1.union(v2);
+ vWin._adj = larger;
+ LRG vLose = v1.leader() ? v2 : v1;
+
+ // LRGs below v2len were always in v2, but maybe not in v1.
+ // LRGs above v2len are in v1 but were not in v2.
+
+ // Walk v2's neighbors, replacing vLose with vWin.
+ if( larger != null )
+ for( LRG vn : larger ) {
+ int idx = vn._adj.find(vLose);
+ if( idx != -1 )
+ vn._adj.del(idx);
+ if( vn._adj.find(vWin) == -1 )
+ vn._adj.add(vWin);
+ }
+ } else {
+
+ // Since `n` is dying, remove from the LRGS
+ if( v1. _machDef==n ) v1. _machDef=null;
+ if( v1. _machUse==n ) v1. _machUse=null;
+ if( v1._splitDef==n ) v1._splitDef=null;
+ if( v1._splitUse==n ) v1._splitUse=null;
+ }
+
+ // Remove junk split
+ n.removeSplit();
+ j--;
+ }
+ }
+
+ if( progress )
+ alloc.unify();
+
+ return true;
+ }
+}
diff --git a/src/main/java/com/seaofnodes/simple/codegen/CodeGen.java b/src/main/java/com/seaofnodes/simple/codegen/CodeGen.java
index 78cf4c06..2c44cb05 100644
--- a/src/main/java/com/seaofnodes/simple/codegen/CodeGen.java
+++ b/src/main/java/com/seaofnodes/simple/codegen/CodeGen.java
@@ -6,22 +6,15 @@ import com.seaofnodes.simple.*;
import com.seaofnodes.simple.node.*;
import com.seaofnodes.simple.print.*;
import com.seaofnodes.simple.type.*;
+import java.io.IOException;
import java.util.BitSet;
import java.util.HashMap;
import java.util.IdentityHashMap;
+@SuppressWarnings("unchecked")
public class CodeGen {
public GraphObserver<Node> _obs;
-
- // Inlining can insert CFG edges and invalidate cached depth ordering.
- private char _iDepthVersion;
- public char iDepthVersion() { return _iDepthVersion; }
- public void invalidateIDepthCaches() {
- assert _iDepthVersion < Character.MAX_VALUE : "Dominator cache version exceeds 65535";
- ++_iDepthVersion;
- }
-
-
+ public static final String PORTS = "com.seaofnodes.simple.node.cpus";
// Last created CodeGen as a global; used all over to avoid passing about a
// "context".
public static CodeGen CODE;
@@ -30,11 +23,13 @@ public class CodeGen {
Parse, // Parse ASCII text into Sea-of-Nodes IR
Opto, // Run ideal optimizations
TypeCheck, // Last check for bad programs
+ LoopTree, // Build a loop tree; break infinite loops
InstSelect, // Convert to target hardware nodes
Schedule, // Global schedule (code motion) nodes
LocalSched, // Local schedule
RegAlloc, // Register allocation
Encoding, // Encoding
+ Export, // Export
}
public Phase _phase;
@@ -43,7 +38,6 @@ public class CodeGen {
public final String _src;
// Compile-time known initial argument type
public final TypeInteger _arg;
-
// ---------------------------
public CodeGen( String src ) { this(src, TypeInteger.BOT, 123L ); }
public CodeGen( String src, TypeInteger arg, long workListSeed ) {
@@ -59,6 +53,27 @@ public class CodeGen {
}
+ // All passes up to Phase, except ELF
+ public CodeGen driver( Phase phase ) { return driver(phase,null,null); }
+ public CodeGen driver( Phase phase, String cpu, String callingConv ) {
+ if( _phase==null ) parse();
+ if( _phase.ordinal() < phase.ordinal() ) opto();
+ if( _phase.ordinal() < phase.ordinal() ) typeCheck();
+ if( _phase.ordinal() < phase.ordinal() ) loopTree();
+ if( _phase.ordinal() < phase.ordinal() && cpu != null ) instSelect(cpu,callingConv);
+ if( _phase.ordinal() < phase.ordinal() ) GCM();
+ if( _phase.ordinal() < phase.ordinal() ) localSched();
+ if( _phase.ordinal() < phase.ordinal() ) regAlloc();
+ if( _phase.ordinal() < phase.ordinal() ) encode();
+ return this;
+ }
+
+ // Run all the phases through final ELF emission
+ public CodeGen driver( String cpu, String callingConv, String obj ) throws IOException {
+ return driver(Phase.Encoding,cpu,callingConv).exportELF(obj);
+ }
+
+
// ---------------------------
/**
* A counter, for unique node id generation. Starting with value 1, to
@@ -75,6 +90,14 @@ public class CodeGen {
public int getALIAS() { return _alias++; }
+ // Inlining can insert CFG edges and invalidate cached depth ordering.
+ private char _iDepthVersion;
+ public char iDepthVersion() { return _iDepthVersion; }
+ public void invalidateIDepthCaches() {
+ assert _iDepthVersion < Character.MAX_VALUE : "Dominator cache version exceeds 65535";
+ ++_iDepthVersion;
+ }
+
// Popular visit bitset, declared here, so it gets reused all over
public final BitSet _visit = new BitSet();
public BitSet visit() { assert _visit.isEmpty(); return _visit; }
@@ -168,7 +191,7 @@ public class CodeGen {
// ---------------------------
// Last check for bad programs
- int _tTypeCheck;
+ public int _tTypeCheck;
public CodeGen typeCheck() {
// Demand phase Opto for cleaning up dead control flow at least,
// required for the following GCM.
@@ -188,17 +211,42 @@ public class CodeGen {
return this;
}
+ // ---------------------------
+ // Build the loop tree; break never-exit loops
+ public int _tLoopTree;
+ public CodeGen loopTree() {
+ assert _phase.ordinal() <= Phase.TypeCheck.ordinal();
+ _phase = Phase.LoopTree;
+ long t0 = System.currentTimeMillis();
+ // Build the loop tree, fix never-exit loops
+ _start.buildLoopTree(_stop);
+ _tLoopTree = (int)(System.currentTimeMillis() - t0);
+ return this;
+ }
// ---------------------------
// Code generation CPU target
public Machine _mach;
- //
+ // Chosen calling convention (usually either Win64 or SystemV)
public String _callingConv;
+ // Callee save registers
+ public RegMask _callerSave;
+
+ // All returns have the following inputs:
+ // 0 - ctrl
+ // 1 - memory
+ // 2 - varies with returning GPR,FPR
+ // 3 - RPC
+ // 4+- Caller save registers
+ public RegMask[] _retMasks;
+ // Return Program Counter
+ public RegMask _rpcMask;
// Convert to target hardware nodes
public int _tInsSel;
- public CodeGen instSelect( String base, String cpu, String callingConv ) {
- assert _phase.ordinal() <= Phase.TypeCheck.ordinal();
+ public CodeGen instSelect( String cpu, String callingConv ) { return instSelect(cpu,callingConv,PORTS); }
+ public CodeGen instSelect( String cpu, String callingConv, String base ) {
+ assert _phase.ordinal() == Phase.LoopTree.ordinal();
_phase = Phase.InstSelect;
_callingConv = callingConv;
@@ -207,9 +255,30 @@ public class CodeGen {
// Look for CPU in fixed named place:
// com.seaofnodes.simple.node.cpus."cpu"."cpu.class"
String clzFile = base+"."+cpu+"."+cpu;
- try { _mach = ((Class<Machine>) Class.forName( clzFile )).getDeclaredConstructor().newInstance(); }
+ try { _mach = ((Class<Machine>) Class.forName( clzFile )).getDeclaredConstructor(new Class[]{CodeGen.class}).newInstance(this); }
catch( Exception e ) { throw new RuntimeException(e); }
+ // Build global copies of common register masks.
+ long callerSave = _mach.callerSave();
+ long neverSave = _mach. neverSave();
+ int maxReg = Math.min(64,_mach.regs().length);
+ assert maxReg>=64 || (-1L << maxReg & callerSave)==0; // No stack slots in callerSave
+ _callerSave = new RegMask(callerSave);
+
+ // Build a Return RegMask array. All returns have the following inputs:
+ // 0 - ctrl
+ // 1 - memory
+ // 2 - varies with returning GPR,FPR
+ // 3 - RPC
+ // 4+- Caller save registers
+ _retMasks = new RegMask[(maxReg-_callerSave.size()-Long.bitCount( neverSave ))+4];
+ for( int reg=0, i=4; reg<maxReg; reg++ )
+ if( !_callerSave.test(reg) && ((1L<<reg)&neverSave)==0 )
+ _retMasks[i++] = new RegMask(reg);
+ _rpcMask = new RegMask(_mach.rpc());
+ _retMasks[3] = _rpcMask;
+
+
// Convert to machine ops
long t0 = System.currentTimeMillis();
_uid = 1; // All new machine nodes reset numbering
@@ -245,7 +314,7 @@ public class CodeGen {
for( int i=0; i < x.nIns(); i++ )
x._inputs.set(i, _instSelect(x.in(i),map) );
if( x instanceof MachNode mach )
- mach.postSelect(); // Post selection action
+ mach.postSelect(this); // Post selection action
// Updates forward edges only.
n._outputs.clear();
@@ -265,8 +334,8 @@ public class CodeGen {
// ---------------------------
- // Control Flow Graph in RPO order.
- int _tGCM;
+ // Control Flow Graph in Reverse Post Order.
+ public int _tGCM;
public Ary<CFGNode> _cfg = new Ary<>(CFGNode.class);
// Global schedule (code motion) nodes
@@ -275,8 +344,6 @@ public class CodeGen {
_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);
@@ -285,7 +352,7 @@ public class CodeGen {
// ---------------------------
// Local (basic block) scheduler phase, a classic list scheduler
- int _tLocal;
+ public int _tLocal;
public CodeGen localSched() {
assert _phase == Phase.Schedule;
_phase = Phase.LocalSched;
@@ -298,7 +365,7 @@ public class CodeGen {
// ---------------------------
// Register Allocation
- int _tRegAlloc;
+ public int _tRegAlloc;
public RegAlloc _regAlloc;
public CodeGen regAlloc() {
assert _phase == Phase.LocalSched;
@@ -309,31 +376,46 @@ public class CodeGen {
_tRegAlloc = (int)(System.currentTimeMillis() - t0);
return this;
}
- public String reg(Node n) {
+
+ // Human readable register name
+ public String reg(Node n) { return reg(n,null); }
+ public String reg(Node n, FunNode fun) {
if( _phase.ordinal() >= Phase.RegAlloc.ordinal() ) {
- String s = _regAlloc.reg(n);
+ String s = _regAlloc.reg(n,fun);
if( s!=null ) return s;
}
return "N"+ n._nid;
}
+
// ---------------------------
// Encoding
- int _tEncode;
+ public int _tEncode;
+ public Encoding _encoding; // Encoding object
+ public void preEncode() { } // overridden by alternative ports
public CodeGen encode() {
assert _phase == Phase.RegAlloc;
_phase = Phase.Encoding;
long t0 = System.currentTimeMillis();
+ _encoding = new Encoding(this);
+ preEncode();
+ _encoding.encode();
+ _tEncode = (int)(System.currentTimeMillis() - t0);
+ return this;
+ }
-
- //_tEncode = (int)(System.currentTimeMillis() - t0);
- //throw Utils.TODO();
+ // ---------------------------
+ // Exporting to external formats
+ public CodeGen exportELF(String fname) throws IOException {
+ assert _phase == Phase.Encoding;
+ _phase = Phase.Export;
+ if( fname == null ) new LinkMem(this).link(); // In memory patching
+ else new ElfFile(this).export(fname); // External ELF file
return this;
}
- // Encoded binary, no relocation info
- public byte[] binary() { throw Utils.TODO(); }
// ---------------------------
+ public boolean _asmLittle=true;
SB asm(SB sb) { return ASMPrinter.print(sb,this); }
public String asm() { return asm(new SB()).toString(); }
@@ -352,4 +434,44 @@ public class CodeGen {
// Debugging helper
public Node f(int idx) { return _stop.find(idx); }
+
+
+ String printCFG() {
+ if( _cfg==null ) return "no CFG";
+ SB sb = new SB();
+ for( CFGNode cfg : _cfg )
+ IRPrinter.printLine(cfg,sb);
+ return sb.toString();
+ }
+
+ public static void print_as_hex(Encoding enc) {
+ for (byte b : enc._bits.toByteArray()) {
+ System.out.print(String.format("%02X", b));
+ }
+ System.out.println();
+ }
+
+ public void print_as_hex() {
+ for (byte b : _encoding._bits.toByteArray()) {
+ System.out.print(String.format("%02X", b));
+ }
+ System.out.println();
+ }
+
+ //// Debug purposes for now
+// public CodeGen printENCODING() {
+// ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+//
+// for(Node bb : CodeGen.CODE._cfg) {
+// for(Node n: bb.outs()) {
+// if(n instanceof MachNode) {
+// ((MachNode) n).encoding(E);
+// }
+// }
+// }
+//
+// print_as_hex(outputStream);
+// // Get the raw bytes from the output stream
+// return this;
+// }
}
diff --git a/src/main/java/com/seaofnodes/simple/codegen/ElfExpr.java b/src/main/java/com/seaofnodes/simple/codegen/ElfExpr.java
new file mode 100644
index 00000000..9d9b8a1c
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/codegen/ElfExpr.java
@@ -0,0 +1,87 @@
+package com.seaofnodes.simple.codegen;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
+public class ElfExpr {
+ public static void main(String[] args) {
+ try (FileOutputStream fos = new FileOutputStream("hello.o")) {
+ ByteBuffer buffer = ByteBuffer.allocate(512);
+ buffer.order(ByteOrder.LITTLE_ENDIAN);
+
+ // === ELF HEADER (64 bytes) ===
+ buffer.put(new byte[]{
+ 0x7F, 'E', 'L', 'F', // ELF Magic
+ 2, 1, 1, 0, // 64-bit, little-endian, version
+ 0, 0, 0, 0, 0, 0, 0, // ABI padding
+ 2, 0, // Type: Executable (ET_EXEC)
+ 0x3E, 0x00, // Machine: x86-64
+ 1, 0, 0, 0, // ELF version
+ 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Entry point
+ 0, 0, 0, 0, 0, 0, 0, 0, // Program header offset
+ 64, 0, 0, 0, 0, 0, 0, 0, // Section header offset
+ 0, 0, 0, 0, // Flags
+ 64, 0, // ELF header size
+ 0, 0, // Program header entry size
+ 0, 0, // Number of program header entries
+ 40, 0, // Section header entry size
+ 2, 0, // Number of section headers
+ 1, 0 // Section header string table index
+ });
+
+ // === .TEXT SECTION (Executable Code) ===
+ byte[] textSection = {
+ // sys_write (write(1, message, 13))
+ (byte) 0x48, (byte) 0xC7, (byte) 0xC0, 0x01, 0x00, 0x00, 0x00, // mov rax, 1
+ (byte) 0x48, (byte) 0xC7, (byte) 0xC7, 0x01, 0x00, 0x00, 0x00, // mov rdi, 1
+ (byte) 0x48, (byte) 0xBE, (byte)0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov rsi, message
+ (byte) 0x48, (byte) 0xC7, (byte) 0xC2, 0x0D, 0x00, 0x00, 0x00, // mov rdx, 13
+ (byte) 0x0F, (byte) 0x05, // syscall
+
+ // sys_exit (exit(0))
+ (byte) 0x48, (byte) 0xC7, (byte) 0xC0, 0x3C, 0x00, 0x00, 0x00, // mov rax, 60
+ (byte) 0x48, (byte) 0x31, (byte) 0xFF, // xor rdi, rdi
+ (byte) 0x0F, (byte) 0x05 // syscall
+ };
+
+ // Append section data
+ buffer.position(256);
+ buffer.put(textSection);
+
+ // === STRING MESSAGE (.data Section) ===
+ byte[] message = "Hello, World!\n".getBytes();
+ buffer.position(400);
+ buffer.put(message);
+
+ // === SECTION HEADERS ===
+ buffer.position(64);
+
+ // Null Section (required as first entry)
+ buffer.put(new byte[40]);
+
+ // .text Section
+ buffer.putInt(1); // Offset in section string table (".text")
+ buffer.putInt(1); // Type: SHT_PROGBITS
+ buffer.putLong(6); // Flags: Executable | Allocatable
+ buffer.putLong(0); // Virtual address
+ buffer.putLong(256); // Offset to section data
+ buffer.putLong(textSection.length); // Section size
+ buffer.putInt(0); // Link
+ buffer.putInt(0); // Info
+ buffer.putLong(4); // Address alignment
+ buffer.putLong(0); // No extra entries
+
+ // === SECTION HEADER STRING TABLE ===
+ buffer.position(500);
+ buffer.put(".text".getBytes());
+ buffer.put((byte) 0x00); // Null terminator
+
+ fos.write(buffer.array());
+ System.out.println("ELF object file 'hello.o' created.");
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+}
diff --git a/src/main/java/com/seaofnodes/simple/codegen/ElfFile.java b/src/main/java/com/seaofnodes/simple/codegen/ElfFile.java
new file mode 100644
index 00000000..d1d20919
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/codegen/ElfFile.java
@@ -0,0 +1,382 @@
+package com.seaofnodes.simple.codegen;
+
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.type.*;
+import com.seaofnodes.simple.codegen.Encoding.BAOS;
+import java.io.*;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.util.HashMap;
+import java.util.Map;
+
+public class ElfFile {
+
+ final CodeGen _code;
+ Ary<Section> _sections = new Ary<>(Section.class);
+
+ ElfFile( CodeGen code ) { _code = code; }
+
+ public static int writeCString(DataSection strtab, String name) {
+ // place name in the string table
+ int pos = strtab._contents.size();
+ byte[] name_bytes = name.getBytes();
+ strtab._contents.write(name_bytes, 0, name_bytes.length);
+ // the strings are null terminated
+ strtab._contents.write(0);
+ return pos;
+ }
+
+ public static void write4( ByteArrayOutputStream bits, int op ) {
+ bits.write(op );
+ bits.write(op>> 8);
+ bits.write(op>>16);
+ bits.write(op>>24);
+ }
+ public static void write8( ByteArrayOutputStream bits, long i64 ) {
+ write4(bits, (int) i64 );
+ write4(bits, (int)(i64>>32));
+ }
+
+ public static final int SYMBOL_SIZE = 4+1+1+2+8+8;
+
+ public static final int SYM_BIND_LOCAL = 0;
+ public static final int SYM_BIND_GLOBAL = 1;
+ public static final int SYM_BIND_WEAK = 2;
+
+ public static final int SYM_TYPE_NOTYPE = 0;
+ public static final int SYM_TYPE_OBJECT = 1;
+ public static final int SYM_TYPE_FUNC = 2;
+ public static final int SYM_TYPE_SECTION = 3;
+
+ public static class Symbol {
+ String _name;
+ int _name_pos;
+
+ // index in the symbol table
+ int _index;
+
+ // which section is it in
+ int _parent;
+
+ // depends on the symbol type
+ // TODO
+ int _value, _size;
+
+ // top 4bits are the "bind", bottom 4 are "type"
+ int _info;
+
+ // bind 1011
+ // 00001111
+ // 10110000
+
+ Symbol(String name, int parent, int bind, int type) {
+ _name = name;
+ _parent = parent;
+ _info = (bind << 4) | (type & 0xF);
+ }
+
+ void writeHeader(ByteBuffer out) {
+ out.putInt(_name_pos); // name
+ out.put((byte) _info); // info
+ // default visibility
+ out.put((byte) 0); // other
+ out.putShort((short) _parent); // shndx
+ out.putLong(_value); // value
+ out.putLong(_size); // size
+ }
+ }
+
+ public static final int SHF_WRITE = 1;
+ public static final int SHF_ALLOC = 2;
+ public static final int SHF_EXECINSTR = 4;
+ public static final int SHF_STRINGS = 32;
+ public static final int SHF_INFO_LINK = 64;
+
+ public static final int SECTION_HDR_SIZE = 64;
+ abstract public static class Section {
+ String _name;
+ int _index;
+
+ int _type, _flags;
+ int _link, _info;
+
+ // where's the name in the string table
+ int _name_pos;
+ int _file_offset;
+
+ Section(String name, int type) {
+ _name = name;
+ _type = type;
+ }
+
+ void writeHeader(ByteBuffer out) {
+ out.putInt(_name_pos); // name
+ out.putInt(_type); // type
+ out.putLong(_flags); // flags
+ out.putLong(0); // addr
+ out.putLong(_file_offset); // offset
+ out.putLong(size()); // size
+ out.putInt(_link); // link
+ out.putInt(_info); // info
+ out.putLong(16); // addralign
+ if (_type == 2) {
+ out.putLong(SYMBOL_SIZE);// entsize
+ } else if (_type == 4) {
+ out.putLong(24); // entsize
+ } else {
+ out.putLong(0); // entsize
+ }
+ }
+
+ abstract void write(ByteBuffer out);
+ abstract int size();
+ }
+
+ public static class SymbolSection extends Section {
+ Ary<Symbol> _symbols = new Ary<>(Symbol.class);
+ Ary<Symbol> _loc = new Ary<>(Symbol.class);
+
+ SymbolSection(String name, int type) {
+ super(name, type);
+ }
+
+ @Override
+ void writeHeader(ByteBuffer out) {
+ // points to string table section
+ _link = 1;
+
+ // number of non-local symbols
+ _info = _loc.len() + 1;
+
+ super.writeHeader(out);
+ }
+
+ void push(Symbol s) {
+ if ((s._info >> 4) != SYM_BIND_LOCAL) {
+ _symbols.push(s);
+ } else {
+ _loc.push(s);
+ }
+ }
+
+ @Override
+ void write(ByteBuffer out) {
+ // Index 0 both designates the first entry in the table and serves as the undefined symbol index
+ for( int i = 0; i < SYMBOL_SIZE/4; i++ ) {
+ out.putInt(0);
+ }
+ // index is already set
+ for( Symbol s : _loc ) {
+ s.writeHeader(out);
+ }
+ for( Symbol s : _symbols ) {
+ s.writeHeader(out);
+ }
+ }
+
+ @Override
+ int size() {
+ return (1 + _symbols.len() + _loc.len()) * SYMBOL_SIZE;
+ }
+ }
+
+ public static class DataSection extends Section {
+ ByteArrayOutputStream _contents;
+
+ DataSection(String name, int type) {
+ super(name, type);
+ _contents = new ByteArrayOutputStream();
+ }
+
+ DataSection(String name, int type, ByteArrayOutputStream contents) {
+ super(name, type);
+ _contents = contents;
+ }
+
+ @Override
+ void write(ByteBuffer out) {
+ out.put(_contents.toByteArray());
+ }
+
+ @Override
+ int size() {
+ return _contents.size();
+ }
+ }
+
+ private void pushSection(Section s) {
+ _sections.push(s);
+ // ELF is effectively "1-based", due to the NULL section
+ s._index = _sections._len;
+ }
+
+ /* creates function and stores where it starts*/
+ private final HashMap<TypeFunPtr,Symbol> _funcs = new HashMap<>();
+ private void encodeFunctions(SymbolSection symbols, DataSection text) {
+ for( int i=0; i<_code._cfg._len; i++ ) {
+ if( !(_code._cfg.at(i) instanceof FunNode fun) ) continue;
+ // skip until the function ends
+ while( !(_code._cfg.at(i) instanceof ReturnNode ret) )
+ i++;
+ int end = _code._encoding._opStart[ret._nid] + _code._encoding._opLen[ret._nid];
+
+ if( fun._name==null ) continue; // Anonymous addresses are relocated within .text.
+ Symbol func = new Symbol(fun._name, text._index, SYM_BIND_GLOBAL, SYM_TYPE_FUNC);
+ func._value = _code._encoding._opStart[fun._nid];
+ func._size = end - func._value;
+ symbols.push(func);
+ _funcs.put(fun.sig(), func);
+ }
+ }
+
+ public void export(String fname) throws IOException {
+ DataSection strtab = new DataSection(".strtab", 3 /* SHT_SYMTAB */);
+ // first byte is reserved for an empty string
+ strtab._contents.write(0);
+ pushSection(strtab);
+
+ // place all the symbols
+ SymbolSection symbols = new SymbolSection(".symtab", 2 /* SHT_STRTAB */);
+ pushSection(symbols);
+
+ // zero flag by default
+ // we've already constructed this entire section in the encoding phase
+ DataSection text = new DataSection(".text", 1 /* SHT_PROGBITS */, _code._encoding._bits);
+ text._flags = SHF_ALLOC | SHF_WRITE | SHF_EXECINSTR;
+ pushSection(text);
+
+ // Build and write constant pool
+ BAOS cpool = new BAOS();
+ Encoding enc = _code._encoding;
+ enc.writeConstantPool(cpool,false);
+ DataSection rdata = new DataSection(".rodata", 1 /* SHT_PROGBITS */, cpool);
+ rdata._flags = SHF_ALLOC;
+ pushSection(rdata);
+
+ // populate function symbols
+ encodeFunctions(symbols, text);
+
+ int idx = 1;
+ for( Section s : _sections ) {
+ Symbol sym = new Symbol(s._name, idx++, SYM_BIND_LOCAL, SYM_TYPE_SECTION);
+ // we can reuse the same name pos from the actual section
+ sym._name_pos = s._name_pos;
+ sym._size = s.size();
+ symbols.push(sym);
+ }
+
+ // calculate local index
+ int num = 1;
+ for( Symbol s : symbols._loc )
+ s._index = num++;
+ // extra space for .rela.text
+ int start_global = num+1; // Add one to skip the final .rela.text local symbol
+ for( Symbol a: symbols._symbols )
+ a._index = start_global++;
+ int bigConIdx = start_global;
+ start_global += enc._bigCons.size();
+
+ // create .text relocations
+ DataSection text_rela = new DataSection(".rela.text", 4 /* SHT_RELA */);
+ for( Node n : enc._externals.keySet()) {
+ int nid = n._nid;
+ String extern = enc._externals.get(n);
+
+ Symbol sym = new Symbol(extern, 0, SYM_BIND_GLOBAL, SYM_TYPE_NOTYPE);
+ sym._index = start_global++;
+ symbols.push(sym);
+
+ int offset = enc._opStart[nid] + enc._opLen[nid] - 4;
+
+ // u64 offset
+ write8(text_rela._contents, offset);
+ // u64 info
+ write8(text_rela._contents, ((long)sym._index << 32L) | 4L /* PLT32 */);
+ // i64 addend
+ write8(text_rela._contents, -4);
+ }
+
+ // Write relocations for the constant pool
+ for( Encoding.Relo relo : enc._bigCons.values() ) {
+ Symbol glob = new Symbol("GLOB$"+bigConIdx, rdata._index, SYM_BIND_GLOBAL, SYM_TYPE_FUNC);
+ glob._value = relo._target;
+ glob._size = 1 << relo._t.log_size();
+ glob._index = bigConIdx++;
+ symbols.push(glob);
+ write8(text_rela._contents, relo._opStart+relo._off);
+ write8(text_rela._contents, ((long)glob._index << 32L) | relo._elf );
+ write8(text_rela._contents, -4);
+ }
+
+ text_rela._flags = SHF_INFO_LINK;
+ text_rela._link = 2;
+ text_rela._info = text._index;
+ pushSection(text_rela);
+
+ Symbol sym = new Symbol(text_rela._name, num++, SYM_BIND_LOCAL, SYM_TYPE_SECTION);
+ sym._name_pos = text_rela._name_pos;
+ sym._size = text_rela.size();
+ symbols.push(sym);
+
+ // populate string table
+ for( Section s : _sections ) { s._name_pos = writeCString(strtab, s._name); }
+ for( Symbol s : symbols._symbols ) { s._name_pos = writeCString(strtab, s._name); }
+ for( Symbol s : symbols._loc ) { s._name_pos = writeCString(strtab, s._name); }
+
+ int size = 64; // ELF header
+ // size of all section data
+ for( Section s : _sections ) {
+ s._file_offset = size;
+ size += s.size();
+ }
+ // headers go at the very end
+ int shdr_offset = size;
+ // there's a "null" section at the start of the section headers
+ size += SECTION_HDR_SIZE*(1 + _sections.len());
+
+ // System.out.printf("Hello %d, %d, %s\n", size, shdr_offset);
+ ByteBuffer out = ByteBuffer.allocate(size);
+ out.order(ByteOrder.LITTLE_ENDIAN);
+
+ // ELF header
+ out.put(new byte[]{ 0x7F, 'E', 'L', 'F', 2, 1, 1, 0 });
+ out.put(new byte[8]);
+ out.putShort((short)1 /* ET_REL */); // type
+ out.putShort((short)62 /* AMD64 */); // machine
+ out.putInt(1); // version
+ out.putLong(0); // entry
+ out.putLong(0); // phoff
+ out.putLong(shdr_offset); // shoff
+ out.putInt(0); // flags
+ out.putShort((short)64); // ehsize
+ out.putShort((short)0); // phentsize
+ out.putShort((short)0); // phnum
+ out.putShort((short)64); // shentsize
+ out.putShort((short) (1 + _sections.len())); // shnum
+ out.putShort((short)1); // shstrndx
+
+ // raw section data
+ for( Section s : _sections ) {
+ s.write(out);
+ }
+
+ // "null" section
+ for( int i = 0; i < 16; i++ ) {
+ out.putInt(0);
+ }
+
+ for( Section s : _sections ) {
+ s.writeHeader(out);
+ }
+ assert out.position() == size;
+
+ File file = new File(fname);
+ if( file.getParentFile()!=null )
+ file.getParentFile().mkdirs();
+ BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(file));
+ bos.write(out.array());
+ bos.close();
+ }
+
+}
diff --git a/src/main/java/com/seaofnodes/simple/codegen/Encoding.java b/src/main/java/com/seaofnodes/simple/codegen/Encoding.java
new file mode 100644
index 00000000..81f195b1
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/codegen/Encoding.java
@@ -0,0 +1,475 @@
+package com.seaofnodes.simple.codegen;
+
+import com.seaofnodes.simple.Ary;
+import com.seaofnodes.simple.SB;
+import com.seaofnodes.simple.Utils;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.print.IRPrinter;
+import com.seaofnodes.simple.type.*;
+import java.io.ByteArrayOutputStream;
+import java.util.*;
+
+
+/**
+ * Instruction encodings
+ * <p>
+ * This class holds the encoding bits, plus the relocation information needed
+ * to move the code to a non-zero offset, or write to an external file (ELF).
+ * <p>
+ * There are also a bunch of generic utilities for managing bits and bytes
+ * common in all encodings.
+ */
+public class Encoding implements com.seaofnodes.isa.CodeSink {
+
+ // Top-level program graph structure
+ public final CodeGen _code;
+
+ // Instruction bytes. The registers are encoded already. Relocatable data
+ // is in a fixed format depending on the kind of relocation.
+
+ // - RIP-relative offsets into the same chunk are just encoded with their
+ // check relative offsets; no relocation info is required.
+
+ // - Fixed address targets in the same encoding will be correct for a zero
+ // base; moving the entire encoding requires adding the new base address.
+
+ // - RIP-relative to external chunks have a zero offset; the matching
+ // relocation info will be used to patch the correct value.
+
+ public static class BAOS extends ByteArrayOutputStream {
+ public byte[] buf() { return buf; }
+ void set( byte[] buf0, int count0 ) { buf=buf0; count=count0; }
+ };
+ final public BAOS _bits = new BAOS();
+
+ public int [] _opStart; // Start of opcodes, by _nid
+ public byte[] _opLen; // Length of opcodes, by _nid
+
+ // Big Constant relocation info.
+ public static class Relo {
+ public final Node _op;
+ public final Type _t; // Constant type
+ public final int _align; // Log alignment, captured for diagnostic display
+ public final byte _off; // Offset from start of opcode
+ public final byte _elf; // ELF relocation type, e.g. 2/PC32
+ public int _target; // Where constant is finally placede
+ public int _opStart; // Opcode start
+ Relo( Node op, Type t, byte off, byte elf ) {
+ _op=op; _t=t; _off=off; _elf=elf;
+ _align = t.log_size();
+ }
+ }
+ public final HashMap<Node,Relo> _bigCons = new HashMap<>();
+
+ Encoding( CodeGen code ) { _code = code; }
+
+ // Shortcut to the defining register
+ public short reg(Node n) {
+ return _code._regAlloc.regnum(n);
+ }
+
+ public int read1(int idx) {
+ byte[] buf = _bits.buf();
+ return (buf[idx]&0xFF);
+ }
+ public int read2(int idx) {
+ byte[] buf = _bits.buf();
+ return (buf[idx]&0xFF) | (buf[idx+1]&0xFF) <<8;
+ }
+ public int read4(int idx) {
+ byte[] buf = _bits.buf();
+ return (buf[idx]&0xFF) | (buf[idx+1]&0xFF) <<8 | (buf[idx+2]&0xFF)<<16 | (buf[idx+3]&0xFF)<<24;
+ }
+ public long read8(int idx) {
+ return (read4(idx) & 0xFFFFFFFFL) | read4(idx+4);
+ }
+
+ // This buffer is invalid/moving until after all encodings are written
+ public byte[] bits() { return _bits.buf(); }
+
+ // 4 byte little-endian write
+ public void patch4( int idx, int val ) {
+ byte[] buf = _bits.buf();
+ buf[idx ] = (byte)(val );
+ buf[idx+1] = (byte)(val>> 8);
+ buf[idx+2] = (byte)(val>>16);
+ buf[idx+3] = (byte)(val>>24);
+ }
+
+ static void padN(int n, BAOS bits) {
+ while( (bits.size()+n-1 & -n) > bits.size() )
+ bits.write(0);
+ }
+
+ // Convenience for writing log-N
+ static void addN( int log, Type t, BAOS bits ) {
+ long x = t instanceof TypeInteger ti
+ ? ti.value()
+ : log==3
+ ? Double.doubleToRawLongBits( ((TypeFloat)t).value())
+ : Float.floatToRawIntBits((float)((TypeFloat)t).value());
+ addN(log,x,bits);
+ }
+ static void addN( int log, long x, BAOS bits ) {
+ for( int i=0; i < 1<<log; i++ ) {
+ bits.write((int)x);
+ x >>= 8;
+ }
+ }
+
+ public Encoding add1( int op ) { addN(0,op,_bits); return this; }
+ public Encoding add2( int op ) { addN(1,op,_bits); return this; }
+ public Encoding add4( int op ) { addN(2,op,_bits); return this; }
+ public Encoding add8(long op ) { addN(3,op,_bits); return this; }
+
+
+ // Nodes need "relocation" patching; things done after code is placed.
+ // Record src and dst Nodes.
+ private final HashMap<Node,CFGNode> _internals = new HashMap<>();
+ // Source is a Call, destination in the Fun.
+ public Encoding relo( CallNode call ) {
+ _internals.put(call,_code.link(call.tfp()));
+ return this;
+ }
+ public Encoding relo( ConstantNode con ) {
+ TypeFunPtr tfp = (TypeFunPtr)con._con;
+ _internals.put(con,_code.link(tfp));
+ return this;
+ }
+ public void jump( CFGNode jmp, CFGNode dst ) {
+ _internals.put(jmp,dst.uctrlSkipEmpty());
+ }
+
+
+ final HashMap<Node,String> _externals = new HashMap<>();
+ public Encoding external( Node call, String extern ) {
+ _externals.put(call,extern);
+ return this;
+ }
+
+ // Store t as a 32/64 bit constant in the code space; generate RIP-relative
+ // addressing to load it
+ public void largeConstant( Node relo, Type t, int off, int elf ) {
+ assert t.isConstant();
+ assert (byte)off == off;
+ assert (byte)elf == elf;
+ _bigCons.put(relo,new Relo(relo,t,(byte)off,(byte)elf));
+ }
+
+ // --------------------------------------------------
+ void encode() {
+ // Basic block layout: negate branches to keep blocks in-order; insert
+ // unconditional jumps. Attempt to keep backwards branches taken,
+ // forwards not-taken (this is the default prediction on most
+ // hardware). Layout is still Reverse Post Order but with more
+ // restrictions.
+ basicBlockLayout();
+
+ // Write encoding bits in order into a big byte array.
+ // Record opcode start and length.
+ writeEncodings();
+
+ // Short-form RIP-relative support: replace long encodings with short
+ // encodings and compact the code, changing all the offsets.
+ compactShortForm();
+
+ // Patch RIP-relative and local encodings now.
+ patchLocalRelocations();
+ }
+
+ // --------------------------------------------------
+ // Basic block layout: negate branches to keep blocks in-order; insert
+ // unconditional jumps. Attempt to keep backwards branches taken, forwards
+ // not-taken (this is the default prediction on most hardware). Layout is
+ // still Reverse Post Order but with more restrictions.
+ private void basicBlockLayout() {
+ IdentityHashMap<LoopNode,Ary<CFGNode>> rpos = new IdentityHashMap<>();
+ Ary<CFGNode> rpo = new Ary<>(CFGNode.class);
+ rpos.put(_code._start.loop(),rpo);
+ BitSet visit = _code.visit();
+ //IdentityHashMap<CFGNode,LoopNode> looptail = new IdentityHashMap<>();
+ rpo.add(_code._stop);
+ for( Node n : _code._start._outputs )
+ if( n instanceof FunNode fun ) {
+ int x = rpo._len;
+ //_rpo_cfg2(fun, visit, rpo, looptail);
+ _rpo_cfg(fun, visit, rpos );
+ assert rpo.at(x) instanceof ReturnNode;
+ }
+ rpo.add(_code._start);
+
+ // Reverse in-place
+ for( int i=0; i< rpo.size()>>1; i++ )
+ rpo.swap(i,rpo.size()-1-i);
+ visit.clear();
+ _code._cfg = rpo; // Save the new ordering
+ }
+
+
+ private void _rpo_cfg(CFGNode bb, BitSet visit, IdentityHashMap<LoopNode,Ary<CFGNode>> rpos ) {
+ if( bb==null || visit.get(bb._nid) ) return;
+ visit.set(bb._nid);
+ CFGNode next = bb.uctrl();
+
+ // Loops run an inner "rpo_cfg", then append the entire loop body in
+ // place. This keeps loop bodies completely contained, although if
+ // Some Day Later we have real profile data we ought to arrange the
+ // branch orderings based on frequency.
+ if( bb instanceof LoopNode loop ) {
+ Ary<CFGNode> body = new Ary<>(CFGNode.class); // Private RPO for the loop
+ rpos.put(loop.loop(),body); // Find it via loop tree
+ _rpo_cfg(next,visit,rpos); // RPO the loop
+ body.add(loop); // Include loop last (first in RPO)
+ Ary<CFGNode> outer = rpos.get(loop.cfg(1).loop());
+ outer.addAll(body); // Append to original CFG
+ return;
+ }
+
+ // IfNodes visit 2 sides, and may choose to reorder them in the RPO
+ if( bb instanceof IfNode iff ) {
+ // Pick out the T/F projections
+ CProjNode t = iff.cproj(0);
+ CProjNode f = iff.cproj(1);
+ // Invert the branch or not
+ if( shouldInvert(t,f,iff.loopDepth()) ) {
+ iff.negate();
+ t.invert();
+ f.invert();
+ CProjNode tmp=f; f=t; t=tmp; // Swap t/f
+ }
+
+ // Whichever side is visited first becomes last in the RPO. With
+ // no loops, visit the False side last (so True side first) so that
+ // when the False RPO visit returns, the IF is immediately next.
+ // When the RPO is reversed, the fall-through path will always be
+ // following the IF.
+ _rpo_cfg(t,visit,rpos); // True side first
+ next = f; // False side last
+ }
+
+ // If the *next* BB has already been visited, and we are not already a
+ // jump, we may need an unconditional forwards jump here
+ Ary<CFGNode> rpo = rpos.get(bb.loop());
+ if( next!=null && visit.get(next._nid) && !(bb instanceof IfNode) ) {
+ boolean needJump = next instanceof LoopNode
+ // Empty blocks from an IF will invert the IF and backwards
+ // branch to the loop head. If not empty, or not an IF
+ // will need a jump.
+ ? (bb.nOuts()>1 || !(bb.cfg0() instanceof IfNode) )
+ // Forwards jump. If all the blocks, in RPO order, to our
+ // target are empty, we will fall in and not need a jump.
+ : !backwardsEmptyScan(rpo,next);
+ if( needJump ) {
+ CFGNode jmp = _code._mach.jump();
+ jmp._ltree = bb._ltree;
+ jmp.setDefX(0,bb);
+ next.setDef(next._inputs.find(bb),jmp);
+ rpo.add(jmp);
+ }
+ }
+
+ _rpo_cfg(next,visit,rpos);
+ rpo.add(bb);
+ }
+
+ // Should this test be inverted?
+ private static boolean shouldInvert(CFGNode t, CFGNode f, int bld) {
+ int tld = t.loopDepth(), fld = f.loopDepth();
+ // These next two are symmetric and can happen in any order; if `tld <
+ // bld` is true, the `fld < bld` must be false, or else both directions
+ // exit the loop... and the IF test would not be in the loop.
+
+ // true to exit a loop usually (and false falls into Yet Another Loop
+ // Block), invert if false is taking an empty backwards branch.
+ if( tld < bld ) return forwardsEmptyScan(f,bld);
+ // false to exit a loop, normally invert to true (except empty back)
+ if( fld < bld ) return !forwardsEmptyScan(t,bld);
+
+ // Not exiting the loop; staying at this depth (or going deeper loop).
+
+ // Jump/true to an empty backwards block
+ if( forwardsEmptyScan(t,bld) ) return false;
+ if( forwardsEmptyScan(f,bld) ) return true ;
+
+ // Fall/false into a full block, Jump/true to an empty block.
+ if( f.nOuts()>1 && t.nOuts()==1 ) return false;
+ if( t.nOuts()>1 && f.nOuts()==1 ) return true ;
+ // Everything else equal, use pre-order
+ return t._pre > f._pre;
+ }
+
+ // Do we continue forwards from 'c' to the Loop back edge, without doing
+ // anything else? Then this branch can directly jump to the loop head.
+ private static boolean forwardsEmptyScan( CFGNode c, int bld ) {
+ if( c.nOuts()!=1 || c.loopDepth()!=bld ) return false;
+ return c.uctrl() instanceof RegionNode cfg &&
+ ((cfg instanceof LoopNode && cfg._ltree==c._ltree) || forwardsEmptyScan(cfg,bld));
+ }
+
+ // Is the CFG from "next" to the end empty? This means jumping to "next"
+ // will naturally fall into the end.
+ private static boolean backwardsEmptyScan(Ary<CFGNode> rpo, CFGNode next) {
+ for( int i=rpo._len-1; rpo.at(i)!=next; i-- )
+ if( rpo.at(i).nOuts()!=1 )
+ return false;
+ return true;
+ }
+
+ // --------------------------------------------------
+ // Write encoding bits in order into a big byte array.
+ // Record opcode start and length.
+ public FunNode _fun; // Currently encoding function
+ private void writeEncodings() {
+ _opStart= new int [_code.UID()];
+ _opLen = new byte[_code.UID()];
+ for( CFGNode bb : _code._cfg ) {
+ if( !(bb instanceof MachNode mach0) )
+ _opStart[bb._nid] = _bits.size();
+ else if( bb instanceof FunNode fun ) {
+ _fun = fun; // Currently encoding function
+ _opStart[bb._nid] = _bits.size();
+ mach0.encoding( this );
+ _opLen[bb._nid] = (byte) (_bits.size() - _opStart[bb._nid]);
+ }
+ for( Node n : bb._outputs ) {
+ if( n instanceof MachNode mach && !(n instanceof FunNode) ) {
+ _opStart[n._nid] = _bits.size();
+ mach.encoding( this );
+ _opLen[n._nid] = (byte) (_bits.size() - _opStart[n._nid]);
+ }
+ }
+ }
+ }
+
+ // --------------------------------------------------
+ // Short-form RIP-relative support: replace short encodings with long
+ // encodings and expand the code, changing all the offsets.
+ private void compactShortForm() {
+ int len = _code._cfg._len;
+ int[] oldStarts = new int[len];
+ for( int i=0; i<len; i++ )
+ oldStarts[i] = _opStart[_code._cfg.at(i)._nid];
+
+ // TODO: Rewrite this algo to use the small "_jmps" list of just the
+ // jumps instead of walking all blocks.
+ // TODO2: Allow short encodings on non-jumps. This algo doesn't
+ // really care jumps vs other rip-relative things.
+
+ // Check the size of short jumps, adjust the block starts for a whole
+ // pass over all blocks. If some jumps go long, they might stretch the
+ // distance for other jumps, so another pass is needed.
+ int slide = -1;
+ while( slide != 0) { // While no fails
+ slide = 0;
+ for( int i=0; i<len; i++ ) {
+ CFGNode bb = _code._cfg.at(i);
+ // Functions pad to align 16
+ if( bb instanceof FunNode ) {
+ int newStart = _opStart[bb._nid]+slide;
+ slide += (newStart+15 & -16)-newStart;
+ }
+ _opStart[bb._nid] += slide;
+ // Slide down all other (non-CFG) ops in the block
+ for( Node n : bb._outputs )
+ if( n instanceof MachNode && !(n instanceof CFGNode) )
+ _opStart[n._nid] += slide;
+ if( bb instanceof RIPRelSize riprel ) {
+ CFGNode target = (bb instanceof IfNode iff ? iff.cproj(0) : (CFGNode)bb.out(0)).uctrlSkipEmpty();
+ // Delta is from opStart to opStart. X86 at least counts
+ // the delta from the opEnd, but we don't have the end until
+ // we decide the size - so the encSize has to deal
+ int delta = _opStart[target._nid] - _opStart[bb._nid];
+ byte opLen = riprel.encSize(delta);
+ // Recorded size is smaller than the current size?
+ if( _opLen[bb._nid] < opLen ) {
+ // Start sliding the code down; record slide amount and new size
+ slide += opLen - _opLen[bb._nid];
+ _opLen[bb._nid] = opLen;
+ }
+ }
+ }
+ }
+
+
+ // Copy/slide the bits to make space for all the longer branches
+ int grow = _opStart[_code._cfg.at(len-1)._nid] - oldStarts[len-1];
+ if( grow > 0 ) { // If no short-form ops, nothing to do here
+ int end = _bits.size();
+ byte[] bits = new byte[end+grow];
+ for( int i=len-1; i>=0; i-- ) {
+ int start = oldStarts[i];
+ if( start==0 && i>1 ) continue;
+ int oldStart = oldStarts[i];
+ int newStart = _opStart[_code._cfg.at(i)._nid];
+ System.arraycopy(_bits.buf(),oldStart,bits,newStart,end-start);
+ end = start;
+ }
+ _bits.set(bits,bits.length);
+ }
+ }
+
+
+ // --------------------------------------------------
+ // Patch local encodings now
+ void patchLocalRelocations() {
+ // Walk the local code-address relocations
+ for( Node src : _internals.keySet() ) {
+ int start = _opStart[src._nid];
+ Node dst = _internals.get(src);
+ int target = _opStart[dst._nid];
+ ((RIPRelSize)src).patch(this, start, _opLen[src._nid], target - start);
+ }
+ }
+
+ // --------------------------------------------------
+ // Write the constant pool into the BAOS and optionally patch locally
+ void writeConstantPool( BAOS bits, boolean patch ) {
+ padN(16,bits);
+
+ HashMap<Type,Integer> targets = new HashMap<>();
+
+ // By log size
+ for( int log = 3; log >= 0; log-- ) {
+ // Write the 8-byte constants
+ for( Node op : _bigCons.keySet() ) {
+ Relo relo = _bigCons.get(op);
+ if( relo._t.log_size()==log ) {
+ // Map from relo to constant start and patch
+ Integer target = targets.get(relo._t);
+ if( target==null ) {
+ targets.put(relo._t,target = bits.size());
+ // Put constant into code space.
+ if( relo._t instanceof TypeTuple tt ) // Constant tuples put all entries
+ for( Type tx : tt._types )
+ addN(log,tx,bits);
+ else
+ addN(log,relo._t,bits);
+ }
+ relo._target = target;
+ relo._opStart= _opStart[op._nid];
+ // Go ahead and locally patch in-memory
+ if( patch )
+ ((RIPRelSize)op).patch(this, relo._opStart, _opLen[op._nid], relo._target - relo._opStart);
+ }
+ }
+ }
+ }
+
+
+ // A series of libc/external calls that Simple can link against in a JIT.
+ // Since no runtime in the JVM process, using magic numbers for the CPU
+ // emulators to pick up on.
+ public static int SENTINAL_CALLOC = -4;
+
+ void patchGlobalRelocations() {
+ for( Node src : _externals.keySet() ) {
+ int start = _opStart[src._nid];
+ String dst = _externals.get(src);
+ int target = switch( dst ) {
+ case "calloc" -> SENTINAL_CALLOC;
+ default -> throw Utils.TODO();
+ };
+ ((RIPRelSize)src).patch(this, start, _opLen[src._nid], target - start);
+ }
+ }
+}
diff --git a/src/main/java/com/seaofnodes/simple/codegen/GlobalCodeMotion.java b/src/main/java/com/seaofnodes/simple/codegen/GlobalCodeMotion.java
index 52906afb..447a6df1 100644
--- a/src/main/java/com/seaofnodes/simple/codegen/GlobalCodeMotion.java
+++ b/src/main/java/com/seaofnodes/simple/codegen/GlobalCodeMotion.java
@@ -172,7 +172,7 @@ public abstract class GlobalCodeMotion {
continue outer; // Nope, await all uses done
// Loads need their memory inputs' uses also done
- if( n instanceof MemOpNode ld && !ld.isMem() )
+ if( n instanceof MemOpNode ld && ld._isLoad )
for( Node memuse : ld.antiDeps() )
if( late[memuse._nid]==null )
continue outer;
@@ -205,9 +205,10 @@ public abstract class GlobalCodeMotion {
lca = use_block(n,use, late).domLCA(lca,null);
// Loads may need anti-dependencies, raising their LCA
- if( n instanceof MemOpNode load && !load.isMem() )
+ if( n instanceof MemOpNode load && load._isLoad )
lca = find_anti_dep(lca,load,early,late,anti);
+
// Walk up from the LCA to the early, looking for best place. This is
// the lowest execution frequency, approximated by least loop depth and
// deepest control flow.
@@ -250,7 +251,7 @@ public abstract class GlobalCodeMotion {
if( visit.get(def._nid) ) return;
visit.set(def._nid);
for( Node out : def._outputs )
- if( out instanceof MemOpNode ld && !ld.isMem() && late[ld._nid]==null ) work.push(ld);
+ if( out instanceof MemOpNode ld && ld._isLoad && late[ld._nid]==null ) work.push(ld);
if( def instanceof MemMergeNode )
for( int i=1; i<def.nIns(); i++ )
if( def.in(i)!=null ) wakeLoads(def.in(i),late,work,visit);
@@ -291,6 +292,8 @@ public abstract class GlobalCodeMotion {
//
private static CFGNode anti_dep( MemOpNode load, CFGNode stblk, CFGNode defblk, CFGNode lca, Node st, int[] anti ) {
+ // A conditional writer can precede the load without dominating its late block.
+ // Walk back to the first overlap with the load's placement range.
for( ; stblk != defblk.idom(); stblk = stblk.idom() ) {
// Store and Load overlap, need anti-dependence
if( anti[stblk._nid]==load._nid ) {
diff --git a/src/main/java/com/seaofnodes/simple/codegen/IFG.java b/src/main/java/com/seaofnodes/simple/codegen/IFG.java
index 849d256e..bc5fae8d 100644
--- a/src/main/java/com/seaofnodes/simple/codegen/IFG.java
+++ b/src/main/java/com/seaofnodes/simple/codegen/IFG.java
@@ -20,7 +20,7 @@ abstract public class IFG {
static final IdentityHashMap<LRG,Node> TMP = new IdentityHashMap<>();
// Inteference Graph: Array of Bitsets
- private static final Ary<BitSet> IFG = new Ary<>(BitSet.class);
+ static final Ary<BitSet> IFG = new Ary<>(BitSet.class);
static void resetIFG() {
for( BitSet bs : IFG )
if( bs!=null ) bs.clear();
@@ -80,6 +80,8 @@ abstract public class IFG {
while( !WORK.isEmpty() )
do_block(round,alloc,WORK.pop());
+ if( alloc.success() )
+ convert2DAdjacency(alloc);
return alloc.success();
}
@@ -126,10 +128,15 @@ abstract public class IFG {
TMP.remove(lrg); // Kill def
}
+ // Phis use and define the same live range, i.e. these LRGs already
+ // marked conflicted, no need to mark again
if( n instanceof PhiNode )
return;
- // Instructions can clobber registers without defining an LRG.
- if( n instanceof MachNode m ) kills(alloc,m);
+
+ // Kill any killed registers; milli-code routines like New can kill
+ // will not being a CFG.
+ if( n instanceof MachNode m )
+ kills(alloc,m);
// Interfere n with all live
if( lrg!=null ) {
@@ -145,7 +152,8 @@ abstract public class IFG {
// must fail. If *n* (a subset of lrg) needs the single
// last tlrg register then only tlrg must fail.
if( ((MachNode)n).outregmap().size1() ) {
- if( !tlrg.clr(lrg._mask.firstReg()) ) alloc.fail(tlrg);
+ if( !tlrg.clr(lrg._mask.firstReg()) ) // Clear bit, no interference
+ alloc.fail(tlrg); // Clearing drives mask to empty
} else addIFG(lrg,tlrg); // Add interference
}
}
@@ -223,13 +231,11 @@ abstract public class IFG {
CFGNode bb = priorbb.cfg(i);
if( bb == null ) return; // Start has no prior
while( !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<>() );
+ IdentityHashMap<LRG, Node> lrgs = BBOUTS.computeIfAbsent( bb, k -> new IdentityHashMap<>() );
- for( LRG lrg : TMP.keySet() ) {
+ 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 ) {
@@ -250,6 +256,24 @@ abstract public class IFG {
}
+ static void convert2DAdjacency( RegAlloc alloc ) {
+ // Convert the 2-D array of bits (a 1-D array of BitSets) into an
+ // adjacency matrix.
+ int maxlrg = alloc._LRGS.length;
+ for( int i=1; i<maxlrg; i++ ) {
+ BitSet ifg = IFG.atX(i);
+ if( ifg != null ) {
+ LRG lrg0 = alloc._LRGS[i];
+ for( int lrg = ifg.nextSetBit(0); lrg>=0; lrg=ifg.nextSetBit(lrg+1) ) {
+ LRG lrg1 = alloc._LRGS[lrg];
+ lrg0.addNeighbor(lrg1);
+ lrg1.addNeighbor(lrg0);
+ }
+ }
+ }
+ }
+
+
// ------------------------------------------------------------------------
// Color the inference graph.
@@ -266,22 +290,10 @@ abstract public class IFG {
// 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);
- }
- }
- }
+ for( int i=1; i<maxlrg; i++ )
+ if( alloc._LRGS[i] != null )
+ nlrgs++;
// Simplify
@@ -494,7 +506,7 @@ abstract public class IFG {
// Can I limit my own choices to valid neighbor choices?
for( LRG alrg : slrg._adj ) {
- int reg = alrg._reg;
+ short reg = alrg._reg;
if( reg == -1 && alrg._mask.size1() )
reg = alrg._mask.firstReg();
if( reg != -1 ) {
diff --git a/src/main/java/com/seaofnodes/simple/codegen/LRG.java b/src/main/java/com/seaofnodes/simple/codegen/LRG.java
index 5803cad8..ec254a2e 100644
--- a/src/main/java/com/seaofnodes/simple/codegen/LRG.java
+++ b/src/main/java/com/seaofnodes/simple/codegen/LRG.java
@@ -2,6 +2,7 @@ package com.seaofnodes.simple.codegen;
import com.seaofnodes.simple.Ary;
import com.seaofnodes.simple.SB;
+import com.seaofnodes.simple.Utils;
import com.seaofnodes.simple.node.*;
import java.util.IdentityHashMap;
@@ -37,6 +38,8 @@ public class LRG {
// Count of single-register defs and uses
short _1regDefCnt, _1regUseCnt;
+ // Count of all defs, uses. Mostly interested 1 vs many
+ boolean _multiDef, _multiUse;
// A sample MachNode def in the live range
public MachNode _machDef, _machUse;
@@ -54,6 +57,8 @@ public class LRG {
// Adjacent Live Range neighbors. Only valid during coloring
Ary<LRG> _adj;
+ public int nadj() { return _adj==null ? 0 : _adj._len; }
+
void addNeighbor(LRG lrg) {
if( _adj==null ) _adj = new Ary<>(LRG.class);
_adj.push(lrg);
@@ -72,7 +77,7 @@ public class LRG {
}
// More registers than neighbors
- boolean lowDegree() { return (_adj==null ? 0 : _adj._len) < _mask.size(); }
+ boolean lowDegree() { return nadj() < _mask.size(); }
LRG( short lrg ) { _lrg = lrg; _reg = -1; }
@@ -116,17 +121,20 @@ public class LRG {
if( _machDef==null ) {
_machDef = lrg._machDef;
} else if( lrg._machDef!=null ) {
+ if( _machDef != lrg._machDef ) _multiDef=true;
if( _1regDefCnt==0 )
_machDef = lrg._machDef;
else if( _machDef==lrg._machDef )
_1regDefCnt--;
}
_1regDefCnt += lrg._1regDefCnt;
+ _multiDef |= lrg._multiDef;
if( _machUse==null ) {
_machUse = lrg._machUse;
_uidx = lrg._uidx;
} else if( lrg._machUse!=null ) {
+ if( _machUse != lrg._machUse ) _multiUse=true;
if( _1regUseCnt==0 ) {
_machUse = lrg._machUse;
_uidx = lrg._uidx;
@@ -135,6 +143,7 @@ public class LRG {
_1regUseCnt--;
}
_1regUseCnt += lrg._1regUseCnt;
+ _multiUse |= lrg._multiUse;
// Fold deepest Split
_splitDef = deepSplit(_splitDef,lrg._splitDef);
@@ -142,7 +151,9 @@ public class LRG {
// Fold together masks
RegMask mask = _mask.and(lrg._mask);
- _mask = mask==null ? _mask.copy().and(lrg._mask) : mask;
+ if( mask==null )
+ mask = _mask.copy().and(lrg._mask);
+ _mask = mask;
return this;
}
@@ -152,6 +163,8 @@ public class LRG {
// Record any Mach def for spilling heuristics
LRG machDef( MachNode def, boolean size1 ) {
+ if( _machDef!=null && _machDef!=def )
+ _multiDef = true;
if( _machDef==null || size1 )
_machDef = def;
if( size1 )
@@ -163,6 +176,8 @@ public class LRG {
// Record any Mach use for spilling heuristics
LRG machUse( MachNode use, short uidx, boolean size1 ) {
+ if( _machUse!=null && _machUse!=use )
+ _multiUse = true;
if( _machUse==null || size1 )
{ _machUse = use; _uidx = uidx; }
if( size1 )
diff --git a/src/main/java/com/seaofnodes/simple/codegen/LinkMem.java b/src/main/java/com/seaofnodes/simple/codegen/LinkMem.java
new file mode 100644
index 00000000..e1e849e6
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/codegen/LinkMem.java
@@ -0,0 +1,23 @@
+package com.seaofnodes.simple.codegen;
+
+import com.seaofnodes.simple.Utils;
+import com.seaofnodes.simple.node.Node;
+
+// In-memory linking
+public class LinkMem {
+ final CodeGen _code;
+ LinkMem( CodeGen code ) { _code = code; }
+
+ public CodeGen link() {
+ Encoding enc = _code._encoding;
+
+ // Patch external calls internally
+ enc.patchGlobalRelocations();
+
+ // Write any large constants into a constant pool; they
+ // are accessed by RIP-relative addressing.
+ enc.writeConstantPool(enc._bits,true);
+
+ return _code;
+ }
+}
diff --git a/src/main/java/com/seaofnodes/simple/codegen/ListScheduler.java b/src/main/java/com/seaofnodes/simple/codegen/ListScheduler.java
index 1796ef7c..de4fadc4 100644
--- a/src/main/java/com/seaofnodes/simple/codegen/ListScheduler.java
+++ b/src/main/java/com/seaofnodes/simple/codegen/ListScheduler.java
@@ -48,6 +48,9 @@ public abstract class ListScheduler {
computeSingleRDef(bb,use);
else
computeSingleRDef(bb,n);
+
+ if( n instanceof NewNode || n instanceof CallNode )
+ bb.fun()._hasCalls = true;
return this;
}
diff --git a/src/main/java/com/seaofnodes/simple/codegen/Machine.java b/src/main/java/com/seaofnodes/simple/codegen/Machine.java
index 13d7abc1..2766414f 100644
--- a/src/main/java/com/seaofnodes/simple/codegen/Machine.java
+++ b/src/main/java/com/seaofnodes/simple/codegen/Machine.java
@@ -1,22 +1,97 @@
package com.seaofnodes.simple.codegen;
import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.type.TypeFunPtr;
abstract public class Machine {
// Human readable machine name. Something like "x86-64" or "arm" or "risc5"
public abstract String name();
+ // Default opcode size for printing; 4 bytes for most 32-bit risc chips
+ public int defaultOpSize() { return 4; }
// Human-readable name for a register number, e.g. "RAX" or "R0"
- public abstract String reg( int reg );
+ public abstract String[] regs();
// Create a split op; any register to any register, including stack slots
public abstract SplitNode split( String kind, byte round, LRG lrg);
- // List of caller-save registers
- public abstract RegMask callerSave();
- // List of callee-save registers
- public abstract RegMask calleeSave();
+ // List of caller-save registers, as a 64bit mask
+ public abstract long callerSave();
+ // List of never-save registers, e.g. RSP or a ZERO register if you have one
+ public abstract long neverSave();
+ // Call Argument Mask. Passed in the function signature and argument
+ // number (2-based; 0 is for control and 1 for memory). Also passed in a 0
+ // for the function itself, or for *outgoing* calls, the maximum stack slot
+ // given to the incoming function arguments (stack slots reserved for
+ // incoming arguments).
+ public abstract RegMask callArgMask(TypeFunPtr tfp, int arg, int maxArgSlot);
+ // Return register mask, based on signature (GPR vs FPR)
+ public abstract RegMask retMask(TypeFunPtr tfp);
+ // Return PC register
+ public abstract int rpc();
// Return a MachNode unconditional branch
public abstract CFGNode jump();
- // Break an infinite loop
- public abstract IfNode never( CFGNode ctrl );
// Instruction select from ideal nodes
public abstract Node instSelect( Node n );
+
+ /** Stack Slot Numbering
+
+RA gives all registers a number, starting at 0. Stack slots continue this
+numbering going up from the last register number. Common register numbers are
+0-15 for GPRs, 16-31 for FPRs, 32 for flags, and stack slots starting at
+register#33 going up. These numbers are machine-specific, YMMV, etc; e.g. RPC
+is only in stack slot 0 on X86; other cpus start with the rpc in a register
+which may spill into any generic spill slot.
+
+For ease of reading and printing, stack slots start again at slot#0 - but
+during RA they are actually biased by CPU.MAX_REG.
+
+Stack Layout during Reg Alloc:
+
+ Slot Value Description
+ ---- ------- -----------
+MAXREG+ R+1 argR maxArgSlot(calleR tfp), can be 0
+ ...
+MAXREG+ 1 arg0
+ ---- -- Caller ------
+MAXREG+ 0 RPC
+MAXREG+ R+E+N PAD optional alignment
+
+MAXREG+ + Spills Caller Frame Space; N spills
+MAXREG+ R+E+1 Spills
+
+MAXREG+ R+E argE maxArgSlot(calleE tfp), can be 0
+ ...
+MAXREG+ R+2 arg0
+ ---- -- Callee -------
+ ...
+
+Slots defined by maxArgSlot do not have to contain the actual passed argument;
+they can be "shadow slots". If they exist, they are property of the frame /
+FunNode where the TFP comes from.
+
+For a given calleR, there are generally many calleE's and they all share the
+low-range stack slots.
+
+Example:, Win64, main(int arg) { calloc(1,size); }
+
+ Post-Warp: SP+56 shadow-main-arg
+Pre-alloc Post-Alloc SP+48 RPC
+--- ------- ---------- -------------
+40 spill#3 SP+40 SP+40 spill#3
+39 spill#2 SP+32 ""
+38 spill#1 SP+24 ""
+37 spill#0 SP+16 "" spill#0
+36 shadow-calloc#size SP+ 8 SP+ 8 shadow-calloc#size
+35 shadow-calloc#1 SP+ 0 SP+ 0 shadow-calloc#1
+--- --- FRAME BASE ---------- ---- FRAME BASE -------
+34 shadow-main-arg SP+56
+33 RPC SP+48
+
+
+ */
+
+
+ // Maximum stack slot (or 0) for the args in this TFP. This will include
+ // shadow slots if defined in the ABI, even if all arguments are passed in
+ // registers.
+ public abstract short maxArgSlot(TypeFunPtr tfp);
+
}
diff --git a/src/main/java/com/seaofnodes/simple/codegen/RIPRelSize.java b/src/main/java/com/seaofnodes/simple/codegen/RIPRelSize.java
new file mode 100644
index 00000000..777182dc
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/codegen/RIPRelSize.java
@@ -0,0 +1,14 @@
+package com.seaofnodes.simple.codegen;
+
+// This Node rip-relative encodings has sizes that vary by delta
+public interface RIPRelSize {
+ // delta is the distance from the opcode *start* to the target start. Each
+ // hardware target adjusts as needed, X86 measures from the opcode *end*
+ // and small jumps are 2 bytes, so they'll need to subtract 2.
+ byte encSize(int delta);
+
+ // Patch full encoding in. Used for both short and long forms. delta
+ // again is from the opcode *start*.
+ void patch( Encoding enc, int opStart, int opLen, int delta );
+
+}
diff --git a/src/main/java/com/seaofnodes/simple/codegen/RegAlloc.java b/src/main/java/com/seaofnodes/simple/codegen/RegAlloc.java
index af28cbb3..dd3d2acb 100644
--- a/src/main/java/com/seaofnodes/simple/codegen/RegAlloc.java
+++ b/src/main/java/com/seaofnodes/simple/codegen/RegAlloc.java
@@ -5,6 +5,7 @@ import com.seaofnodes.simple.Utils;
import com.seaofnodes.simple.node.*;
import com.seaofnodes.simple.type.Type;
import java.util.Arrays;
+import java.util.BitSet;
import java.util.IdentityHashMap;
/**
@@ -138,11 +139,18 @@ public class RegAlloc {
}
// Printable register number for node n
- String reg( Node n ) {
+ String reg( Node n ) { return reg(n,null); }
+ String reg( Node n, FunNode fun ) {
LRG lrg = _lrg(n);
if( lrg==null ) return null;
+ // No register yet, use LRG
if( lrg._reg == -1 ) return "V"+lrg._lrg;
- return _code._mach.reg(lrg._reg);
+ // Chosen machine register unless stack-slot and past RA
+ String[] regs = _code._mach.regs();
+ if( lrg._reg < regs.length || _code._phase.ordinal() <= CodeGen.Phase.RegAlloc.ordinal() || fun==null )
+ return RegMask.reg(regs,lrg._reg);
+ // Stack-slot past RA uses the frame layout logic
+ return "[rsp+"+fun.computeStackOffset(_code,lrg._reg)+"]";
}
// -----------------------
@@ -150,9 +158,24 @@ public class RegAlloc {
public void regAlloc() {
// Insert callee-save registers
+ String[] regs = _code._mach.regs();
+ long neverSave= _code._mach.neverSave();
for( CFGNode bb : _code._cfg )
- if( bb instanceof FunNode fun )
- insertCalleeSave(fun);
+ if( bb instanceof FunNode fun ) {
+ ReturnNode ret = fun.ret();
+ int len = Math.min(regs.length,64);
+ for( int reg=0; reg<len; reg++ )
+ if( !_code._callerSave.test(reg) && ((1L<<reg)&neverSave)==0 ) {
+ ret.addDef(new CalleeSaveNode(fun,reg,regs[reg]));
+ assert ret.regmap(ret.nIns()-1).firstReg()==reg;
+ }
+ }
+ // Cache reg masks for New and Call
+ for( CFGNode bb : _code._cfg ) {
+ if( bb instanceof CallEndNode cend ) cend.cacheRegs(_code);
+ for( Node n : bb._outputs )
+ if( n instanceof NewNode nnn ) nnn.cacheRegs(_code);
+ }
// Optional diagnostic for values accidentally shared across functions.
//assert verifyFunctionLocalEdges();
@@ -161,7 +184,8 @@ public class RegAlloc {
byte round=0;
while( !graphColor(round) ) {
split(round);
- assert round < 7 : "Register allocation made no progress after eight rounds";
+ if( round >= 7 ) // Really expect to be done soon
+ throw new IllegalStateException("Register allocation made no progress after eight rounds");
round++;
}
postColor(); // Remove no-op spills
@@ -193,24 +217,12 @@ public class RegAlloc {
BuildLRG.run(round,this) && // if no hard register conflicts
// Build Interference Graph
IFG.build(round,this) && // If no self conflicts or uncolorable
+ // Conservative coalesce copies
+ Coalesce.coalesce(round,this) &&
// Color attempt
IFG.color(round,this); // If colorable
}
- // 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) {
@@ -220,12 +232,15 @@ public class RegAlloc {
// independently... which generally requires a full pass over the
// program for each failing live range. i.e., might be a lot of
// passes.
+
+ // Sort, to avoid non-deterministic HashMap ordering
LRG[] splits = _failed.keySet().toArray(new LRG[0]);
- Arrays.sort(splits, (x,y) -> x._lrg - y._lrg);
- for( LRG lrg : splits ) split(round,lrg);
+ Arrays.sort(splits, (x,y) -> x._lrg - y._lrg );
+ for( LRG lrg : splits )
+ split(round,lrg);
}
- // Split this live range
+ // Split this live range, top level heuristic
boolean split( byte round, LRG lrg ) {
assert lrg.leader(); // Already rolled up
@@ -234,7 +249,7 @@ public class RegAlloc {
// Register mask when empty; split around defs and uses with limited
// register masks.
- if( lrg._mask.isEmpty() ) {
+ if( lrg._mask.isEmpty() && (!lrg._multiDef || lrg._1regUseCnt==1) ) {
if( lrg._1regDefCnt <= 1 &&
lrg._1regUseCnt <= 1 &&
(lrg._1regDefCnt + lrg._1regUseCnt) > 0 &&
@@ -358,10 +373,10 @@ public class RegAlloc {
(min==max || n.cfg0().loopDepth() <= min) ) {
// Cloneable constants will be cloned at uses, not after def
if( !(n instanceof MachNode mach && mach.isClone()) &&
- // Single user is already a split, with no intervening clobber
- !(n.nOuts()==1 && n.out(0) instanceof SplitNode split && sameBlockNoClobber(split)) )
+ // Single user is already a split adjacent
+ !(n.nOuts()==1 && n.out(0) instanceof SplitNode split && sameBlockNoClobber(split) ) )
// Split after def in min loop nest
- insertAfterAndReplace(makeSplit("def/loop",round,lrg),n,true);
+ insertAfterAndReplace( makeSplit("def/loop",round,lrg), n,true);
}
// PhiNodes check all CFG inputs
@@ -372,17 +387,19 @@ public class RegAlloc {
// splitting in inner loop or at loop border
(min==max || phi.region().cfg(i).loopDepth() <= min) &&
// and not around the backedge of a loop (bad place to force a split, hard to remove)
- !(phi.region() instanceof LoopNode && i==2 && phi.in(i) instanceof PhiNode pp && pp.region()==phi.region()) )
+ !(phi.region() instanceof LoopNode && i==2 && (phi.in(i) instanceof PhiNode pp && pp.region()==phi.region())) )
// Split before phi-use in prior block
insertBefore(phi,i, "use/loop/phi",round,lrg);
} else {
// Others check uses
for( int i=1; i<n.nIns(); i++ ) {
- if( lrgSame(n.in(i),lrg) &&
- (min==max || n.in(i) instanceof MachNode mach && mach.isClone() || n.cfg0().loopDepth() <= min) )
- // Preserve the new split; folding it through can undo progress.
- insertBefore(n,i,"use/loop/use",round,lrg,false);
+ // This is a LRG use
+ // splitting in inner loop or at loop border
+ if( lrgSame( n.in( i ), lrg ) &&
+ (min == max || (n.in(i) instanceof MachNode mach && mach.isClone()) || n.cfg0().loopDepth() <= min) )
+ // Split before in this block
+ insertBefore( n, i, "use/loop/use", round,lrg, false );
}
}
}
@@ -403,6 +420,22 @@ public class RegAlloc {
int min = (int)ld;
int max = (int)(ld>>32);
int d = cfg.loopDepth();
+ // if n will lower the min loop and is in the tail end of the loop
+ // header, splitting "around" the loop will not help. Treat n as being
+ // in the loop.
+ if( d < min ) {
+ if( cfg.uctrl() instanceof LoopNode loop && loop.entry()==cfg ) {
+ for( int i=cfg.nOuts()-2; i>=0; i-- ) {
+ Node out = cfg.out(i);
+ if( n==out )
+ { d = loop.loopDepth(); break; } // Treat n as being "in the loop"
+ if( !((out instanceof MachNode mach && mach.isClone()) || out instanceof SplitNode ) )
+ break; // Treat b as "normal", out of loop
+ }
+ }
+ }
+
+ // lower min, raise max, and re-fold
min = Math.min(min,d);
max = Math.max(max,d);
return ((long)max<<32) | min;
@@ -428,7 +461,6 @@ public class RegAlloc {
for( Node n : _ns ) assert !n.isDead();
}
- void insertBefore(Node n, int i, String kind, byte round, LRG lrg) { insertBefore(n,i,kind,round,lrg,true); }
void insertBefore(Node n, int i, String kind, byte round, LRG lrg, boolean skip) {
Node def = n.in(i);
// Phi splitting may visit the synthetic never-exit arm.
@@ -449,6 +481,9 @@ public class RegAlloc {
if( skip && def instanceof SplitNode && cfg==def.cfg0() )
n.in(i).setDefOrdered(1,def.in(1));
}
+ void insertBefore(Node n, int i, String kind, byte round, LRG lrg) {
+ insertBefore(n,i,kind,round,lrg,true);
+ }
// Replace uses of `def` with `split`, and insert `split` immediately after
// `def` in the basic block.
@@ -486,18 +521,34 @@ public class RegAlloc {
// -----------------------
// POST PASS: Remove empty spills that biased-coloring made
private void postColor() {
+ int maxReg = -1;
for( CFGNode bb : _code._cfg ) { // For all ops
+ if( bb instanceof FunNode fun )
+ maxReg = -1; // Reset for new function
+ // Compute frame size, based on arguments and largest reg seen
+ if( bb instanceof ReturnNode ret )
+ ret.fun().computeFrameAdjust(_code,maxReg);
+ // Raise frame size by max stack args passed, even if ignored
+ if( bb instanceof CallEndNode cend )
+ maxReg = Math.max(maxReg,cend._xslot);
+
for( int j=0; j<bb.nOuts(); j++ ) {
Node n = bb.out(j);
- if( !(n instanceof SplitNode lo) ) continue;
- int defreg = lrg(n )._reg;
+ if( lrg(n)!=null )
+ maxReg = Math.max(maxReg,lrg(n)._reg+1);
+ // Raise frame size by max stack args passed to New
+ if( n instanceof NewNode nnn )
+ maxReg = Math.max(maxReg,nnn._xslot);
+
+ if( !(n instanceof SplitNode ) ) continue;
+ int defreg = lrg(n )._reg;
int usereg = lrg(n.in(1))._reg;
// Attempt to bypass split
if( defreg != usereg && splitBypass(bb,j,n,defreg) )
usereg = lrg(n.in(1))._reg;
- // Split has same reg? Useless! Can remove it!
- if( defreg == usereg ) {
+ // Same register, or the next instruction can use the source directly.
+ if( defreg == usereg || splitForward(bb,j,n,usereg) ) {
n.removeSplit();
j--;
continue;
@@ -510,6 +561,18 @@ public class RegAlloc {
}
}
+ // A sole, adjacent use can read the source register directly. Nothing can
+ // clobber it in between; only the operand mask and two-address tie remain.
+ private boolean splitForward( CFGNode bb, int j, Node split, int reg ) {
+ if( split.nOuts()!=1 || j+1==bb.nOuts() ) return false;
+ Node use = split.out(0);
+ if( use!=bb.out(j+1) || use instanceof PhiNode || use instanceof CFGNode ||
+ !(use instanceof MachNode mach) ) return false;
+ int i = use._inputs.find(split);
+ RegMask mask = mach.regmap(i);
+ return mask!=null && mask.test(reg) && mach.twoAddress()!=i;
+ }
+
private boolean splitBypass( CFGNode bb, int j, Node lo, int defreg ) {
// Attempt to bypass split
Node hi = lo.in(1);
@@ -529,6 +592,8 @@ public class RegAlloc {
lo.setDefOrdered(1,hi.in(1));
return true;
}
+
+
private boolean clobbers(Node n, int reg) {
LRG lrg = lrg(n);
return lrg!=null && (lrg._reg==reg || lrg._reg==-1 && lrg._mask.size1() && lrg._mask.firstReg()==reg) ||
@@ -553,5 +618,4 @@ public class RegAlloc {
}
throw Utils.TODO();
}
-
}
diff --git a/src/main/java/com/seaofnodes/simple/codegen/RegMask.java b/src/main/java/com/seaofnodes/simple/codegen/RegMask.java
index 5227c11e..f73cd9a1 100644
--- a/src/main/java/com/seaofnodes/simple/codegen/RegMask.java
+++ b/src/main/java/com/seaofnodes/simple/codegen/RegMask.java
@@ -4,11 +4,11 @@ import com.seaofnodes.simple.Utils;
import com.seaofnodes.simple.SB;
/** RegMask
- * A "register mask" - 1 bit set for each allowed register. In addition
+ * 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
+ * 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
@@ -32,6 +32,7 @@ public class RegMask {
}
public RegMask(long bits ) { _bits0 = bits; }
public RegMask(long bits0, long bits1 ) { _bits0 = bits0; _bits1 = bits1; }
+ public RegMask(RegMask r) { _bits0 = r._bits0; _bits1 = r._bits1; }
private RegMask() { _bits0 = _bits1 = 0; }
// AND, with copy-on-write if changing
@@ -81,7 +82,7 @@ public class RegMask {
boolean isEmpty() { return _bits0==0 && _bits1==0; }
- boolean test( int reg ) {
+ public boolean test( int reg ) {
return ((reg<64 ? (_bits0 >> reg) : (_bits1 >> (reg-64))) & 1) != 0;
}
@@ -105,35 +106,17 @@ public class RegMask {
Machine mach = CodeGen.CODE._mach;
if( _bits0==0 && _bits1==0 ) return sb.p("[]");
sb.p("[");
+ String[] regs = mach.regs();
for( int i=0; i<64; i++ )
if( ((_bits0 >> i)&1) != 0 )
- sb.p(mach.reg(i)).p(",");
+ sb.p(reg(regs,i)).p(",");
for( int i=0; i<64; i++ )
if( ((_bits1 >> i)&1) != 0 )
- sb.p(mach.reg(i+64)).p(",");
+ sb.p(reg(regs,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;
+ public static String reg(String[] regs, int i) {
+ return i<regs.length ? regs[i] : "[stk#"+i+"]";
}
}
diff --git a/src/main/java/com/seaofnodes/simple/codegen/RegMaskRW.java b/src/main/java/com/seaofnodes/simple/codegen/RegMaskRW.java
new file mode 100644
index 00000000..1da9a3b1
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/codegen/RegMaskRW.java
@@ -0,0 +1,36 @@
+package com.seaofnodes.simple.codegen;
+
+// Mutable regmask(writable)
+public class RegMaskRW extends RegMask {
+ public RegMaskRW(long x, long y) { super(x,y); }
+ // clears bit at position r. Returns true if the mask is still not empty.
+ public boolean clr(int r) {
+ if( r < 64 ) _bits0 &= ~(1L<<(r ));
+ else _bits1 &= ~(1L<<(r-64));
+ return _bits0!=0 || _bits1!=0;
+ }
+ // sets bit at position r.
+ public void set(int r) {
+ if( r < 64 ) _bits0 |= (1L<<(r ));
+ else _bits1 |= (1L<<(r-64));
+ }
+ @Override RegMaskRW and( RegMask mask ) {
+ if( mask==null ) return this;
+ _bits0 &= mask._bits0;
+ _bits1 &= mask._bits1;
+ return this;
+ }
+ @Override RegMaskRW sub( RegMask mask ) {
+ if( mask==null ) return this;
+ _bits0 &= ~mask._bits0;
+ _bits1 &= ~mask._bits1;
+ return this;
+ }
+ public RegMaskRW or( RegMask mask ) {
+ if( mask!=null ) {
+ _bits0 |= mask._bits0;
+ _bits1 |= mask._bits1;
+ }
+ return this;
+ }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/AddNode.java b/src/main/java/com/seaofnodes/simple/node/AddNode.java
index 0fc3bad6..62b33ada 100644
--- a/src/main/java/com/seaofnodes/simple/node/AddNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/AddNode.java
@@ -89,7 +89,7 @@ public class AddNode extends Node {
// If lhs.in(2) is not a constant, we add ourselves as a dependency
// because if it later became a constant then we could make this
// transformation.
- if( lhs.in(2).addDep(this)._type.isConstant() && rhs._type.isConstant() )
+ if( addDep(lhs.in(2))._type.isConstant() && rhs._type.isConstant() )
return new AddNode(lhs.in(1),new AddNode(lhs.in(2),rhs).peephole());
diff --git a/src/main/java/com/seaofnodes/simple/node/BoolNode.java b/src/main/java/com/seaofnodes/simple/node/BoolNode.java
index 85bcae0f..d6a126f5 100644
--- a/src/main/java/com/seaofnodes/simple/node/BoolNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/BoolNode.java
@@ -87,6 +87,18 @@ abstract public class BoolNode extends Node {
Node copyF() { return new EQF(null,null); }
}
+ public static class NE extends BoolNode {
+ public NE(Node lhs, Node rhs) { super(lhs,rhs); }
+ public String op() { return "!="; }
+ TypeInteger doOp(TypeInteger i1, TypeInteger i2) {
+ if( i1==i2 && i1.isConstant() ) return TRUE;
+ if( i1._max < i2._min || i1._min > i2._max ) return FALSE;
+ return BOOL;
+ }
+ Node copy(Node lhs, Node rhs) { return new NE(lhs,rhs); }
+ Node copyF() { throw Utils.TODO(); }
+ }
+
public static class LT extends BoolNode {
public LT(Node lhs, Node rhs) { super(lhs,rhs); }
public String op() { return "<" ; }
diff --git a/src/main/java/com/seaofnodes/simple/node/BulkMemPhiNode.java b/src/main/java/com/seaofnodes/simple/node/BulkMemPhiNode.java
index e9d4b897..7d4f1fb8 100644
--- a/src/main/java/com/seaofnodes/simple/node/BulkMemPhiNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/BulkMemPhiNode.java
@@ -111,7 +111,7 @@ public class BulkMemPhiNode extends PhiNode {
// Normal graph-neighbor enqueueing flows from defs to uses, so retain
// an explicit forward dependency to revisit this Phi when the user
// sharpens.
- use.addDepForwards(this);
+ addDepForwards(use);
return switch(use) {
case ScopeNode scope -> 0;
case MemMergeNode mmm -> missingAlias(mmm);
diff --git a/src/main/java/com/seaofnodes/simple/node/CFGNode.java b/src/main/java/com/seaofnodes/simple/node/CFGNode.java
index 0e770659..47dfa311 100644
--- a/src/main/java/com/seaofnodes/simple/node/CFGNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/CFGNode.java
@@ -1,10 +1,9 @@
package com.seaofnodes.simple.node;
-import com.seaofnodes.simple.codegen.CodeGen;
-
import com.seaofnodes.simple.Ary;
import com.seaofnodes.simple.Parser;
import com.seaofnodes.simple.Utils;
+import com.seaofnodes.simple.codegen.CodeGen;
import com.seaofnodes.simple.type.*;
import java.util.BitSet;
import java.util.HashMap;
@@ -58,6 +57,13 @@ public abstract class CFGNode extends Node {
return c;
}
+ // Used by the encoding / final BB layout
+ public CFGNode uctrlSkipEmpty() {
+ CFGNode x = this, y;
+ while( x.nOuts() == 1 && (y=x.uctrl())!=null ) // Skip empty blocks
+ x = y;
+ return x;
+ }
// ------------------------------------------------------------------------
/**
@@ -91,8 +97,8 @@ public abstract class CFGNode extends Node {
while( lhs != rhs ) {
if( lhs==null || rhs==null ) return null;
int comp = lhs.idepth() - rhs.idepth();
- if( comp >= 0 ) lhs = (dep==null ? lhs : (CFGNode)lhs.addDep(dep)).idom();
- if( comp <= 0 ) rhs = (dep==null ? rhs : (CFGNode)rhs.addDep(dep)).idom();
+ if( comp >= 0 ) lhs = (dep==null ? lhs : dep.addDep(lhs)).idom();
+ if( comp <= 0 ) rhs = (dep==null ? rhs : dep.addDep(rhs)).idom();
}
return lhs;
}
@@ -110,9 +116,9 @@ public abstract class CFGNode extends Node {
public LoopNode loop() { return _ltree._head; }
public int loopDepth() { return _ltree==null ? 0 : _ltree.depth(); }
- LoopTree _ltree;
- int _pre; // Pre-order numbers for loop tree finding
- private static class LoopTree {
+ public LoopTree _ltree;
+ public int _pre; // Pre-order numbers for loop tree finding
+ static class LoopTree {
LoopTree _par;
final LoopNode _head;
int _depth;
@@ -196,4 +202,9 @@ public abstract class CFGNode extends Node {
(this instanceof CallNode && usecfg instanceof FunNode) ||
(this instanceof ReturnNode && usecfg instanceof CallEndNode);
}
+
+
+ public String label( CFGNode target ) {
+ return (target instanceof LoopNode ? "LOOP" : "L")+target._nid;
+ }
}
diff --git a/src/main/java/com/seaofnodes/simple/node/CProjNode.java b/src/main/java/com/seaofnodes/simple/node/CProjNode.java
index bfdcb04b..4d3d8d0d 100644
--- a/src/main/java/com/seaofnodes/simple/node/CProjNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/CProjNode.java
@@ -11,10 +11,10 @@ import com.seaofnodes.simple.type.TypeTuple;
public class CProjNode extends CFGNode {
// Which slice of the incoming multipart value
- public final int _idx;
+ public int _idx;
// Debugging label
- public final String _label;
+ public String _label;
public CProjNode(Node ctrl, int idx, String label) {
super(ctrl);
@@ -47,15 +47,21 @@ public class CProjNode extends CFGNode {
}
// Flip a negating if-test, to remove the not
- if( ctrl() instanceof IfNode iff && iff.pred().addDep(this) instanceof NotNode not )
+ if( ctrl() instanceof IfNode iff && addDep(iff.pred()) instanceof NotNode not )
return new CProjNode(new IfNode(iff.ctrl(),not.in(1)).peephole(),1-_idx,_idx==0 ? "False" : "True");
// Copy of some other input
return ((MultiNode)ctrl()).pcopy(_idx);
}
+ // Only called during basic-block layout, inverts a T/F CProj
+ public void invert() {
+ _label = _idx == 0 ? "False" : "True";
+ _idx = 1-_idx;
+ }
+
@Override
- boolean eq( Node n ) { return _idx == ((CProjNode)n)._idx; }
+ public boolean eq( Node n ) { return _idx == ((CProjNode)n)._idx; }
@Override
int hash() { return _idx; }
diff --git a/src/main/java/com/seaofnodes/simple/node/CallEndNode.java b/src/main/java/com/seaofnodes/simple/node/CallEndNode.java
index 0ee3d418..47e80ca9 100644
--- a/src/main/java/com/seaofnodes/simple/node/CallEndNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/CallEndNode.java
@@ -3,7 +3,7 @@ package com.seaofnodes.simple.node;
import com.seaofnodes.print.ExprPrinter;
import com.seaofnodes.simple.*;
-import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.type.*;
/**
@@ -39,7 +39,7 @@ public class CallEndNode extends CFGNode implements MultiNode {
return TypeTuple.RET.dual();
Type ret = Type.BOTTOM;
TypeMem mem = TypeMem.BOT;
- if( call.fptr().addDep(this)._type instanceof TypeFunPtr tfp ) {
+ if( addDep(call.fptr())._type instanceof TypeFunPtr tfp ) {
ret = tfp.ret();
// Here, if I can figure out I've found *all* callers, then I can meet
// across the linked returns and join with the function return type.
@@ -71,8 +71,9 @@ public class CallEndNode extends CFGNode implements MultiNode {
assert fun.in(1) instanceof StartNode && fun.in(2)==call;
// Disallow self-recursive inlining (loop unrolling by another name)
CFGNode idom = call;
- while( !(idom instanceof FunNode fun2) )
+ while( !(idom instanceof FunNode) )
idom = idom.idom();
+ // Inline?
if( idom != fun ) {
// Trivial inline: rewrite
_folding = true;
@@ -82,14 +83,16 @@ public class CallEndNode extends CFGNode implements MultiNode {
fun.setDef(2,call.ctrl()); // Bypass the Call;
fun.ret().setDef(3,null); // Return is folding also
CodeGen.CODE.addAll(fun._outputs);
+ // Inlining immediately blows all cache idepth fields past the inline point.
+ // Bump the global version number invalidating them en-masse.
CodeGen.CODE.invalidateIDepthCaches();
return this;
}
} else {
- fun.addDep(this);
+ addDep(fun);
}
} else { // Function ptr has multiple users (so maybe multiple call sites)
- fptr.addDep(this);
+ addDep(fptr);
}
}
@@ -103,7 +106,7 @@ public class CallEndNode extends CFGNode implements MultiNode {
Node fptr = call.fptr();
if( !(fptr instanceof ConstantNode) ||
!(fptr._type instanceof TypeFunPtr tfp && tfp.notNull() && tfp.isConstant()) )
- { fptr.addDep(this); return false; }
+ { addDep(fptr); return false; }
if( call.err()!=null ) return false;
ReturnNode ret = (ReturnNode)in(1);
FunNode fun = ret.fun();
@@ -164,13 +167,13 @@ public class CallEndNode extends CFGNode implements MultiNode {
private int smallBody(FunNode fun) {
ReturnNode ret = fun.ret();
for( Node in : ret._inputs )
- if( in!=null ) in.addDep(this);
+ if( in!=null ) addDep(in);
if( ret.ctrl()!=fun ) return 0;
TINYBODY[0] = fun;
int len=1;
for( int i=0; i<len; i++ ) {
Node n = TINYBODY[i];
- n.addDep(this);
+ addDep(n);
if( n==ret ) continue;
if( n instanceof ParmNode parm && parm.region()!=fun ) return 0;
if( n!=fun && !(n instanceof ParmNode) &&
@@ -182,7 +185,7 @@ public class CallEndNode extends CFGNode implements MultiNode {
int j=0;
while( j<len && TINYBODY[j]!=use ) j++;
if( j<len ) continue;
- if( len==TINYBODY.length ) { use.addDep(this); return 0; }
+ if( len==TINYBODY.length ) { addDep(use); return 0; }
TINYBODY[len++] = use;
}
}
@@ -192,4 +195,34 @@ public class CallEndNode extends CFGNode implements MultiNode {
@Override public Node pcopy(int idx) {
return _folding ? in(1).in(idx) : null;
}
+
+ // ------------
+ // MachNode specifics, shared across all CPUs
+ public int _xslot;
+ private RegMask _retMask;
+ private RegMask _kills;
+ public void cacheRegs(CodeGen code) {
+ // Return mask depends on TFP (either GPR or FPR)
+ _retMask = code._mach.retMask(call().tfp());
+ // Kill mask is all caller-saves, and any mirror stack slots for args
+ // in registers.
+ RegMaskRW kills = code._callerSave.copy();
+ // Start of stack slots
+ int maxReg = code._mach.regs().length;
+ // Incoming function arg slots, all low numbered in the RA
+ int fslot = fun()._maxArgSlot;
+ // Killed slots for this calls outgoing args
+ int xslot = code._mach.maxArgSlot(call().tfp());
+ _xslot = (maxReg+fslot)+xslot;
+ for( int i=0; i<xslot; i++ )
+ kills.set((maxReg+fslot)+i);
+ _kills = kills;
+ }
+ public String op() { return "cend"; }
+ public RegMask regmap(int i) { return null; }
+ public RegMask outregmap() { return null; }
+ public RegMask outregmap(int idx) { return idx==2 ? _retMask : null; }
+ public RegMask killmap() { return _kills; }
+ public void encoding( Encoding enc ) { }
+ public void asm(CodeGen code, SB sb) { }
}
diff --git a/src/main/java/com/seaofnodes/simple/node/CallNode.java b/src/main/java/com/seaofnodes/simple/node/CallNode.java
index 371a1d69..70c71d43 100644
--- a/src/main/java/com/seaofnodes/simple/node/CallNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/CallNode.java
@@ -52,6 +52,10 @@ public class CallNode extends CFGNode {
// Error if not a TFP
public TypeFunPtr tfp() { return (TypeFunPtr)fptr()._type; }
+ // Call is to an externally supplied code
+ public boolean external() { return false; }
+
+
// Find the Call End from the Call
public CallEndNode cend() {
// Always in use slot 0
@@ -89,6 +93,7 @@ public class CallNode extends CFGNode {
// Link: call calls target function. Linking makes the target FunNode
// point to this Call, and all his Parms point to the call arguments;
// also the CallEnd points to the Return.
+ Node progress = null;
if( fptr()._type instanceof TypeFunPtr tfp && tfp.nargs() == nargs() ) {
// If fidxs is negative, then infinite unknown functions
long fidxs = tfp.fidxs();
@@ -100,12 +105,12 @@ public class CallNode extends CFGNode {
TypeFunPtr tfp0 = tfp.makeFrom(fidx);
FunNode fun = CodeGen.CODE.link(tfp0);
if( fun!=null && !fun._folding && !linked(fun) )
- link(fun);
+ progress = link(fun);
}
}
}
- return null;
+ return progress;
}
// True if Fun is linked to this Call
@@ -118,7 +123,7 @@ public class CallNode extends CFGNode {
// Link so this calls fun
- private void link( FunNode fun ) {
+ private Node link( FunNode fun ) {
assert !linked(fun);
fun.addDef(this);
for( Node use : fun._outputs )
@@ -126,7 +131,7 @@ public class CallNode extends CFGNode {
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);
+ return this;
}
// Unlink all linked functions
@@ -136,7 +141,7 @@ public class CallNode extends CFGNode {
assert linked(fun);
int idx = fun._inputs.find(this);
for( Node use : fun._outputs )
- if( use instanceof ParmNode parm )
+ if( use instanceof ParmNode )
use.delDef(idx);
fun.delDef(idx);
cend().delDef(cend()._inputs.find(fun.ret()));
diff --git a/src/main/java/com/seaofnodes/simple/node/CalleeSaveNode.java b/src/main/java/com/seaofnodes/simple/node/CalleeSaveNode.java
index d4a937bd..0afc5827 100644
--- a/src/main/java/com/seaofnodes/simple/node/CalleeSaveNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/CalleeSaveNode.java
@@ -1,10 +1,8 @@
package com.seaofnodes.simple.node;
-import com.seaofnodes.simple.Utils;
+import com.seaofnodes.simple.codegen.Encoding;
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;
@@ -18,7 +16,7 @@ public class CalleeSaveNode extends ProjNode implements MachNode {
}
@Override public RegMask regmap(int i) { return null; }
@Override public RegMask outregmap() { return _mask; }
- @Override public int encoding(ByteArrayOutputStream bytes) { return 0; }
+ @Override public void encoding( Encoding enc ) { }
@Override public Type compute() { return Type.BOTTOM; }
@Override public Node idealize() { return null; }
diff --git a/src/main/java/com/seaofnodes/simple/node/CastNode.java b/src/main/java/com/seaofnodes/simple/node/CastNode.java
index 9aedf53e..5598acd8 100644
--- a/src/main/java/com/seaofnodes/simple/node/CastNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/CastNode.java
@@ -43,7 +43,7 @@ public class CastNode extends Node {
}
@Override
- boolean eq(Node n) {
+ public boolean eq(Node n) {
CastNode cast = (CastNode)n; // Contract
return _t==cast._t;
}
diff --git a/src/main/java/com/seaofnodes/simple/node/ConstantNode.java b/src/main/java/com/seaofnodes/simple/node/ConstantNode.java
index cadb6511..c4a41e5d 100644
--- a/src/main/java/com/seaofnodes/simple/node/ConstantNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/ConstantNode.java
@@ -49,7 +49,7 @@ public class ConstantNode extends Node {
if( fun!=null && fun._name != null )
return p.p("{ ").p(fun._name).p("}");
}
- return p.p(_con.print(new SB()));
+ return p.p(_con==null ? "---" : _con.print(new SB()));
}
@Override public boolean isConst() { return true; }
@@ -61,7 +61,7 @@ public class ConstantNode extends Node {
public Node idealize() { return null; }
@Override
- boolean eq(Node n) {
+ public boolean eq(Node n) {
ConstantNode con = (ConstantNode)n; // Contract
return _con==con._con;
}
diff --git a/src/main/java/com/seaofnodes/simple/node/FRefNode.java b/src/main/java/com/seaofnodes/simple/node/FRefNode.java
index bf1c5a00..e7333c23 100644
--- a/src/main/java/com/seaofnodes/simple/node/FRefNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/FRefNode.java
@@ -30,6 +30,6 @@ public class FRefNode extends ConstantNode {
public Parser.ParseException err() { return Parser.error("Undefined name '"+_n._name+"'",_n._loc); }
- @Override boolean eq(Node n) { return this==n; }
+ @Override public boolean eq(Node n) { return this==n; }
@Override int hash() { return _n._idx; }
}
diff --git a/src/main/java/com/seaofnodes/simple/node/FunNode.java b/src/main/java/com/seaofnodes/simple/node/FunNode.java
index 2c91d1fc..eb1e8259 100644
--- a/src/main/java/com/seaofnodes/simple/node/FunNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/FunNode.java
@@ -1,10 +1,13 @@
package com.seaofnodes.simple.node;
+
import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.Encoding;
+import com.seaofnodes.simple.codegen.RegMask;
import com.seaofnodes.simple.type.Type;
import com.seaofnodes.simple.type.TypeFunPtr;
import com.seaofnodes.simple.type.TypeTuple;
-
import java.util.BitSet;
import static com.seaofnodes.simple.codegen.CodeGen.CODE;
@@ -110,7 +113,8 @@ public class FunNode extends RegionNode {
return null;
}
- // Unfolded functions are roots; inlining reconnects their depth to the caller.
+ // Bypass Region idom, always assume depth == 1, one more than Start,
+ // unless folding then just a ID on input#1
@Override public int idepth() { return _folding ? super.idepth() : 1; }
// Bypass Region idom, always assume idom is Start
@Override public CFGNode idom(Node dep) { return cfg(1); }
@@ -162,4 +166,53 @@ public class FunNode extends RegionNode {
if( in ) body.set(n._nid);
return in;
}
+
+
+ // ------------
+ // MachNode specifics, shared across all CPUs
+
+ // Function may call other functions? X86 requires 16b aligned SP on
+ // outbound calls, but not leaf. Set during an unrelated RA scan.
+ public boolean _hasCalls;
+
+ // Maximum incoming argument slot, based on function type
+ public int _maxArgSlot;
+
+ // Frame adjust, including padding, set during early Encoding
+ public int _frameAdjust;
+
+ public String op() { return _frameAdjust==0 ? "entry" : "subi"; }
+ public void postSelect(CodeGen code) {
+ code.link(this);
+ // Max slot seen past space reserved for incoming arguments
+ _maxArgSlot = code._mach.maxArgSlot(sig());
+ }
+ public RegMask regmap(int i) { return null; }
+ public RegMask outregmap() { return null; }
+ public void encoding( Encoding enc ) { throw Utils.TODO(); }
+ public void asm(CodeGen code, SB sb) {
+ if( _frameAdjust!=0 )
+ sb.p("rsp -= #").p(_frameAdjust);
+ }
+
+ public void computeFrameAdjust(CodeGen code, int maxReg) {
+ // Max slot seen, or 0.
+ int maxSlot = Math.max(maxReg - code._mach.regs().length,_maxArgSlot);
+ // Frame adjust before rounding, can be negative for no spills
+ int size = maxSlot - _maxArgSlot;
+ _frameAdjust = size*8;
+ }
+
+ // Given a high register number and during encoding compute the stack
+ // pointer offset. Pre-RA we lack the fun._frameAdjust. We also need a
+ // FunNode.
+ public int computeStackOffset(CodeGen code, int reg) {
+ String[] regs = code._mach.regs();
+ int stackSlot = reg - regs.length;
+ int slotRotate = stackSlot < _maxArgSlot
+ ? stackSlot + (_frameAdjust>>3)
+ : stackSlot - _maxArgSlot;
+ return slotRotate*8;
+ }
+
}
diff --git a/src/main/java/com/seaofnodes/simple/node/IfNode.java b/src/main/java/com/seaofnodes/simple/node/IfNode.java
index 2c4cad70..81fbe170 100644
--- a/src/main/java/com/seaofnodes/simple/node/IfNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/IfNode.java
@@ -1,10 +1,10 @@
package com.seaofnodes.simple.node;
+import com.seaofnodes.print.ExprPrinter;
+import com.seaofnodes.simple.Utils;
import com.seaofnodes.simple.codegen.CodeGen;
-import com.seaofnodes.simple.IterPeeps;
import com.seaofnodes.simple.type.*;
-import java.util.HashSet;
public class IfNode extends CFGNode implements MultiNode {
@@ -17,7 +17,13 @@ public class IfNode extends CFGNode implements MultiNode {
@Override
public String label() { return "If"; }
- @Override protected String format() { return "if( %1 )"; }
+ @Override
+ protected ExprPrinter<Node> _print1(ExprPrinter<Node> p) {
+ p.p("if( ");
+ if( in(1)==null ) p.p("never");
+ else p.n(in(1));
+ return p.p(" )");
+ }
public Node ctrl() { return in(0); }
public Node pred() { return in(1); }
@@ -53,11 +59,40 @@ public class IfNode extends CFGNode implements MultiNode {
// test on either the true or false branch, that side wins.
if( !pred()._type.isHighOrConst() )
for( CFGNode dom = idom(), prior=this; dom!=null; prior = dom, dom = dom.idom() )
- if( dom.addDep(this) instanceof IfNode iff && iff.pred().addDep(this)==pred() && prior instanceof CProjNode prj ) {
+ if( addDep(dom) instanceof IfNode iff && addDep(iff.pred())==pred() && prior instanceof CProjNode prj ) {
setDef(1,con( prj._idx==0 ? 1 : 0 ));
return this;
}
return null;
}
+ // MachNode variants need to support this and negate the conditional test.
+ // The following CProjs will be inverted by the caller.
+ public void negate() { throw Utils.TODO(); }
+
+ // Negate the sense of a test
+ public static String negate( String bop ) {
+ return switch( bop ) {
+ case "<" -> ">=";
+ case "<=" -> ">" ;
+ case "==" -> "!=";
+ case "!=" -> "==";
+ case ">" -> "<=";
+ case ">=" -> "<" ;
+ default -> throw Utils.TODO();
+ };
+ }
+
+ // Swap compare operands
+ public static String swap( String bop ) {
+ return switch( bop ) {
+ case "<" -> ">" ;
+ case "<=" -> ">=";
+ case "==" -> "!=";
+ case "!=" -> "==";
+ case ">" -> "<" ;
+ case ">=" -> "<=";
+ default -> throw Utils.TODO();
+ };
+ }
}
diff --git a/src/main/java/com/seaofnodes/simple/node/LoadNode.java b/src/main/java/com/seaofnodes/simple/node/LoadNode.java
index b9905912..3084b4f7 100644
--- a/src/main/java/com/seaofnodes/simple/node/LoadNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/LoadNode.java
@@ -4,8 +4,8 @@ import com.seaofnodes.print.ExprPrinter;
import com.seaofnodes.simple.codegen.CodeGen;
import com.seaofnodes.simple.Parser;
-import com.seaofnodes.simple.Utils;
import com.seaofnodes.simple.type.*;
+import java.util.BitSet;
/**
* Load represents extracting a value from inside a memory object,
@@ -22,7 +22,7 @@ public class LoadNode extends MemOpNode {
* @param off The offset inside the struct base
*/
public LoadNode(Parser.Lexer loc, String name, int alias, Type glb, Node mem, Node ptr, Node off) {
- super(loc, name, alias, glb, mem, ptr, off);
+ super(loc, name, alias, true, glb, mem, ptr, off);
}
@Override boolean canDrop(MemOpNode other, Node dep) {
@@ -33,10 +33,10 @@ public class LoadNode extends MemOpNode {
// aggregates might lead to a clobber, so stop rather than search further.
private boolean clobbered(Node dep) {
Node mem = mem();
- mem.addDepForwards(dep);
+ dep.addDepForwards(mem);
for( Node use : mem._outputs ) {
if( use==null ) continue;
- use.addDepForwards(dep);
+ dep.addDepForwards(use);
if( use instanceof StoreNode || use instanceof PhiNode ||
use instanceof MemMergeNode ||
use instanceof NewNode || use instanceof CallNode ) return true;
@@ -69,17 +69,8 @@ public class LoadNode extends MemOpNode {
Node ptr = ptr();
Node mem = mem();
- // Simple Load-after-Store on same address.
- if( mem() instanceof StoreNode st &&
- _alias==st._alias && ptr == st.ptr() && off() == st.off() ) { // Must check same object
- assert _name.equals(st._name); // Equiv class aliasing is perfect
- return extend(st.val());
- }
-
- // Simple Load-after-New on same address.
- if( mem instanceof ProjNode p && p.in(0) instanceof NewNode nnn &&
- ptr == nnn.proj(0) ) // Must check same object
- return zero(nnn); // Load zero from new
+ Node win = find(mem,ptr,null,null);
+ if( win!=null ) return folded(win);
// Uplift control to a prior dominating load.
for( Node memuse : mem._outputs )
@@ -88,52 +79,7 @@ public class LoadNode extends MemOpNode {
cfg0()!=null && cfg0().domLCA(ld.cfg0(),this) == ld.cfg0() ) // Higher control means load is legal earlier
return ld;
- // Load-after-Store on same address, but bypassing provably unrelated
- // stores. This is a more complex superset of the above two peeps.
- // "Provably unrelated" is really weak.
- if( ptr instanceof ReadOnlyNode ro )
- ptr = ro.in(1);
- outer:
- while( true ) {
- mem.addDep(this);
- switch( mem ) {
- case MemMergeNode merge:
- mem = merge.alias(_alias);
- break;
- case StoreNode st:
- if( _alias==st._alias && ptr == st.ptr().addDep(this) && off() == st.off() )
- return extend(castRO(st.val())); // Proved equal
- // Can we prove unequal? Offsets do not overlap?
- if( _alias==st._alias && !off()._type.join(st.off()._type).isHigh() && // Offsets overlap
- !neverAlias(ptr,st.ptr()) ) // And might alias
- break outer; // Cannot tell, stop trying
- // Pointers cannot overlap
- mem = st.mem(); // Proved never equal
- break;
- case PhiNode phi:
- // Assume related
- phi.addDep(this);
- break outer;
- case ConstantNode top: break outer; // Assume shortly dead
- case ProjNode mproj: // Memory projection
- switch( mproj.in(0) ) {
- case NewNode nnn1:
- if( ptr instanceof ProjNode pproj && pproj.in(0) == mproj.in(0) )
- return zero(nnn1);
- if( !(ptr instanceof ProjNode pproj && pproj.in(0) instanceof NewNode) )
- break outer; // Cannot tell, ptr not related to New
- mem = nnn1.mem();// Bypass unrelated New
- break;
- case StartNode start: break outer;
- case CallEndNode cend: break outer; // TODO: Bypass no-alias call
- default: throw Utils.TODO();
- }
- break;
-
- default:
- break outer;
- }
- }
+ if( ptr instanceof ReadOnlyNode ro ) ptr=ro.in(1);
// Push a Load up through a Phi, as long as it collapses on at least
// one arm. If at a Loop, the backedge MUST collapse - else we risk
@@ -143,18 +89,26 @@ public class LoadNode extends MemOpNode {
// else ptr.x = e1; : e1;
// val = ptr.x; ptr.x = val;
if( mem() instanceof MemPhiNode memphi && memphi.region()._type == Type.CONTROL && memphi.nIns()== 3 &&
+ // Array access control must not be moved across the merge.
+ in(0)==null &&
// Offset can be hoisted
off() instanceof ConstantNode &&
// Pointer can be hoisted
hoistPtr(ptr,memphi) ) {
- // Profit on RHS/Loop backedge
- if( profit(memphi,2) ||
- // Else must not be a loop to count profit on LHS.
- (!(memphi.region() instanceof LoopNode) && profit(memphi,1)) ) {
- Node ld1 = ld(1);
- Node ld2 = ld(2);
- return new PhiNode(_name,_declaredType,memphi.region(),ld1,ld2);
+ // Returning to this memory means no change only if the pointer
+ // is also loop invariant. A pointer Phi may select another object.
+ Node stop = memphi.region() instanceof LoopNode &&
+ !(ptr instanceof PhiNode p && p.region()==memphi.region()) ? memphi : null;
+ if( find(memphi.in(2),ptr,stop,new BitSet())!=null ||
+ (!(memphi.region() instanceof LoopNode) && find(memphi.in(1),ptr,null,new BitSet())!=null) ) {
+ PhiNode phi = new PhiNode(_name,_declaredType,memphi.region()).keep();
+ phi.setType(_type); // Backedge searches may return this value Phi.
+ for( int i=1; i<memphi.nIns(); i++ ) {
+ Node p = ptr instanceof PhiNode pp && pp.region()==memphi.region() ? pp.in(i) : ptr;
+ phi.addDef(load(memphi.in(i),p,stop,phi));
+ }
+ return CodeGen.CODE.add(phi.unkeep());
}
}
@@ -169,9 +123,64 @@ public class LoadNode extends MemOpNode {
return castRO(new ConstantNode(zero).peephole());
}
- private Node ld( int idx ) {
- Node mem = mem(), ptr = ptr();
- LoadNode ld = new LoadNode(_loc,_name,_alias,_declaredType,mem.in(idx),ptr instanceof PhiNode && ptr.in(0)==mem.in(0) ? ptr.in(idx) : ptr,off());
+ // Search only; no new nodes or rewiring. Return a Store/New that folds,
+ // the unchanged loop memory, or a merge whose every arm folds. Null fails.
+ // Register dependencies so a later pointer/offset rewrite retries the query.
+ private Node find(Node mem, Node ptr, Node stop, BitSet visit) {
+ if( ptr instanceof ReadOnlyNode ro ) ptr=ro.in(1);
+ while( mem!=null ) {
+ addDep(mem);
+ if( mem==stop ) return mem;
+ switch( mem ) {
+ case MemMergeNode merge: mem=merge.alias(_alias); break;
+ case StoreNode st:
+ addDep(st.ptr());
+ if( _alias==st._alias && ptr==st.ptr() && off()==st.off() ) return st;
+ if( _alias==st._alias && !neverAlias(ptr,st.ptr()) &&
+ !addDep(off())._type.join(addDep(st.off())._type).isHigh() ) return null;
+ mem=st.mem();
+ break;
+ case ProjNode proj:
+ if( !(proj.in(0) instanceof NewNode nnn) ) return null;
+ if( ptr instanceof ProjNode p && p.in(0)==nnn ) return nnn;
+ if( !(ptr instanceof ProjNode p && p.in(0) instanceof NewNode) ) return null;
+ mem=nnn.mem(); // Distinct allocations cannot overlap.
+ break;
+ case MemPhiNode phi:
+ addDep(phi.region());
+ // Only the original loop closes a successful cycle. Another
+ // loop or an unfinished merge needs a separate proof.
+ if( visit==null || !(phi.region() instanceof RegionNode r) || r instanceof LoopNode ||
+ r.inProgress() || phi.inProgress() || r._type!=Type.CONTROL ) return null;
+ if( visit.get(phi._nid) ) return phi; // Already proved every arm.
+ for( int i=1; i<phi.nIns(); i++ )
+ if( find(phi.in(i),ptr,stop,visit)==null ) return null;
+ visit.set(phi._nid);
+ return phi;
+ default: return null;
+ }
+ }
+ return null;
+ }
+
+ private Node folded(Node win) {
+ return win instanceof StoreNode st ? extend(castRO(st.val())) : zero((NewNode)win);
+ }
+
+ // Materialize only after the profitability search succeeds. The value Phi
+ // stands for a load which comes back around the loop with unchanged memory.
+ private Node load(Node mem, Node ptr, Node stop, PhiNode value) {
+ Node win=find(mem,ptr,stop,new BitSet());
+ if( win!=null ) {
+ if( win==stop ) return value;
+ if( win instanceof MemPhiNode mp ) {
+ PhiNode phi=new PhiNode(_name,_declaredType,mp.region()).keep();
+ for( int i=1; i<mp.nIns(); i++ ) phi.addDef(load(mp.in(i),ptr,stop,value));
+ return phi.unkeep().peephole();
+ }
+ return folded(win).peephole();
+ }
+ LoadNode ld=new LoadNode(_loc,_name,_alias,_declaredType,mem,ptr,off());
ld.setDef(0,in(0));
return ld.peephole();
}
@@ -202,16 +211,6 @@ public class LoadNode extends MemOpNode {
return false;
}
- // Profitable if we find a matching Store on this Phi arm.
- private boolean profit(PhiNode phi, int idx) {
- Node px = phi.in(idx);
- if( px==null ) return false;
- px.addDep(this);
- if( px._type instanceof TypeMem mem && mem._t.isHighOrConst() ) return true;
- if( px instanceof StoreNode st1 && _alias==st1._alias && ptr()==st1.ptr() && off()==st1.off() ) return true;
- return false;
- }
-
// Read-Only is a deep property, and cannot be cast-away
private Node castRO(Node rez) {
if( (ptr()._type.isFinal() || _declaredType.isFinal()) && !rez._type.isFinal() )
diff --git a/src/main/java/com/seaofnodes/simple/node/LoopNode.java b/src/main/java/com/seaofnodes/simple/node/LoopNode.java
index db353c1d..f86d7518 100644
--- a/src/main/java/com/seaofnodes/simple/node/LoopNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/LoopNode.java
@@ -2,9 +2,10 @@ package com.seaofnodes.simple.node;
import com.seaofnodes.simple.Parser;
import com.seaofnodes.simple.Utils;
+import com.seaofnodes.simple.codegen.CodeGen;
import com.seaofnodes.simple.type.Type;
-import com.seaofnodes.simple.type.TypeMem;
import com.seaofnodes.simple.type.TypeInteger;
+import com.seaofnodes.simple.type.TypeMem;
import java.util.BitSet;
import java.util.HashSet;
@@ -25,7 +26,7 @@ public class LoopNode extends RegionNode {
}
// Bypass Region idom, same as the default idom() using use in(1) instead of in(0)
- @Override public int idepth() { return validIDepth() ? _idepth : cacheIDepth(entry().idepth()+1); }
+ public int idepth() { return validIDepth() ? _idepth : cacheIDepth(entry().idepth()+1); }
// Bypass Region idom, same as the default idom() using use in(1) instead of in(0)
@Override public CFGNode idom(Node dep) { return entry(); }
@@ -39,51 +40,63 @@ public class LoopNode extends RegionNode {
while( x != this ) {
if( x instanceof CProjNode exit && exit.in(0) instanceof IfNode iff ) {
CFGNode other = iff.cproj(1-exit._idx);
- if( other!=null && other.loopDepth() < loopDepth() )
+ if( other!=null && other._ltree != _ltree && nested(_ltree,other._ltree) )
return fun; // Found an exit, not an infinite loop
}
x = x.idom();
}
// Found a no-exit loop. Insert an exit
- NeverNode iff = new NeverNode(back());
- for( Node use : _outputs )
- if( use instanceof PhiNode )
- iff.addDef(use);
+ NeverNode iff = new NeverNode(back()); // Ideal never-branch
CProjNode t = new CProjNode(iff,0,"True" ).init();
CProjNode f = new CProjNode(iff,1,"False").init();
- setDef(2,f);
+ setDef(2,t); // True continues loop, False (never) exits loop
+ ReturnNode ret = fun.ret();
+ iff._ltree = t._ltree = _ltree;
+ ret._ltree = f._ltree = fun._ltree;
// Now fold control into the exit. Might have 1 valid exit, or an
// XCtrl or a bunch of prior NeverNode exits.
Node top = new ConstantNode(Type.TOP).peephole();
- ReturnNode ret = fun.ret();
- iff._ltree = f._ltree = _ltree;
- ret._ltree = t._ltree = fun._ltree;
+ MemMergeNode memout = new MemMergeNode(new ConstantNode(TypeMem.BOT).peephole());
+ memout.setDef(0,f);
+ for( Node u : _outputs )
+ if( u instanceof BulkMemPhiNode ) memout.setDef(1,u);
+ else if( u instanceof MemPhiNode phi ) memout.alias(phi._alias,phi);
+ memout.init();
+
Node ctrl = ret.ctrl(), mem = ret.mem(), expr = ret.expr();
- if( ctrl._type != Type.XCONTROL ) {
+ if( ctrl!=null && ctrl._type != Type.XCONTROL ) {
// Perfect aligned exit?
if( !(ctrl instanceof RegionNode r &&
mem instanceof PhiNode pmem && pmem.region()==r &&
expr instanceof PhiNode prez && prez.region()==r ) ) {
// Nope, insert an aligned exit layer
- ctrl = new RegionNode(_loc,null,ctrl).init();
- mem = new PhiNode((RegionNode)ctrl,mem ).init();
- expr = new PhiNode((RegionNode)ctrl,expr).init();
+ RegionNode r = new RegionNode(_loc,null,ctrl).init();
+ ctrl = r; r._ltree = fun._ltree;
+ mem = new BulkMemPhiNode(ScopeNode.MEM0,r,mem).init();
+ expr = new PhiNode(r,expr).init();
}
// Append new Never exit
- ctrl.addDef(t );
- mem .addDef(top);
+ ctrl.addDef(f );
+ mem .addDef(memout);
expr.addDef(top);
} else {
- ctrl = t;
- mem = top;
+ ctrl = f;
+ mem = memout;
expr = top;
}
- ((CFGNode)ctrl)._ltree = fun._ltree;
ret.setDef(0,ctrl);
ret.setDef(1,mem );
ret.setDef(2,expr);
return fun;
}
+
+ private static boolean nested(LoopTree inner, LoopTree outer) {
+ for( ; inner!=null; inner = inner._par )
+ if( inner == outer )
+ return true;
+ return false;
+ }
+
}
diff --git a/src/main/java/com/seaofnodes/simple/node/MachNode.java b/src/main/java/com/seaofnodes/simple/node/MachNode.java
index ecdbc651..f63b3651 100644
--- a/src/main/java/com/seaofnodes/simple/node/MachNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/MachNode.java
@@ -1,10 +1,7 @@
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;
+import com.seaofnodes.simple.codegen.*;
public interface MachNode {
@@ -12,7 +9,7 @@ public interface MachNode {
default Machine machine() { return CodeGen.CODE._mach; }
// Run a post-instruction-selection action
- default void postSelect() { }
+ default void postSelect( CodeGen code ) { }
// Register mask allowed on input i. 0 for no register.
RegMask regmap( int i );
@@ -47,8 +44,8 @@ public interface MachNode {
// Make a clone of a cheap instruction
default Node copy() { return null; }
- // Encoding is appended into the byte array; size is returned
- int encoding(ByteArrayOutputStream bytes);
+ // Encoding is appended, relocations gathered.
+ void encoding( Encoding enc );
// Human-readable form appended to the SB. Things like the encoding,
// indentation, leading address or block labels not printed here.
diff --git a/src/main/java/com/seaofnodes/simple/node/MemMergeNode.java b/src/main/java/com/seaofnodes/simple/node/MemMergeNode.java
index c8a76714..421e4864 100644
--- a/src/main/java/com/seaofnodes/simple/node/MemMergeNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/MemMergeNode.java
@@ -18,7 +18,6 @@ public class MemMergeNode extends Node {
this(bulk);
alias(alias,precise);
}
-
public Node alias(int alias) {
Node n = alias < nIns() ? in(alias) : null;
assert n!=null || !(in(1) instanceof BulkMemPhiNode bulk) || !bulk.isSplit(alias);
@@ -37,12 +36,12 @@ public class MemMergeNode extends Node {
// join it with the incoming contents for all previously allocated objects.
// Phis use their cached types, so this query does not recurse around loops.
static Type contents(Node mem, int alias, Node dep) {
- mem.addDep(dep);
+ dep.addDep(mem);
if( mem instanceof MemMergeNode merge )
return contents(merge.alias(alias),alias,dep);
if( mem instanceof ProjNode proj && proj.in(0) instanceof NewNode nnn ) {
assert proj._idx==1 && nnn.field(alias)!=null;
- nnn.addDep(dep);
+ dep.addDep(nnn);
return contents(nnn.mem(),alias,dep).meet(nnn.field(alias)._type.makeZero());
}
// Function parameters and call results may contain arbitrary heap values.
diff --git a/src/main/java/com/seaofnodes/simple/node/MemOpNode.java b/src/main/java/com/seaofnodes/simple/node/MemOpNode.java
index 4f404846..adfc80a5 100644
--- a/src/main/java/com/seaofnodes/simple/node/MemOpNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/MemOpNode.java
@@ -12,26 +12,49 @@ import java.util.ArrayList;
/**
* Convenience common base for Load and Store.
+ *
+ * 0 - Control
+ * 1 - Memory
+ * 2 - Base of object, OOP
+ * 3 - Offset, integer types
+ * 4 - Value, for Stores only
*/
public abstract class MemOpNode extends Node {
- public final String _name;
+ // The equivalence alias class
public final int _alias;
+
+ // True if load-like, false if store-like.
+ //
+ // Stores produce memory (maybe as part of a tuple with other things),
+ // loads do not.
+ //
+ // Loads might pick up anti-dependences on prior Stores, and never cause an
+ // anti-dependence themselves.
+ //
+ // Stores must maximally sink to the least dominator use. Loads can be
+ // opportunistically hoisted.
+ public final boolean _isLoad;
+
// Declared type; not final because it might be a forward-reference
// which will be lazily improved when the reference is declared.
public Type _declaredType;
+
+ // A debug name, no semantic meaning
+ public final String _name;
// Source location for late reported errors
public final Parser.Lexer _loc;
- public MemOpNode(Parser.Lexer loc, String name, int alias, Type glb, Node mem, Node ptr, Node off) {
+ public MemOpNode(Parser.Lexer loc, String name, int alias, boolean isLoad, Type glb, Node mem, Node ptr, Node off) {
super(null, mem, ptr, off);
_name = name;
_alias = alias;
_declaredType = glb;
_loc = loc;
+ _isLoad = isLoad;
}
- public MemOpNode(Parser.Lexer loc, String name, int alias, Type glb, Node mem, Node ptr, Node off, Node value) {
- this(loc,name, alias, glb, mem, ptr, off);
+ public MemOpNode(Parser.Lexer loc, String name, int alias, boolean isLoad, Type glb, Node mem, Node ptr, Node off, Node value) {
+ this(loc,name, alias, isLoad, glb, mem, ptr, off);
addDef(value);
}
public MemOpNode( Node mach, MemOpNode mop ) {
@@ -39,13 +62,28 @@ public abstract class MemOpNode extends Node {
_name = mop==null ? null : mop._name;
_alias = mop==null ? 0 : mop._alias;
_loc = mop==null ? null : mop._loc;
+ _isLoad= mop==null ? true : mop._isLoad;
_declaredType = mop==null ? Type.BOTTOM : mop._declaredType;
+ if( mop==null )
+ throw Utils.TODO("Load or not");
+ }
+
+ // Used by M2 when translating graph to Simple
+ public MemOpNode( boolean isLoad ) {
+ super((Node)null);
+ _name = null;
+ _alias = 0;
+ _loc = null;
+ _isLoad = isLoad;
+ _declaredType = Type.BOTTOM;
}
//
static String mlabel(String name) { return "[]".equals(name) ? "ary" : ("#".equals(name) ? "len" : name); }
String mlabel() { return mlabel(_name); }
+ @Override public boolean isMem() { return !_isLoad; }
+
public Node mem() { return in(1); }
public Node ptr() { return in(2); }
public Node off() { return in(3); }
@@ -57,12 +95,12 @@ public abstract class MemOpNode extends Node {
// Extra conditions for factoring matching memory operations through a Phi.
// The caller has already checked the opcode, input shape and control.
boolean canDrop(MemOpNode other, Node dep) {
- ptr().addDep(dep);
+ dep.addDep(ptr());
return _alias==other._alias && _declaredType==other._declaredType && err()==null;
}
@Override
- boolean eq(Node n) {
+ public boolean eq(Node n) {
MemOpNode mem = (MemOpNode)n; // Invariant
return _alias==mem._alias; // When comparing types error to use "equals"; always use "=="
}
@@ -98,7 +136,7 @@ public abstract class MemOpNode extends Node {
if( (slice==mem) || (slice==null && merge.in(1)==mem) )
antiDeps(merge,deps,visit);
} else if( use instanceof CallNode ||
- (use instanceof MemOpNode st && st.isMem() && st._alias==_alias) ||
+ (use instanceof MemOpNode st && !st._isLoad && st._alias==_alias) ||
(use instanceof NewNode nnn && nnn.field(_alias)!=null) ||
(use instanceof MemPhiNode phi && phi._alias==_alias) ||
(use instanceof BulkMemPhiNode phi && !phi.isSplit(_alias)) ) {
diff --git a/src/main/java/com/seaofnodes/simple/node/MemPhiNode.java b/src/main/java/com/seaofnodes/simple/node/MemPhiNode.java
index df2c72f7..1349cadb 100644
--- a/src/main/java/com/seaofnodes/simple/node/MemPhiNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/MemPhiNode.java
@@ -25,7 +25,7 @@ public class MemPhiNode extends PhiNode {
if( r.inProgress() ) return _declaredType;
Type t = Type.TOP;
for( int i=1; i<nIns(); i++ )
- if( r.in(i).addDep(this)._type!=Type.XCONTROL )
+ if( addDep(r.in(i))._type!=Type.XCONTROL )
t = t.meet(MemMergeNode.contents(in(i),_alias,this));
return TypeMem.make(_alias,t);
}
@@ -44,6 +44,6 @@ public class MemPhiNode extends PhiNode {
return super.idealize();
}
- @Override boolean eq(Node n) { return _alias==((MemPhiNode)n)._alias && super.eq(n); }
+ @Override public boolean eq(Node n) { return _alias==((MemPhiNode)n)._alias && super.eq(n); }
@Override int hash() { return _alias; }
}
diff --git a/src/main/java/com/seaofnodes/simple/node/NeverNode.java b/src/main/java/com/seaofnodes/simple/node/NeverNode.java
index 7e42ccf9..66879b66 100644
--- a/src/main/java/com/seaofnodes/simple/node/NeverNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/NeverNode.java
@@ -10,7 +10,8 @@ import com.seaofnodes.simple.type.TypeTuple;
// "Never true" for infinite loop exits
public class NeverNode extends IfNode {
- public NeverNode(Node ctrl) { super(ctrl,Parser.ZERO); }
+ public NeverNode(NeverNode ctrl) { super(ctrl); }
+ public NeverNode(Node ctrl) { super(ctrl,null); }
@Override public String label() { return "Never"; }
diff --git a/src/main/java/com/seaofnodes/simple/node/NewNode.java b/src/main/java/com/seaofnodes/simple/node/NewNode.java
index 68871918..4179821c 100644
--- a/src/main/java/com/seaofnodes/simple/node/NewNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/NewNode.java
@@ -2,6 +2,9 @@ package com.seaofnodes.simple.node;
import com.seaofnodes.print.ExprPrinter;
+import com.seaofnodes.simple.SB;
+import com.seaofnodes.simple.Utils;
+import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.type.*;
/** Allocate a zeroed object. Inputs {ctrl, $mem, size};
@@ -49,8 +52,42 @@ public class NewNode extends Node implements MultiNode {
public Node idealize() { return null; }
@Override
- boolean eq(Node n) { return this == n; }
+ public boolean eq(Node n) { return this == n; }
@Override
int hash() { return _ptr.hashCode(); }
+
+ // ------------
+ // MachNode specifics, shared across all CPUs
+ public int _arg2Reg, _xslot;
+ private RegMask _arg3Mask;
+ private RegMask _retMask;
+ private RegMask _kills;
+ public void cacheRegs(CodeGen code) {
+ _arg2Reg = code._mach.callArgMask(TypeFunPtr.CALLOC,2,0).firstReg();
+ _arg3Mask = code._mach.callArgMask(TypeFunPtr.CALLOC,3,0);
+ // Return mask depends on TFP (either GPR or FPR)
+ _retMask = code._mach.retMask(TypeFunPtr.CALLOC);
+ // Kill mask is all caller-saves, and any mirror stack slots for args
+ // in registers.
+ RegMaskRW kills = code._callerSave.copy();
+ // Start of stack slots
+ int maxReg = code._mach.regs().length;
+ // Incoming function arg slots, all low numbered in the RA
+ int fslot = cfg0().fun()._maxArgSlot;
+ // Killed slots for this calls outgoing args
+ int xslot = code._mach.maxArgSlot(TypeFunPtr.CALLOC);
+ _xslot = (maxReg+fslot)+xslot;
+ for( int i=0; i<xslot; i++ )
+ kills.set((maxReg+fslot)+i);
+ _kills = kills;
+ }
+ public String op() { return "alloc"; }
+ public RegMask regmap(int i) { return i==2 ? _arg3Mask : null; }
+ public RegMask outregmap() { return null; }
+ public RegMask outregmap(int idx) { return idx==0 ? _retMask : null; }
+ public RegMask killmap() { return _kills; }
+ public void asm(CodeGen code, SB sb) {
+ sb.p("#calloc, ").p(code.reg(size()));
+ }
}
diff --git a/src/main/java/com/seaofnodes/simple/node/Node.java b/src/main/java/com/seaofnodes/simple/node/Node.java
index e038d351..8b0647d0 100644
--- a/src/main/java/com/seaofnodes/simple/node/Node.java
+++ b/src/main/java/com/seaofnodes/simple/node/Node.java
@@ -217,13 +217,23 @@ public abstract class Node extends BaseNode<Node> implements Cloneable {
assert isDead(); // Really dead now
}
+ // Preserve CFG use-ordering when killing
+ public void killOrdered() {
+ CFGNode cfg = cfg0();
+ cfg._outputs.remove(cfg._outputs.find(this));
+ _inputs.set(0,null);
+ kill();
+ }
+
// Mostly used for asserts and printing.
public boolean isDead() { return isUnused() && nIns()==0 && _type==null; }
// Shortcuts to stop DCE mid-parse
// Add bogus null use to keep node alive
+ @SuppressWarnings("unchecked")
public <N extends Node> N keep() { addUse(null); return (N)this; }
// Remove bogus null.
+ @SuppressWarnings("unchecked")
public <N extends Node> N unkeep() { delUse(null); return (N)this; }
// Test "keep" status
public boolean iskeep() { return _outputs.find(null) != -1; }
@@ -270,6 +280,8 @@ public abstract class Node extends BaseNode<Node> implements Cloneable {
int i;
if( use instanceof PhiNode phi ) {
cfg = phi.region().cfg(uidx);
+ if( cfg instanceof CProjNode && cfg.in(0) instanceof NeverNode nvr )
+ cfg = nvr.cfg0();
i = cfg.nOuts()-1;
} else {
i = cfg._outputs.find(use);
@@ -281,7 +293,7 @@ public abstract class Node extends BaseNode<Node> implements Cloneable {
use.setDefOrdered(uidx,this);
}
- public void setDefOrdered(int idx, Node def) {
+ 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 ) {
@@ -294,13 +306,6 @@ public abstract class Node extends BaseNode<Node> implements Cloneable {
setDef(idx,def);
}
- public void killOrdered() {
- CFGNode cfg = cfg0();
- cfg._outputs.remove(cfg._outputs.find(this));
- _inputs.set(0,null);
- kill();
- }
-
public void removeSplit() {
CFGNode cfg = cfg0();
cfg._outputs.remove(cfg._outputs.find(this));
@@ -427,6 +432,7 @@ public abstract class Node extends BaseNode<Node> implements Cloneable {
return old;
}
+ @SuppressWarnings("unchecked")
public <N extends Node> N init() { _type = compute(); return (N)this; }
/**
@@ -490,27 +496,23 @@ public abstract class Node extends BaseNode<Node> implements Cloneable {
* or output of this node, that is, it is at least one step away. The node
* being added must benefit from this node being peepholed.
*/
- Node addDep( Node dep ) {
- return addDep(dep,false);
- }
+ <N extends Node> N addDep( N dep ) { return addDep(dep,false); }
- // A bulk Phi inspects its users' partitions. Def-to-use propagation does
- // not revisit that producer when an immediate user's partition changes.
- Node addDepForwards(Node dep) { return addDep(dep,true); }
+ // Keep an explicit dependency when inspecting an immediate user's shape.
+ <N extends Node> N addDepForwards(N dep) { return addDep(dep,true); }
- private Node addDep(Node dep, boolean forwards) {
+ private <N extends Node> N addDep(N dep, boolean forwards) {
// Running peepholes during the big assert cannot have side effects
// like adding dependencies.
- if( CODE._midAssert ) return this;
+ if( CODE._midAssert ) return dep;
var obs = CodeGen.CODE._midAssert ? null : CodeGen.CODE._obs;
if( obs != null ) obs.dep(this, dep);
- if( dep == null ) return this;
- if( _deps==null ) _deps = new Ary<>(Node.class);
- if( _deps .find(dep) != -1 ) return this; // Already on list
- if( !forwards && _inputs .find(dep) != -1 ) return this; // No need for deps on immediate neighbors
- if( !forwards && _outputs.find(dep) != -1 ) return this;
- _deps.add(dep);
- return this;
+ if( dep._deps==null ) dep._deps = new Ary<>(Node.class);
+ if( dep._deps .find(this) != -1 ) return dep; // Already on list
+ if( !forwards && dep._inputs .find(this) != -1 ) return dep; // No need for deps on immediate neighbors
+ if( !forwards && dep._outputs.find(this) != -1 ) return dep;
+ dep._deps.add(this);
+ return dep;
}
// Move the dependents onto a worklist, and clear for future dependents.
@@ -535,7 +537,7 @@ public abstract class Node extends BaseNode<Node> implements Cloneable {
}
// Subclasses add extra checks (such as ConstantNodes have same constant),
// and can assume "this!=n" and has the same Java class.
- boolean eq( Node n ) { return true; }
+ public boolean eq( Node n ) { return true; }
// Cached hash. If zero, then not computed AND this Node is NOT in the GVN
// table - and can have its edges hacked (which will change his hash
@@ -611,7 +613,7 @@ public abstract class Node extends BaseNode<Node> implements Cloneable {
boolean allCons(Node dep) {
for( int i=1; i<nIns(); i++ )
if( !(in(i)._type.isConstant()) ) {
- in(i).addDep(dep); // If in(i) becomes a constant later, will trigger some peephole
+ dep.addDep(in(i)); // If in(i) becomes a constant later, will trigger some peephole
return false;
}
return true;
@@ -622,6 +624,8 @@ public abstract class Node extends BaseNode<Node> implements Cloneable {
// Does not need to be implemented in isCFG() nodes.
Node copy(Node lhs, Node rhs) { throw Utils.TODO("Binary ops need to implement copy"); }
+ public Node copy() { return copyEmpty(); }
+
// Exact-class copy with fresh identity and no edges. Unlike machine
// rematerialization copies, this never registers or copies input edges.
public final Node copyEmpty() {
diff --git a/src/main/java/com/seaofnodes/simple/node/PhiNode.java b/src/main/java/com/seaofnodes/simple/node/PhiNode.java
index a752977b..e075ea5c 100644
--- a/src/main/java/com/seaofnodes/simple/node/PhiNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/PhiNode.java
@@ -14,7 +14,7 @@ public class PhiNode extends Node {
final Type _declaredType;
public PhiNode(String label, Type declaredType, Node... inputs) { super(inputs); _label = label; assert declaredType!=null; _declaredType = declaredType; }
- public PhiNode(PhiNode phi, String label, Type declaredType) { super(phi); _label = label; _declaredType = declaredType; }
+ public PhiNode(PhiNode phi, String label, Type declaredType) { super(phi); _label = label; _type = _declaredType = declaredType; }
public PhiNode(PhiNode phi) { super(phi); _label = phi._label; _declaredType = phi._declaredType; }
public PhiNode(RegionNode r, Node sample) {
@@ -54,7 +54,7 @@ public class PhiNode extends Node {
for (int i = 1; i < nIns(); i++)
// If the region's control input is live, add this as a dependency
// to the control because we can be peeped should it become dead.
- if( r.in(i).addDep(this)._type != Type.XCONTROL )
+ if( addDep(r.in(i))._type != Type.XCONTROL )
t = t.meet(in(i)._type);
return t;
}
@@ -82,8 +82,8 @@ public class PhiNode extends Node {
// If merging Phi(N, cast(N)) - we are losing the cast JOIN effects, so just remove.
if( nIns()==3 ) {
- if( in(1) instanceof CastNode cast && cast.in(1).addDep(this)==in(2) ) return in(2);
- if( in(2) instanceof CastNode cast && cast.in(1).addDep(this)==in(1) ) return in(1);
+ if( in(1) instanceof CastNode cast && addDep(cast.in(1))==in(2) ) return in(2);
+ if( in(2) instanceof CastNode cast && addDep(cast.in(1))==in(1) ) return in(1);
}
// If merging a null-checked null and the checked value, just use the value.
// if( val ) ..; phi(Region,False=0/null,True=val);
@@ -96,7 +96,7 @@ public class PhiNode extends Node {
Node val = in(3-nullx);
if( val instanceof CastNode cast )
val = cast.in(1);
- if( r.idom(this).addDep(this) instanceof IfNode iff && iff.pred().addDep(this)==val ) {
+ if( addDep(r.idom(this)) instanceof IfNode iff && addDep(iff.pred())==val ) {
// Must walk the idom on the null side to make sure we hit False.
CFGNode idom = (CFGNode)r.in(nullx);
while( idom != null && idom.nIns() > 0 && idom.in(0) != iff ) idom = idom.idom();
@@ -109,6 +109,8 @@ public class PhiNode extends Node {
return null;
}
+ // Same op on all Phi paths; all ops have only the Phi as a use.
+ // None have a control input.
private boolean same_op() {
Node op = in(1);
if( op instanceof CFGNode || op instanceof ConstantNode || op instanceof PhiNode ||
@@ -121,22 +123,22 @@ public class PhiNode extends Node {
if( op instanceof MemOpNode )
for( Node use : region()._outputs )
if( use instanceof BulkMemPhiNode ) {
- use.addDepForwards(this);
+ addDepForwards(use);
return false;
}
Node busy = null;
for( int i=1; i<nIns(); i++ ) {
Node n = in(i);
- if( region().in(i).addDep(this)._type==Type.XCONTROL ||
+ if( addDep(region().in(i))._type==Type.XCONTROL ||
op.getClass()!=n.getClass() || n.nIns()!=op.nIns() || n.in(0)!=null || !op.eq(n) ) return false;
if( n instanceof MemOpNode mem && !mem.canDrop((MemOpNode)op,this) ) return false;
// Moving a Store must remove the old effect, not duplicate it.
// Allow one shared arm: the other arms disappear into the single
// factored operation, usually reducing the total operation count.
- n.addDepForwards(this);
+ addDepForwards(n);
if( n.nOuts()>1 ) {
for( Node use : n._outputs )
- if( use!=null && use!=this ) use.addDepForwards(this);
+ if( use!=null && use!=this ) addDepForwards(use);
if( n instanceof StoreNode || busy!=null ) return false;
busy=n;
}
@@ -174,7 +176,7 @@ public class PhiNode extends Node {
if( cp.compute().isa(compute()) ) return cp;
for( int i=1; i<nIns(); i++ )
for( int j=1; j<in(i).nIns(); j++ )
- if( in(i).in(j)!=null ) in(i).in(j).addDep(this);
+ if( in(i).in(j)!=null ) addDep(in(i).in(j));
cp.kill();
return null;
}
@@ -189,7 +191,7 @@ public class PhiNode extends Node {
for( int i=1; i<nIns(); i++ ) {
// If the region's control input is live, add this as a dependency
// to the control because we can be peeped should it become dead.
- if( region().in(i).addDep(this)._type != Type.XCONTROL && in(i) != this )
+ if( addDep(region().in(i))._type != Type.XCONTROL && in(i) != this )
if( live == null || live == in(i) ) live = in(i);
else return null;
}
@@ -201,7 +203,7 @@ public class PhiNode extends Node {
if( !(region() instanceof RegionNode r) ) return false;
// When the region completes (is no longer in progress) the Phi can
// become a "all constants" Phi, and the "dep" might make progress.
- addDep(dep);
+ dep.addDep(this);
if( r.inProgress() ) return false;
return super.allCons(dep);
}
@@ -212,7 +214,7 @@ public class PhiNode extends Node {
}
// Never equal if inProgress
- @Override boolean eq( Node n ) {
+ @Override public boolean eq( Node n ) {
return !inProgress();
}
diff --git a/src/main/java/com/seaofnodes/simple/node/ProjNode.java b/src/main/java/com/seaofnodes/simple/node/ProjNode.java
index da5841f6..26b3a522 100644
--- a/src/main/java/com/seaofnodes/simple/node/ProjNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/ProjNode.java
@@ -47,7 +47,7 @@ public class ProjNode extends Node {
@Override public Node idealize() { return ((MultiNode)in(0)).pcopy(_idx); }
@Override
- boolean eq( Node n ) { return _idx == ((ProjNode)n)._idx; }
+ public boolean eq( Node n ) { return _idx == ((ProjNode)n)._idx; }
@Override
int hash() { return _idx; }
diff --git a/src/main/java/com/seaofnodes/simple/node/RegionNode.java b/src/main/java/com/seaofnodes/simple/node/RegionNode.java
index 8993a49f..79b7f53a 100644
--- a/src/main/java/com/seaofnodes/simple/node/RegionNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/RegionNode.java
@@ -3,6 +3,7 @@ package com.seaofnodes.simple.node;
import com.seaofnodes.print.ExprPrinter;
import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.Parser;
import com.seaofnodes.simple.codegen.CodeGen;
import com.seaofnodes.simple.type.Type;
import java.util.*;
@@ -48,7 +49,7 @@ public class RegionNode extends CFGNode {
if( !hasPhi() && // No Phi users, just a control user
in(1) instanceof CProjNode p1 &&
in(2) instanceof CProjNode p2 &&
- p1.addDepForwards(this).in(0)==p2.addDepForwards(this).in(0) &&
+ addDepForwards(p1).in(0)==addDepForwards(p2).in(0) &&
p1.in(0) instanceof IfNode iff ) {
// Replace with the iff.ctrl directly
if( nIns()==3 ) return iff.ctrl();
@@ -57,6 +58,29 @@ public class RegionNode extends CFGNode {
return delDef(2);
}
+ // Flatten stack regions (no loops involved)
+ if( getClass() == RegionNode.class ) {
+ for( int i=1; i<nIns(); i++ )
+ if( cfg(i) instanceof RegionNode region && region.getClass() == RegionNode.class && region.nIns()>2 && !hasMidUser(region) ) {
+ assert !region.inProgress();
+ // Fold Phis
+ for( Node use : _outputs ) {
+ if( use instanceof PhiNode phi ) {
+ PhiNode phi2 = phi.in(i) instanceof PhiNode phi2x && phi2x.region()==region ? phi2x : null;
+ for( int j=1; j<region.nIns(); j++ )
+ phi.addDef(phi2==null ? phi.in(i) : phi2.in(j));
+ CodeGen.CODE.add(phi);
+ }
+ }
+
+ // Fold Region
+ for( int j=1; j<region.nIns(); j++ )
+ addDef(region.in(j));
+ setDef(i,Parser.XCTRL);
+ return this;
+ }
+ }
+
return null;
}
@@ -99,6 +123,23 @@ public class RegionNode extends CFGNode {
return false;
}
+ // Is there a value in-between 2 Regions? Difficult to correctly fuse
+ // without extensive testing, so just ignore.
+ boolean hasMidUser(RegionNode r) {
+ for( Node use : r._outputs ) {
+ if( use==this ) continue;
+ if( use instanceof PhiNode ) {
+ for( Node data : use._outputs ) {
+ if( !(data instanceof PhiNode phi2) || phi2.region()!=this )
+ { addDep(use); addDep(data); return true; }
+ }
+ } else
+ { addDep(use); return true; } // Control user
+ }
+ return false;
+ }
+
+
// Immediate dominator of Region is a little more complicated.
@Override public int idepth() {
if( validIDepth() ) return _idepth;
@@ -121,6 +162,6 @@ public class RegionNode extends CFGNode {
public boolean inProgress() { return nIns()>1 && in(nIns()-1) == null; }
// Never equal if inProgress
- @Override boolean eq( Node n ) { return !inProgress(); }
+ @Override public boolean eq( Node n ) { return !inProgress(); }
}
diff --git a/src/main/java/com/seaofnodes/simple/node/ReturnNode.java b/src/main/java/com/seaofnodes/simple/node/ReturnNode.java
index e101ee09..e0ab863a 100644
--- a/src/main/java/com/seaofnodes/simple/node/ReturnNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/ReturnNode.java
@@ -5,6 +5,9 @@ import com.seaofnodes.print.ExprPrinter;
import com.seaofnodes.simple.Parser;
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.codegen.Encoding;
import com.seaofnodes.simple.type.*;
/** Keeps all preceding memory effects and the return value alive: {ctrl, $mem, value, rpc}. */
@@ -22,7 +25,7 @@ public class ReturnNode extends CFGNode {
public Node mem () { return in(1); }
public Node expr() { return in(2); }
public Node rpc () { return in(3); }
- public FunNode fun() { return _fun; }
+ @Override public FunNode fun() { return _fun; }
@Override
public String label() { return "Return"; }
@@ -118,4 +121,39 @@ public class ReturnNode extends CFGNode {
if( tp || tn ) sb.p("reference and ");
return Parser.error(sb.unchar(5).toString(),loc);
}
+
+
+ // ------------
+ // MachNode specifics, shared across all CPUs
+ public String op() {
+ return _fun._frameAdjust > 0 ? "addi" : "ret";
+ }
+ // Correct Nodes outside the normal edges
+ public void postSelect(CodeGen code) {
+ FunNode fun = (FunNode)rpc().in(0);
+ _fun = fun;
+ fun.setRet(this);
+ }
+ public RegMask regmap(int i) {
+ return i==2
+ ? CodeGen.CODE._mach.retMask(_fun.sig())
+ : CodeGen.CODE._retMasks[i];
+ }
+ public RegMask outregmap() { return null; }
+ public void encoding( Encoding enc ) { throw Utils.TODO(); }
+ public void asm(CodeGen code, SB sb) {
+ int frameAdjust = fun()._frameAdjust;
+ if( frameAdjust>0 )
+ sb.p("rsp += #").p(frameAdjust).p("\nret");
+ // Post code-gen, just print the "ret"
+ if( code._phase.ordinal() <= CodeGen.Phase.RegAlloc.ordinal() )
+ // Prints return reg (either RAX or XMM0), RPC and then the
+ // callee-save registers.
+ for( int i=2; i<nIns(); i++ )
+ sb.p(code.reg(in(i),fun())).p(" ");
+ // If we did not get the expected rpc, print which one we got
+ else if( code._regAlloc.regnum(rpc()) != code._mach.rpc() )
+ sb.p("[").p(code.reg(rpc())).p("]");
+ }
+
}
diff --git a/src/main/java/com/seaofnodes/simple/node/ShlNode.java b/src/main/java/com/seaofnodes/simple/node/ShlNode.java
index 84c19247..afd20062 100644
--- a/src/main/java/com/seaofnodes/simple/node/ShlNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/ShlNode.java
@@ -36,7 +36,7 @@ public class ShlNode extends LogicalNode {
if( (shl.value()&63)==0 && lhs._type instanceof TypeInteger )
return lhs;
// (x + c) << i => (x << i) + (c << i)
- if( lhs instanceof AddNode add && add.addDep(this).in(2)._type instanceof TypeInteger c && c.isConstant() ) {
+ if( lhs instanceof AddNode add && addDep(add).in(2)._type instanceof TypeInteger c && c.isConstant() ) {
long sum = c.value() << shl.value();
if( Integer.MIN_VALUE <= sum && sum <= Integer.MAX_VALUE )
return new AddNode(new ShlNode(_loc,add.in(1),rhs).peephole(), Parser.con(sum) );
diff --git a/src/main/java/com/seaofnodes/simple/node/SplitNode.java b/src/main/java/com/seaofnodes/simple/node/SplitNode.java
index 07166a7c..ff18b292 100644
--- a/src/main/java/com/seaofnodes/simple/node/SplitNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/SplitNode.java
@@ -13,7 +13,7 @@ public abstract class SplitNode extends MachConcreteNode {
@Override public String op() { return "mov"; }
@Override protected ExprPrinter<Node> _print1(ExprPrinter<Node> p) {
p.p("mov(");
- if( in(1)==null ) p.p("---");
+ if( in(1) == null ) p.p("---");
else p.n(in(1));
return p.close();
}
diff --git a/src/main/java/com/seaofnodes/simple/node/StopNode.java b/src/main/java/com/seaofnodes/simple/node/StopNode.java
index 524c6a9e..c88dbf51 100644
--- a/src/main/java/com/seaofnodes/simple/node/StopNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/StopNode.java
@@ -27,7 +27,8 @@ public class StopNode extends CFGNode {
if( ret()!=null ) return p.n(ret());
p.p("Stop[ ");
for( Node ret : _inputs )
- p.n(ret).p(" ");
+ if( ret!=null )
+ p.n(ret).p(" ");
return p.p("]");
}
diff --git a/src/main/java/com/seaofnodes/simple/node/StoreNode.java b/src/main/java/com/seaofnodes/simple/node/StoreNode.java
index 9e6689f0..a5b09663 100644
--- a/src/main/java/com/seaofnodes/simple/node/StoreNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/StoreNode.java
@@ -22,7 +22,7 @@ public class StoreNode extends MemOpNode {
* @param value Value to be stored
*/
public StoreNode(Parser.Lexer loc, String name, int alias, Type glb, Node mem, Node ptr, Node off, Node value, boolean init) {
- super(loc, name, alias, glb, mem, ptr, off, value);
+ super(loc, name, alias, false, glb, mem, ptr, off, value);
_init = init;
}
@@ -85,6 +85,11 @@ public class StoreNode extends MemOpNode {
}
}
+ // Store of zero after alloc
+ if( mem() instanceof ProjNode prj && prj.in(0) instanceof NewNode &&
+ prj.in(0)==ptr().in(0) && // Same NewNode memory & pointer
+ (val()._type==TypeInteger.ZERO || val()._type==Type.NIL ) )
+ return mem();
return null;
}
@@ -93,9 +98,9 @@ public class StoreNode extends MemOpNode {
// With no observers of the backedge store, only the last value matters.
// The entry load preserves the field even when the loop takes zero trips.
Node sink(MemPhiNode phi) {
- addDepForwards(phi);
+ phi.addDepForwards(this);
if( nOuts()!=1 ) return null;
- ptr().addDep(phi);
+ phi.addDep(ptr());
// Start with a fixed field of an allocation dominating the loop.
// In particular, do not sink stores through a loop-varying address.
if( !(ptr() instanceof ProjNode p) || !(p.in(0) instanceof NewNode obj) ||
@@ -106,20 +111,20 @@ public class StoreNode extends MemOpNode {
// before replacing this memory point with a Store.
for( Node use : loop._outputs )
if( use instanceof BulkMemPhiNode ) {
- use.addDepForwards(phi);
+ phi.addDepForwards(use);
return null;
}
- obj.addDep(phi);
+ phi.addDep(obj);
CFGNode ctrl = loop.entry();
while( ctrl!=null && ctrl!=obj.cfg0() ) {
- ctrl.addDep(phi);
+ phi.addDep(ctrl);
ctrl=ctrl.idom(phi);
}
if( ctrl==null ) return null;
Node mem = phi.in(1);
Node init = new LoadNode(_loc,_name,_alias,_declaredType,mem,ptr(),off()).peephole();
- Node value = new PhiNode(_name,_declaredType,loop,init,val()).peephole();
+ Node value = CodeGen.CODE.add(new PhiNode(_name,_declaredType,loop,init,val()).peephole());
Node sink = new StoreNode(_loc,_name,_alias,_declaredType,mem,ptr(),off(),value,_init);
// Break the memory cycle, killing the old store. Keep the Phi alive
// even if that store was its last use, until our caller replaces it.
@@ -136,7 +141,7 @@ public class StoreNode extends MemOpNode {
// when the other uses go away we can retry.
for( Node use : mem._outputs )
if( use != this )
- use.addDep(this);
+ addDep(use);
return false;
}
@@ -149,7 +154,7 @@ public class StoreNode extends MemOpNode {
TypeMemPtr tmp = (TypeMemPtr)ptr()._type;
if( (tmp._ro || tmp._obj.field(_name)._final) && !_init )
return Parser.error("Cannot modify final field '"+_name+"'",_loc);
- Type t = val()._type;
+ //Type t = val()._type;
//return _init || t.isa(_declaredType) ? null : Parser.error("Cannot store "+t+" into field "+_declaredType+" "+_name,_loc);
return null;
}
diff --git a/src/main/java/com/seaofnodes/simple/node/StructNode.java b/src/main/java/com/seaofnodes/simple/node/StructNode.java
index b9801f63..3782d64a 100644
--- a/src/main/java/com/seaofnodes/simple/node/StructNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/StructNode.java
@@ -39,7 +39,7 @@ public class StructNode extends Node {
public Node idealize() { return null; }
@Override
- boolean eq(Node n) { return _ts == ((StructNode)n)._ts; }
+ public boolean eq(Node n) { return _ts == ((StructNode)n)._ts; }
@Override
int hash() { return _ts==null ? 0 : _ts.hashCode(); }
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/AddARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/AddARM.java
index c4014571..76e7b9b5 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/AddARM.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/AddARM.java
@@ -1,34 +1,21 @@
package com.seaofnodes.simple.node.cpus.arm;
+import com.seaofnodes.isa.Arm64;
import com.seaofnodes.simple.*;
-import com.seaofnodes.simple.codegen.CodeGen;
-import com.seaofnodes.simple.codegen.RegMask;
+import com.seaofnodes.simple.codegen.*;
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); }
+ @Override public String op() { return "add"; }
+ @Override public RegMask regmap(int i) { return arm.RMASK; }
+ @Override public RegMask outregmap() { return arm.WMASK; }
- 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();
- }
-
+ // ADD (shifted register)
+ @Override public void encoding( Encoding enc ) { arm.r_reg(enc,this,Arm64.OP_ADD); }
// General form: "rd = rs1 + rs2"
@Override public void asm(CodeGen code, SB sb) {
sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" + ").p(code.reg(in(2)));
}
- @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
index 098ab8c1..9c2f64c0 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/AddFARM.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/AddFARM.java
@@ -1,31 +1,23 @@
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.isa.Arm64;
+import com.seaofnodes.simple.SB;
+import com.seaofnodes.simple.codegen.*;
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 String op() { return "addf"; }
+ @Override public RegMask regmap(int i) { return arm.DMASK; }
@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();
- }
+ //FADD (scalar)
+ @Override public void encoding( Encoding enc ) { arm.f_scalar(enc,this,Arm64.OPF_OP_ADD);}
// Default on double precision for now(64 bits)
- // General form: "vadd.32 rd = src1 + src2
+ // General form: "addf rd = src1 + src2
@Override public void asm(CodeGen code, SB sb) {
sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" + ").p(code.reg(in(2)));
}
- @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
index c8f5f8dd..52cf36ea 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/AddIARM.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/AddIARM.java
@@ -1,51 +1,28 @@
package com.seaofnodes.simple.node.cpus.arm;
+import com.seaofnodes.isa.Arm64;
import com.seaofnodes.simple.*;
-import com.seaofnodes.simple.codegen.CodeGen;
-import com.seaofnodes.simple.codegen.RegMask;
+import com.seaofnodes.simple.codegen.*;
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) {
+ final int _imm;
+ AddIARM(Node add, int imm) {
super(add);
_inputs.pop();
- _ti = ti;
+ _imm = imm;
}
-
- // Register mask allowed on input i.
- @Override
- public RegMask regmap(int i) {
- // assert i== i;
- return arm.RMASK;
+ @Override public String op() {
+ return _imm == 1 ? "inc" : (_imm == -1 ? "dec" : "addi");
}
-
- // 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();
+ @Override public RegMask regmap(int i) { return arm.RMASK; }
+ @Override public RegMask outregmap() { return arm.WMASK; }
+ //ADD (immediate)
+ @Override public void encoding( Encoding enc ) {
+ arm.imm_inst(enc,this, in(1), Arm64.OPI_ADD,_imm);
}
-
// General form: "addi rd = rs1 + imm"
- @Override
- public void asm(CodeGen code, SB sb) {
- sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" + #");
- _ti.print(sb);
+ @Override public void asm(CodeGen code, SB sb) {
+ sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" + #").p(_imm);
}
-
- @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
index 7655df40..40812d07 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/AndARM.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/AndARM.java
@@ -1,36 +1,20 @@
package com.seaofnodes.simple.node.cpus.arm;
+import com.seaofnodes.isa.Arm64;
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.codegen.*;
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
+ AndARM(Node and) { super(and); }
+ @Override public String op() { return "and"; }
+ @Override public String glabel() { return "&"; }
+ @Override public RegMask regmap(int i) { return arm.RMASK; }
+ @Override public RegMask outregmap() { return arm.WMASK; }
+ // AND (shifted register)
+ @Override public void encoding( Encoding enc ) { arm.r_reg(enc,this,Arm64.OP_AND); }
// General form: #rd = rs1 & rs2
@Override public void asm(CodeGen code, SB sb){
sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" & ").p(code.reg(in(2)));
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/AndIARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/AndIARM.java
index ee05654b..82d553aa 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/AndIARM.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/AndIARM.java
@@ -1,56 +1,25 @@
package com.seaofnodes.simple.node.cpus.arm;
-
+import com.seaofnodes.isa.Arm64;
import com.seaofnodes.simple.*;
-import com.seaofnodes.simple.codegen.CodeGen;
-import com.seaofnodes.simple.codegen.RegMask;
+import com.seaofnodes.simple.codegen.*;
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) {
+public class AndIARM extends MachConcreteNode implements MachNode {
+ final int _imm;
+ AndIARM(Node and, int imm) {
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;
+ _imm = imm;
}
-
- // Register mask allowed as a result. 0 for no register.
- @Override
- public RegMask outregmap() {
- return arm.RMASK;
+ @Override public String op() { return "andi"; }
+ @Override public RegMask regmap(int i) { return arm.RMASK; }
+ @Override public RegMask outregmap() { return arm.WMASK; }
+ @Override public void encoding( Encoding enc ) {
+ arm.imm_inst_n(enc,this, in(1), Arm64.OPI_AND,_imm);
}
-
- // 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 void asm(CodeGen code, SB sb) {
+ sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" & #").p(Arm64.decodeImm12(_imm));
}
-
- @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
index a79de49b..cce868f9 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/AsrARM.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/AsrARM.java
@@ -1,40 +1,19 @@
package com.seaofnodes.simple.node.cpus.arm;
+import com.seaofnodes.isa.Arm64;
import com.seaofnodes.simple.*;
-import com.seaofnodes.simple.codegen.CodeGen;
-import com.seaofnodes.simple.codegen.RegMask;
+import com.seaofnodes.simple.codegen.*;
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 String op() { return "sar"; }
+ @Override public RegMask regmap(int i) { return arm.RMASK; }
+ @Override public RegMask outregmap() { return arm.DMASK; }
+ @Override public void encoding( Encoding enc ) { arm.shift_reg(enc,this,Arm64.OP_ASR); }
@Override public void asm(CodeGen code, SB sb) {
-
sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" >> ").p(code.reg(in(2)));
}
- @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
index 099c3699..2d50e967 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/AsrIARM.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/AsrIARM.java
@@ -1,41 +1,32 @@
package com.seaofnodes.simple.node.cpus.arm;
-
-import com.seaofnodes.simple.*;
+import com.seaofnodes.isa.Arm64;
+import com.seaofnodes.simple.SB;
import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.Encoding;
import com.seaofnodes.simple.codegen.RegMask;
-import com.seaofnodes.simple.node.*;
-import com.seaofnodes.simple.type.Type;
-import com.seaofnodes.simple.type.TypeInteger;
-import java.io.ByteArrayOutputStream;
-import java.util.BitSet;
-import java.lang.StringBuilder;
+import com.seaofnodes.simple.node.MachConcreteNode;
+import com.seaofnodes.simple.node.MachNode;
+import com.seaofnodes.simple.node.Node;
-// 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();
+ final int _imm;
+ AsrIARM(Node asr, int imm) {
+ super(asr);
+ _inputs.pop();
+ _imm = imm;
}
-
- // General form
- // General form: "asri rd, rs1, imm"
+ @Override public String op() { return "asri"; }
+ @Override public RegMask regmap(int i) { return arm.RMASK; }
+ @Override public RegMask outregmap() { return arm.WMASK; }
+ @Override public void encoding( Encoding enc ) {
+ short rd = enc.reg(this);
+ short rn = enc.reg(in(1));
+ assert _imm > 0;
+ enc.add4(Arm64.imm_shift(Arm64.OPI_ASR,_imm, 0b111111, rn, rd));
+ }
+ // General form: "asri rd = rs1 >> imm"
@Override public void asm(CodeGen code, SB sb) {
- sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" >> #");
- _ti.print(sb);
+ sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" >> #").p(_imm);
}
-
- @Override public String op() { return "asri"; }
-}
+} \ No newline at end of file
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/BranchARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/BranchARM.java
index 1a5108cf..5a660c90 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/BranchARM.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/BranchARM.java
@@ -1,25 +1,23 @@
package com.seaofnodes.simple.node.cpus.arm;
+import com.seaofnodes.isa.Arm64;
import com.seaofnodes.simple.*;
-import com.seaofnodes.simple.codegen.CodeGen;
-import com.seaofnodes.simple.codegen.RegMask;
+import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.*;
-import java.io.ByteArrayOutputStream;
-
-
// Jump on flags, uses flags
-public class BranchARM extends IfNode implements MachNode{
- final String _bop;
+public class BranchARM extends IfNode implements MachNode, RIPRelSize {
+ String _bop;
BranchARM(IfNode iff, String bop ) {
super(iff);
_bop = bop;
}
-
+ @Override public String op() { return "j"+_bop; }
@Override public String label() { return op(); }
- @Override public void postSelect() {
+ @Override public void postSelect(CodeGen code) {
Node set = in(1);
+ if( set==null ) return; // Never-node cutout
Node cmp = set.in(1);
// Bypass an expected Set and just reference the cmp directly
if( set instanceof SetARM)
@@ -30,19 +28,50 @@ public class BranchARM extends IfNode implements MachNode{
@Override public RegMask regmap(int i) { assert i==1; return arm.FLAGS_MASK; }
@Override public RegMask outregmap() { return null; }
+ @Override public void negate() { _bop = negate(_bop); }
+
+ @Override public void encoding( Encoding enc ) {
+ // Assuming that condition flags are already set. These flags are set
+ // by comparison (or sub). No need for regs because it uses flags
+ if( in(1)!=null ) {
+ // B.cond
+ enc.add4( Arm64.b_cond(Arm64.OP_BRANCH, 0, Arm64.make_condition(_bop)) );
+ } else {
+ if( _bop=="!=" ) return; // Inverted, no code
+ enc.add4(Arm64.b(Arm64.OP_UJMP, 0));
+ }
+ enc.jump(this,cproj(0));
+ }
- // Encoding is appended into the byte array; size is returned
- @Override public int encoding(ByteArrayOutputStream bytes) {
+ // Delta is from opcode start
+ @Override public byte encSize(int delta) {
+ if( in(1)==null && _bop=="!=" ) return 0; // Inverted never-node, no code
+ if( -(1<<19) <= delta && delta < (1<<19) ) return 4;
+ // 2 word encoding needs a tmp register, must teach RA
throw Utils.TODO();
}
- @Override public void asm(CodeGen code, SB sb) {
- String src = code.reg(in(1));
- if( src!="flags" ) sb.p(src);
+ // Delta is from opcode start
+ @Override public void patch( Encoding enc, int opStart, int opLen, int delta ) {
+ assert !( in(1)==null && _bop=="!=" ); // Inverted never-node, no code no patch
+ if( opLen==4 ) {
+ enc.patch4(opStart,Arm64.b_cond(Arm64.OP_BRANCH, delta, Arm64.make_condition(_bop)));
+ } else {
+ throw Utils.TODO();
+ }
}
- @Override public String op() { return "j"+_bop; }
- @Override public String comment() {
- return "L"+cproj(1)._nid+", L"+cproj(0)._nid;
+ @Override public void asm(CodeGen code, SB sb) {
+ if( in(1)!=null ) { // Never-node
+ String src = code.reg(in(1));
+ if( src!="flags" ) sb.p(src).p(" ");
+ } else if( _bop=="!=" ) {
+ sb.p("never");
+ return;
+ }
+ CFGNode prj = cproj(0).uctrlSkipEmpty();
+ if( !prj.blockHead() ) prj = prj.cfg0();
+ sb.p(label(prj));
}
+ @Override public String comment() { return "L"+cproj(1)._nid; }
}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/CallARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/CallARM.java
index 0f883847..57d7ef7c 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/CallARM.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/CallARM.java
@@ -1,13 +1,12 @@
package com.seaofnodes.simple.node.cpus.arm;
+import com.seaofnodes.isa.Arm64;
import com.seaofnodes.simple.*;
-import com.seaofnodes.simple.codegen.CodeGen;
-import com.seaofnodes.simple.codegen.RegMask;
+import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.*;
import com.seaofnodes.simple.type.TypeFunPtr;
-import java.io.ByteArrayOutputStream;
-public class CallARM extends CallNode implements MachNode {
+public class CallARM extends CallNode implements MachNode, RIPRelSize {
final TypeFunPtr _tfp;
final String _name;
@@ -19,27 +18,31 @@ public class CallARM extends CallNode implements MachNode {
_name = CodeGen.CODE.link(tfp)._name;
}
+ @Override public String op() { return "call"; }
@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; }
+ @Override public RegMask regmap(int i) { return arm.callInMask(_tfp,i,fun()._maxArgSlot); }
+ @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 encoding( Encoding enc ) {
+ enc.relo(this);
+ // BL
+ enc.add4(Arm64.b(Arm64.OP_CALL,0)); // Target patched at link time
+ }
+
+ // Delta is from opcode start, but X86 measures from the end of the 5-byte encoding
+ @Override public byte encSize(int delta) { return 4; }
+
+ // Delta is from opcode start
+ @Override public void patch( Encoding enc, int opStart, int opLen, int delta ) {
+ enc.patch4(opStart,Arm64.b(Arm64.OP_CALL,delta));
}
@Override public void asm(CodeGen code, SB sb) {
sb.p(_name).p(" ");
for( int i=0; i<nargs(); i++ )
- sb.p(code.reg(arg(i))).p(" ");
+ sb.p(code.reg(arg(i+2))).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
index 6177c5ea..b3c2f6c2 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/CallEndARM.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/CallEndARM.java
@@ -1,35 +1,8 @@
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"; }
-
+ CallEndARM( CallEndNode cend ) { super(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
index db85a9a9..fa0fbd81 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/CallRRARM.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/CallRRARM.java
@@ -1,26 +1,26 @@
package com.seaofnodes.simple.node.cpus.arm;
+import com.seaofnodes.isa.Arm64;
import com.seaofnodes.simple.*;
-import com.seaofnodes.simple.codegen.CodeGen;
-import com.seaofnodes.simple.codegen.RegMask;
+import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.*;
-import java.io.ByteArrayOutputStream;
public class CallRRARM extends CallNode implements MachNode {
- CallRRARM(CallNode call) {super(call);}
+ 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
+ : arm.callInMask(tfp(),i,fun()._maxArgSlot); // 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 encoding( Encoding enc ) {
+ // Needs a register, typically a jump-and-link-register opcode
+ // blr
+ short self = enc.reg(this);
+ enc.add4(Arm64.blr(Arm64.OP_CALLRARM, self));
}
@Override public void asm(CodeGen code, SB sb) {
@@ -29,4 +29,5 @@ public class CallRRARM extends CallNode implements MachNode {
sb.p(code.reg(arg(i))).p(" ");
sb.unchar(2);
}
+
}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/CastARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/CastARM.java
new file mode 100644
index 00000000..a5a4e112
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/CastARM.java
@@ -0,0 +1,18 @@
+package com.seaofnodes.simple.node.cpus.arm;
+
+import com.seaofnodes.simple.SB;
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.Encoding;
+import com.seaofnodes.simple.codegen.RegMask;
+import com.seaofnodes.simple.node.CastNode;
+import com.seaofnodes.simple.node.MachNode;
+
+public class CastARM extends CastNode implements MachNode {
+ public CastARM( CastNode cast ) { super(cast); }
+ @Override public String op() { return label(); }
+ @Override public RegMask regmap(int i) { assert i==1; return RegMask.FULL; }
+ @Override public RegMask outregmap() { return RegMask.FULL; }
+ @Override public int twoAddress( ) { return 1; }
+ @Override public void encoding( Encoding enc ) { }
+ @Override public void asm(CodeGen code, SB sb) { _t.print(sb.p(code.reg(in(1))).p(" isa ")); }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/CmpARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/CmpARM.java
index f4f6242d..ec5e8ff0 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/CmpARM.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/CmpARM.java
@@ -1,25 +1,18 @@
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.isa.Arm64;
+import com.seaofnodes.simple.SB;
+import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.*;
-import com.seaofnodes.simple.type.Type;
-import com.seaofnodes.simple.type.TypeFunPtr;
-import java.io.ByteArrayOutputStream;
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 regmap(int i) { 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();
- }
+ // SUBS (shifted register)
+ @Override public void encoding( Encoding enc ) { arm.r_reg_subs(enc,this,Arm64.OP_CMP); }
// General form: "cmp rs1, rs2"
@Override public void asm(CodeGen code, SB sb) {
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
index 9c287fcb..e82a2ee3 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/CmpFARM.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/CmpFARM.java
@@ -1,31 +1,22 @@
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.codegen.*;
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);}
+ @Override public String op() { return "cmpf"; }
+ @Override public RegMask regmap(int i) { return arm.DMASK; }
+ @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();
- }
-
+ // FCMP
+ @Override public void encoding( Encoding enc ) { arm.f_cmp(enc,this); }
// General form: "cmp d0, d1"
@Override public void asm(CodeGen code, SB sb) {
String dst = code.reg(this);
if( dst!="flags" ) sb.p(dst).p(" = ");
sb.p(code.reg(in(1))).p(", ").p(code.reg(in(2)));
}
-
- @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
index a0b10ba4..dbf9c2e3 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/CmpIARM.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/CmpIARM.java
@@ -1,46 +1,35 @@
package com.seaofnodes.simple.node.cpus.arm;
+import com.seaofnodes.isa.Arm64;
import com.seaofnodes.simple.*;
-import com.seaofnodes.simple.codegen.CodeGen;
-import com.seaofnodes.simple.codegen.RegMask;
+import com.seaofnodes.simple.codegen.*;
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)
+// CMP (immediate) - SUBS
public class CmpIARM extends MachConcreteNode implements MachNode {
final int _imm;
final String _bop;
- CmpIARM(BoolNode bool, TypeInteger ti) {
+ CmpIARM(BoolNode bool, int imm) {
super(bool);
_inputs.pop();
_bop = bool.op();
- _imm = (int)ti.value();
- assert _imm == ti.value();
+ _imm = imm;
}
- // 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);
+ super((Node)null);
+ addDef(null);
+ addDef(cmp.in(1));
_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 String op() { return _imm==0 ? "test" : "cmp"; }
+ @Override public RegMask regmap(int i) { return arm.RMASK; }
@Override public RegMask outregmap() { return arm.FLAGS_MASK; }
@Override public boolean isClone() { return true; }
@Override public Node copy() { return new CmpIARM(this); }
- // Encoding is appended into the byte array; size is returned
- @Override public int encoding(ByteArrayOutputStream bytes) {
- throw Utils.TODO();
- }
+ @Override public void encoding( Encoding enc ) { arm.imm_inst_subs(enc,in(1),in(1), Arm64.OPI_CMP,_imm); }
// General form: "cmp rs1, 1"
@Override public void asm(CodeGen code, SB sb) {
@@ -49,6 +38,4 @@ public class CmpIARM extends MachConcreteNode implements MachNode {
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
deleted file mode 100644
index 47f68ccc..00000000
--- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/CmpMemARM.java
+++ /dev/null
@@ -1,4 +0,0 @@
-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
index 7c67e2dd..91bd347f 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/DivARM.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/DivARM.java
@@ -1,33 +1,20 @@
package com.seaofnodes.simple.node.cpus.arm;
+import com.seaofnodes.isa.Arm64;
import com.seaofnodes.simple.*;
-import com.seaofnodes.simple.codegen.CodeGen;
-import com.seaofnodes.simple.codegen.RegMask;
+import com.seaofnodes.simple.codegen.*;
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();
- }
-
+ @Override public String op() { return "div"; }
+ @Override public RegMask regmap(int i) { return arm.RMASK; }
+ @Override public RegMask outregmap() { return arm.WMASK; }
+ // SDIV
+ @Override public void encoding( Encoding enc ) { arm.madd(enc,this,Arm64.OP_DIV,3); }
// General form: "div dst /= src"
@Override public void asm(CodeGen code, SB sb) {
sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" / ").p(code.reg(in(2)));
}
- @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
index 2137c01c..6b86e722 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/DivFARM.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/DivFARM.java
@@ -1,30 +1,20 @@
package com.seaofnodes.simple.node.cpus.arm;
+import com.seaofnodes.isa.Arm64;
import com.seaofnodes.simple.*;
-import com.seaofnodes.simple.codegen.CodeGen;
-import com.seaofnodes.simple.codegen.RegMask;
+import com.seaofnodes.simple.codegen.*;
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 String op() { return "divf"; }
+ @Override public RegMask regmap(int i) { return arm.DMASK; }
@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();
- }
-
-
+ // FDIV (scalar)
+ @Override public void encoding( Encoding enc ) { arm.f_scalar(enc,this,Arm64.OPF_DIV); }
// General form: "VDIF = dst /= src"
@Override public void asm(CodeGen code, SB sb) {
sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" / ").p(code.reg(in(2)));
}
-
- @Override public String op() { return "divf"; }
}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/F8ToIARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/F8ToIARM.java
index 937c437b..098c1353 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/F8ToIARM.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/F8ToIARM.java
@@ -1,28 +1,21 @@
package com.seaofnodes.simple.node.cpus.arm;
+import com.seaofnodes.isa.Arm64;
import com.seaofnodes.simple.*;
-import com.seaofnodes.simple.codegen.CodeGen;
-import com.seaofnodes.simple.codegen.RegMask;
+import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.*;
-import com.seaofnodes.simple.type.TypeInteger;
-import java.io.ByteArrayOutputStream;
-// VCVT (between floating-point and integer)
public class F8ToIARM extends MachConcreteNode implements MachNode {
- F8ToIARM(Node f8toi) {super(f8toi);}
- @Override public String op() { return "f8toi"; }
-
- // Register mask allowed on input i.
- @Override public RegMask regmap(int i) { assert i==1; return arm.DMASK; }
- // Register mask allowed as a result. 0 for no register.
+ F8ToIARM(Node f8toi) { super(f8toi); }
+ @Override public String op() { return "cvti"; }
+ @Override public RegMask regmap(int i) { return arm.DMASK; }
@Override public RegMask outregmap() { return arm.WMASK; }
-
- // Encoding is appended into the byte array; size is returned
- @Override public int encoding(ByteArrayOutputStream bytes) {
- throw Utils.TODO();
+ @Override public void encoding( Encoding enc ) {
+ enc.add4(Arm64.floatToInteger(enc.reg(in(1))-arm.D_OFFSET,enc.reg(this)));
}
@Override public void asm(CodeGen code, SB sb) {
sb.p(code.reg(this)).p(" = ").p("(int)").p(code.reg(in(1)));
}
+
}
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
index 7fafc31c..6209397d 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/FloatARM.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/FloatARM.java
@@ -1,29 +1,38 @@
package com.seaofnodes.simple.node.cpus.arm;
+import com.seaofnodes.isa.Arm64;
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.codegen.*;
import com.seaofnodes.simple.node.ConstantNode;
import com.seaofnodes.simple.node.MachNode;
-import java.io.ByteArrayOutputStream;
+import com.seaofnodes.simple.type.TypeFloat;
//FMOV (scalar, immediate)
//Floating-point move immediate.
-public class FloatARM extends ConstantNode implements MachNode {
+public class FloatARM extends ConstantNode implements MachNode, RIPRelSize {
FloatARM(ConstantNode con) { super(con); }
- @Override public String op() { return "fld"; }
- // Register mask allowed on input i. 0 for no register.
+ @Override public String op() { return "ld8"; }
@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();
+ @Override public void encoding( Encoding enc ) {
+ enc.largeConstant(this,_con,0,-1/*TODO: ARM-style ELF patching*/);
+ short dst = (short)(enc.reg(this) - arm.D_OFFSET);
+ double d = ((TypeFloat)_con).value();
+ long x = Double.doubleToRawLongBits(d);
+ enc.add4(Arm64.load_pc(Arm64.OPF_ARM, 0, dst));
+ }
+
+ // Delta is from opcode start.
+ @Override public byte encSize(int delta) { return 4; }
+
+ // Delta is from opcode start
+ @Override public void patch( Encoding enc, int opStart, int opLen, int delta ) {
+ short dst = (short)(enc.reg(this) - arm.D_OFFSET);
+ enc.patch4(opStart, Arm64.load_pc(Arm64.OPF_ARM, delta, dst));
}
// Human-readable form appended to the SB. Things like the encoding,
@@ -31,6 +40,7 @@ public class FloatARM extends ConstantNode implements MachNode {
// 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(" #"));
+ _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
index 0134911f..c49875f4 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/FunARM.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/FunARM.java
@@ -1,21 +1,22 @@
package com.seaofnodes.simple.node.cpus.arm;
-import com.seaofnodes.simple.codegen.CodeGen;
-import com.seaofnodes.simple.codegen.RegMask;
+import com.seaofnodes.isa.Arm64;
+import com.seaofnodes.simple.SB;
import com.seaofnodes.simple.Utils;
+import com.seaofnodes.simple.codegen.*;
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();
+public class FunARM extends FunNode implements MachNode {
+ FunARM(FunNode fun) { super(fun); }
+ @Override public void computeFrameAdjust(CodeGen code, int maxReg) {
+ super.computeFrameAdjust(code,maxReg);
+ _frameAdjust = (_frameAdjust+15) & -16; // Keep SP aligned, including leaf spills.
+ }
+ @Override public void encoding( Encoding enc ) {
+ int sz = _frameAdjust;
+ if( sz == 0 ) return; // Skip if no frame adjust
+ if( sz >= 1L<<12 ) throw Utils.TODO();
+ enc.add4(Arm64.imm_inst(Arm64.OPI_SUB, sz, arm.RSP, arm.RSP));
}
}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/I2F8ARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/I2F8ARM.java
index b106ab8a..a7fa7a6a 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/I2F8ARM.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/I2F8ARM.java
@@ -1,29 +1,26 @@
package com.seaofnodes.simple.node.cpus.arm;
+import com.seaofnodes.isa.Arm64;
import com.seaofnodes.simple.*;
-import com.seaofnodes.simple.codegen.CodeGen;
-import com.seaofnodes.simple.codegen.RegMask;
+import com.seaofnodes.simple.codegen.*;
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.
+ I2F8ARM(Node i2f8) { super(i2f8); }
+ @Override public String op() { return "cvt"; }
+ @Override public RegMask regmap(int i) { return arm.RMASK; }
@Override public RegMask outregmap() { return arm.DMASK; }
-
- // Encoding is appended into the byte array; size is returned
- @Override public int encoding(ByteArrayOutputStream bytes) {
- throw Utils.TODO();
+ @Override public void encoding( Encoding enc ) {
+ // SCVTF
+ short self = (short)(enc.reg(this )-arm.D_OFFSET);
+ short reg1 = enc.reg(in(1));
+ int body = Arm64.float_cast(Arm64.OP_FLOAT_C, 1, reg1, self);
+ enc.add4(body);
}
// General form: "i2f8 (flt)int_value"
@Override public void asm(CodeGen code, SB sb) {
sb.p(code.reg(this)).p(" = ").p("(flt)").p(code.reg(in(1)));
}
+
}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/IntARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/IntARM.java
index 1f81300e..64d00d57 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/IntARM.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/IntARM.java
@@ -1,30 +1,28 @@
package com.seaofnodes.simple.node.cpus.arm;
+import com.seaofnodes.isa.Arm64;
import com.seaofnodes.simple.*;
-import com.seaofnodes.simple.codegen.CodeGen;
-import com.seaofnodes.simple.codegen.RegMask;
+import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.ConstantNode;
import com.seaofnodes.simple.node.MachNode;
import com.seaofnodes.simple.type.Type;
import com.seaofnodes.simple.type.TypeInteger;
-import 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 String op() { return "ldi"; }
@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();
+ @Override public void encoding( Encoding enc ) {
+ short self = enc.reg(this);
+ long x = _con==Type.NIL ? 0 : ((TypeInteger)_con).value();
+ Arm64.constant(enc,self,x);
}
// Human-readable form appended to the SB. Things like the encoding,
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
index 0d59cc47..6fd4274a 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/LoadARM.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/LoadARM.java
@@ -1,37 +1,34 @@
package com.seaofnodes.simple.node.cpus.arm;
-
+import com.seaofnodes.isa.Arm64;
import com.seaofnodes.simple.SB;
-import com.seaofnodes.simple.codegen.CodeGen;
-import com.seaofnodes.simple.codegen.RegMask;
+import com.seaofnodes.simple.codegen.*;
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) {
+public class LoadARM extends MemOpARM {
+ LoadARM(LoadNode ld, Node base, Node idx, int off) {
super(ld, base, idx, off, 0);
}
+ @Override public String op() { return "ld"+_sz; }
+ @Override public RegMask outregmap() { return size() >= 4 ? arm.MEM_MASK : arm.WMASK; }
- @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; }
-
+ private static final int[] OP_LOADS = new int[]{ Arm64.OP_LOAD_IMM_8, Arm64.OP_LOAD_IMM_16, Arm64.OP_LOAD_IMM_32, Arm64.OP_LOAD_IMM_64, };
- @Override public String op() { return "ld"+_sz; }
+ private static final int[] OP_LOAD_RS = new int[]{ Arm64.OP_LOAD_R_8 , Arm64.OP_LOAD_R_16 , Arm64.OP_LOAD_R_32 , Arm64.OP_LOAD_R_64, };
+ // ldr(immediate - unsigned offset) | ldr(register)
+ @Override public void encoding( Encoding enc ) {
+ ldst_encode(enc, OP_LOADS[Integer.numberOfTrailingZeros(size())], OP_LOAD_RS[_declaredType.log_size()], this, size());
+ }
@Override public void asm(CodeGen code, SB sb) {
sb.p(code.reg(this)).p(",");
asm_address(code,sb);
}
-
}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/LslARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/LslARM.java
index 6108c889..7624554e 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/LslARM.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/LslARM.java
@@ -1,41 +1,20 @@
package com.seaofnodes.simple.node.cpus.arm;
+import com.seaofnodes.isa.Arm64;
import com.seaofnodes.simple.*;
-import com.seaofnodes.simple.codegen.CodeGen;
-import com.seaofnodes.simple.codegen.RegMask;
+import com.seaofnodes.simple.codegen.*;
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; }
+ @Override public String op() { return "shr"; }
+ @Override public RegMask regmap(int i) { return arm.RMASK; }
+ @Override public RegMask outregmap() { return arm.WMASK; }
- // 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 encoding( Encoding enc ) { arm.shift_reg(enc,this,Arm64.OP_LSL); }
@Override public void asm(CodeGen code, SB sb) {
-
sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" << ").p(code.reg(in(2)));
}
- @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
index 7a7014f4..bf394172 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/LslIARM.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/LslIARM.java
@@ -1,42 +1,34 @@
package com.seaofnodes.simple.node.cpus.arm;
-
-import com.seaofnodes.simple.*;
+import com.seaofnodes.isa.Arm64;
+import com.seaofnodes.simple.SB;
import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.Encoding;
import com.seaofnodes.simple.codegen.RegMask;
-import com.seaofnodes.simple.node.*;
-import com.seaofnodes.simple.type.Type;
-import com.seaofnodes.simple.type.TypeInteger;
-import java.io.ByteArrayOutputStream;
-import java.util.BitSet;
-import java.lang.StringBuilder;
-
+import com.seaofnodes.simple.node.MachConcreteNode;
+import com.seaofnodes.simple.node.MachNode;
+import com.seaofnodes.simple.node.Node;
-// 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();
+ final int _imm;
+ LslIARM(Node lsl, int imm) {
+ super(lsl);
+ _inputs.pop();
+ _imm = imm;
}
-
- // General form
- // General form: "lsli rd, rs1, imm"
+ @Override public String op() { return "lsli"; }
+ @Override public RegMask regmap(int i) { return arm.RMASK; }
+ @Override public RegMask outregmap() { return arm.WMASK; }
+ @Override public void encoding( Encoding enc ) {
+ short rd = enc.reg(this);
+ short rn = enc.reg(in(1));
+ assert _imm > 0;
+ // UBFM <Xd>, <Xn>, #(-<shift> MOD 64), #(63-<shift>)
+ // immr must be (-<shift> MOD 64) = 64 - shift
+ enc.add4(Arm64.imm_shift(Arm64.OPI_LSL, 64 - _imm, (64 - _imm) - 1, rn, rd));
+ }
+ // General form: "lsli rd = rs1 << imm"
@Override public void asm(CodeGen code, SB sb) {
- sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" << #");
- _ti.print(sb);
+ sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" << #").p(_imm);
}
-
- @Override public String op() { return "lsli"; }
-}
+} \ No newline at end of file
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/LsrARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/LsrARM.java
index e6168dee..2d5fa52c 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/LsrARM.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/LsrARM.java
@@ -1,40 +1,20 @@
package com.seaofnodes.simple.node.cpus.arm;
-
+import com.seaofnodes.isa.Arm64;
import com.seaofnodes.simple.*;
-import com.seaofnodes.simple.codegen.CodeGen;
-import com.seaofnodes.simple.codegen.RegMask;
+import com.seaofnodes.simple.codegen.*;
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();
- }
+ @Override public String op() { return "shr"; }
+ @Override public RegMask regmap(int i) { return arm.RMASK; }
+ @Override public RegMask outregmap() { return arm.WMASK; }
- // General form
- // General form: "lsr rd, rs1, rs2"
+ @Override public void encoding( Encoding enc ) { arm.shift_reg(enc,this,Arm64.OP_LSR); }
@Override public void asm(CodeGen code, SB sb) {
sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" >>> ").p(code.reg(in(2)));
}
- @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
index 8fb20cb2..6a12e4a5 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/LsrIARM.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/LsrIARM.java
@@ -1,41 +1,30 @@
package com.seaofnodes.simple.node.cpus.arm;
+import com.seaofnodes.isa.Arm64;
import com.seaofnodes.simple.*;
-import com.seaofnodes.simple.codegen.CodeGen;
-import com.seaofnodes.simple.codegen.RegMask;
+import com.seaofnodes.simple.codegen.*;
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();
+// logical right shift immediate
+public class LsrIARM extends MachConcreteNode implements MachNode {
+ final int _imm;
+ LsrIARM(Node lsr, int imm) {
+ super(lsr);
+ _inputs.pop();
+ _imm = imm;
}
-
- // General form
- // General form: "lsrl rd, rs1, imm"
+ @Override public String op() { return "lsri"; }
+ @Override public RegMask regmap(int i) { return arm.RMASK; }
+ @Override public RegMask outregmap() { return arm.WMASK; }
+ @Override public void encoding( Encoding enc ) {
+ short rd = enc.reg(this);
+ short rn = enc.reg(in(1));
+ assert _imm > 0;
+ enc.add4(Arm64.imm_shift(Arm64.OPI_LSR,_imm, 0b111111, rn,rd));
+ }
+ // General form: "lsri rd = rs1 >>> imm"
@Override public void asm(CodeGen code, SB sb) {
- sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" >>> #");
- _ti.print(sb);
+ sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" >>> #").p(_imm);
}
-
- @Override public String op() { return "lsrl"; }
-}
+} \ No newline at end of file
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/MemOpARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/MemOpARM.java
index c22e71f6..5e175b1e 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/MemOpARM.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/MemOpARM.java
@@ -1,60 +1,79 @@
package com.seaofnodes.simple.node.cpus.arm;
+import com.seaofnodes.isa.Arm64;
import com.seaofnodes.print.ExprPrinter;
import com.seaofnodes.simple.*;
-import com.seaofnodes.simple.codegen.CodeGen;
-import com.seaofnodes.simple.codegen.RegMask;
+import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.*;
import com.seaofnodes.simple.type.*;
-import java.io.ByteArrayOutputStream;
+
import java.lang.StringBuilder;
-public class MemOpARM extends MemOpNode implements MachNode {
- final int _off; // Limit 32 bits
- final int _imm; // Limit 32 bits
+public abstract class MemOpARM extends MemOpNode implements MachNode {
+ final int _off; // Limit 9 bits sized, or (13 bits<<logsize) unsigned
+ final int _imm; // Limit ? bits
final char _sz = (char)('0'+(1<<_declaredType.log_size()));
- MemOpARM(MemOpNode mop, Node base, Node idx, int off, int imm) {
+ MemOpARM(MemOpNode mop, Node ptr, Node idx, int off, int imm) {
super(mop,mop);
- assert base._type instanceof TypeMemPtr && !(base instanceof AddNode);
- assert ptr() == base;
- _inputs.setX(3,idx);
+ assert ptr._type instanceof TypeMemPtr && !(ptr instanceof AddNode);
+ assert ptr() == ptr;
+ _inputs.setX(2, ptr);
+ _inputs.setX(3, idx);
_off = off;
_imm = imm;
}
// Store-based flavors have a value edge
- MemOpARM( MemOpNode mop, Node base, Node idx, int off, int imm, Node val ) {
- this(mop,base,idx,off,imm);
+ MemOpARM( MemOpNode mop, Node ptr, Node idx, int off, int imm, Node val ) {
+ this(mop,ptr,idx,off,imm);
_inputs.setX(4,val);
}
- Node idx() { return in(3); }
- Node val() { return in(4); } // Only for stores, includin
+ @Override public String label() { return op();}
+ Node val() { return in(4); } // Only for stores
@Override protected ExprPrinter<Node> _printMach(ExprPrinter<Node> p) { return p.p(".").p(_name); }
- @Override public boolean isMem() { return _type instanceof TypeMem; }
-
- @Override public String label() { return op(); }
@Override public Type compute() { throw Utils.TODO(); }
@Override public Node idealize() { throw Utils.TODO(); }
+ int size() { return 1<<_declaredType.log_size(); }
-
- // Register mask allowed on input i.
+ // Wider mask to store both GPRs and FPRs
@Override public RegMask regmap(int i) {
- throw Utils.TODO();
+ // 0 - ctrl
+ if( i==1 ) return null; // memory
+ if( i==2 ) return arm.RMASK; // ptr/base
+ if( i==3 ) return arm.RMASK; // off/index
+ if( i==4 ) return size() >= 4 ? arm.MEM_MASK : arm.RMASK; // value
+ return null; // Anti-dependence
}
- // 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(); }
+ // Shared encoding for loads and stores(int and float)
+ public void ldst_encode( Encoding enc, int opcode_imm, int opcode_reg, Node xval, int size) {
+ short ptr = enc.reg(ptr());
+ short off = enc.reg(off());
+ short val = enc.reg(xval);
+ // The allocated register selects integer vs SIMD/FP encoding, even for
+ // bitwise copies. ARM numbers each register bank from zero in the opcode.
+ boolean fp = val >= arm.D0;
+ assert !fp || size >= 4;
+ if( fp ) {
+ opcode_imm |= 1<<4;
+ opcode_reg |= 1<<5;
+ val -= arm.D0;
+ }
+ int body = off() == null
+ ? Arm64.load_str_imm(opcode_imm, _off, ptr, val, size)
+ : Arm64.indr_adr(opcode_reg, off, Arm64.STORE_LOAD_OPTION.SXTX, 0, ptr, val);
+ enc.add4(body);
+ }
SB asm_address(CodeGen code, SB sb) {
sb.p("[").p(code.reg(ptr())).p("+");
- if( idx() != null ) sb.p(code.reg(idx()));
+ if( off() != null ) sb.p(code.reg(off()));
else sb.p(_off);
return sb.p("]");
}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/MulARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/MulARM.java
index 582b97d4..a0955565 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/MulARM.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/MulARM.java
@@ -1,34 +1,20 @@
package com.seaofnodes.simple.node.cpus.arm;
+import com.seaofnodes.isa.Arm64;
import com.seaofnodes.simple.*;
-import com.seaofnodes.simple.codegen.CodeGen;
-import com.seaofnodes.simple.codegen.RegMask;
+import com.seaofnodes.simple.codegen.*;
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{
+public class MulARM extends MachConcreteNode implements MachNode {
MulARM(Node mul) {super(mul);}
+ @Override public String op() { return "mul"; }
+ @Override public RegMask regmap(int i) { return arm.RMASK; }
+ @Override public RegMask outregmap() { return arm.WMASK; }
- // 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();
- }
-
+ @Override public void encoding( Encoding enc ) { arm.madd(enc,this,Arm64.OP_MUL,31); }
// General form: "rd = rs1 * rs2"
@Override public void asm(CodeGen code, SB sb) {
sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" * ").p(code.reg(in(2)));
}
-
- @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
index b71b412d..33d32bd4 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/MulFARM.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/MulFARM.java
@@ -1,30 +1,19 @@
package com.seaofnodes.simple.node.cpus.arm;
+import com.seaofnodes.isa.Arm64;
import com.seaofnodes.simple.*;
-import com.seaofnodes.simple.codegen.CodeGen;
-import com.seaofnodes.simple.codegen.RegMask;
+import com.seaofnodes.simple.codegen.*;
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 String op() { return "mulf"; }
+ @Override public RegMask regmap(int i) { return arm.DMASK; }
@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();
- }
-
+ @Override public void encoding( Encoding enc ) { arm.f_scalar(enc,this,Arm64.OPF_MUL); }
// Default on double precision for now(64 bits)
// General form: "VMUL.f32 rd = src1 * src2
@Override public void asm(CodeGen code, SB sb) {
sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" * ").p(code.reg(in(2)));
}
-
- @Override public String op() { return "mulf"; }
-
}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/MulIARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/MulIARM.java
deleted file mode 100644
index c118b00a..00000000
--- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/MulIARM.java
+++ /dev/null
@@ -1,43 +0,0 @@
-package com.seaofnodes.simple.node.cpus.arm;
-
-import com.seaofnodes.simple.*;
-import com.seaofnodes.simple.codegen.CodeGen;
-import com.seaofnodes.simple.codegen.RegMask;
-import com.seaofnodes.simple.node.*;
-import com.seaofnodes.simple.type.TypeInteger;
-import java.io.ByteArrayOutputStream;
-
-public class MulIARM extends MachConcreteNode implements MachNode{
- final TypeInteger _ti;
- MulIARM(Node mul, TypeInteger ti) {
- super(mul);
- _inputs.pop();
- _ti = ti;
- }
-
- // Register mask allowed on input i.
- // This is the normal calling convention
- @Override public RegMask regmap(int i) {
- // assert i==1;
- return arm.RMASK; }
-
- // Register mask allowed as a result. 0 for no register.
- @Override public RegMask outregmap() { return arm.RMASK; }
-
- // Output is same register as input#1
- @Override public int twoAddress() { return 1; }
-
- // Encoding is appended into the byte array; size is returned
- @Override public int encoding(ByteArrayOutputStream bytes) {
- throw Utils.TODO();
- }
-
- // General form
- // General form: "muli dst * #imm"
- @Override public void asm(CodeGen code, SB sb) {
- sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" * #");
- _ti.print(sb);
- }
-
- @Override public String op() { return "muli"; }
-}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/NegARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/NegARM.java
new file mode 100644
index 00000000..b7c3c803
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/NegARM.java
@@ -0,0 +1,25 @@
+package com.seaofnodes.simple.node.cpus.arm;
+
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.codegen.*;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.node.MachConcreteNode;
+import com.seaofnodes.simple.type.TypeInteger;
+
+public class NegARM extends MachConcreteNode implements MachNode {
+ NegARM(Node sub) {
+ super(sub);
+ //_inputs.pop();
+ }
+ @Override public String op() { return "neg"; }
+ @Override public RegMask regmap(int i) { return arm.RMASK; }
+ @Override public RegMask outregmap() { return arm.WMASK; }
+ @Override public void encoding( Encoding enc ) {
+ // reverse subtract with immediate 0
+ arm.imm_inst(enc,this, in(1), 0b00100110, 0);
+ }
+ // General form: "subi rd = rs1 - imm"
+ @Override public void asm(CodeGen code, SB sb) {
+ sb.p(code.reg(this)).p(" = -").p(code.reg(in(1)));
+ }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/NewARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/NewARM.java
index c145f348..0dac086b 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/NewARM.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/NewARM.java
@@ -1,31 +1,37 @@
package com.seaofnodes.simple.node.cpus.arm;
-
+import com.seaofnodes.isa.Arm64;
import com.seaofnodes.simple.*;
-import com.seaofnodes.simple.codegen.CodeGen;
-import com.seaofnodes.simple.codegen.RegMask;
+import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.*;
-import java.io.ByteArrayOutputStream;
-
-public class NewARM extends NewNode implements MachNode {
+public class NewARM extends NewNode implements MachNode, RIPRelSize {
// A pre-zeroed chunk of memory.
NewARM(NewNode nnn) { super(nnn); }
- // Size and pointer result in standard calling convention; null for all the
- // memory and ordering edges
- @Override public RegMask regmap(int i) { return i == 2 ? arm. X0_MASK : null; }
- @Override public RegMask outregmap(int i) { return i == 0 ? arm. X0_MASK : null; }
- @Override public RegMask outregmap() { return null; }
-
- // Encoding is appended into the byte array; size is returned
- @Override public int encoding(ByteArrayOutputStream bytes) {
- throw Utils.TODO();
+ @Override public void encoding( Encoding enc ) {
+ // BL(branch with link)
+ enc.external(this,"calloc").
+ add4(Arm64.mov(Arm64.OP_MOVZ, 0, 1, 0)). // movz x0,#1
+ add4(Arm64.b(Arm64.OP_CALL, 0));
}
- // General form: "alloc #bytes"
+ // Patch is for running "new" in a JIT.
+ // Delta is from opcode start
+ @Override public byte encSize(int delta) { return 4*2; }
+
+ // Patch is for running "new" in a JIT.
+ // Delta is from opcode start
+ @Override public void patch(Encoding enc, int opStart, int opLen, int delta ) {
+ // Negative patches are JIT emulator targets.
+ if( opStart+delta < 0 ) {
+ enc.patch4(opStart+4, Arm64.b_calloc(Arm64.OP_CALL, delta-4));
+ } else {
+ throw Utils.TODO();
+ }
+ }
+ // General form: "alloc #bytes PC"
@Override public void asm(CodeGen code, SB sb) {
- sb.p(code.reg(size()));
+ sb.p("ldi x0=#1\n");
+ sb.p("call #calloc");
}
-
- @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
index 28ebd60d..ec017032 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/NotARM.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/NotARM.java
@@ -1,26 +1,29 @@
package com.seaofnodes.simple.node.cpus.arm;
+import com.seaofnodes.isa.Arm64;
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.codegen.*;
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 String op() { return "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 RegMask outregmap() { return arm.WMASK; }
+ @Override public RegMask killmap() { return arm.FLAGS_MASK; }
+ @Override public void encoding( Encoding enc ) {
+ // subs xzr, rs, #0
+ // cset rd, eq // Set rd to 1 if rs == 0 (equal), else 0
+ // subtracting zero from rs will just yield rs, it sets the zero flag and then it's used in cset
+ short self = enc.reg(this );
+ short reg1 = enc.reg(in(1));
+ int subs = Arm64.imm_inst(Arm64.OP_SUBS, 0, reg1, 31);
+ enc.add4(subs);
+ int cset = Arm64.cset(Arm64.OP_CSET, 31, Arm64.COND.EQ, 31, self);
+ enc.add4(cset);
}
@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
index 12ea8265..ba3197c1 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/OrARM.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/OrARM.java
@@ -1,42 +1,21 @@
package com.seaofnodes.simple.node.cpus.arm;
+import com.seaofnodes.isa.Arm64;
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;
+import com.seaofnodes.simple.codegen.*;
+import com.seaofnodes.simple.node.*;
// 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
+ OrARM(Node and) { super(and); }
+ @Override public String op() { return "or"; }
+ @Override public String glabel() { return "|"; }
+ @Override public RegMask regmap(int i) { return arm.RMASK; }
+ @Override public RegMask outregmap() { return arm.WMASK; }
+ @Override public void encoding( Encoding enc ) { arm.r_reg(enc,this,Arm64.OP_OR); }
// General form: #rd = rs1 & rs2
@Override public void asm(CodeGen code, SB sb) {
-
sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" | ").p(code.reg(in(2)));
}
- @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
index f257a867..9c7cb8cc 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/OrIARM.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/OrIARM.java
@@ -1,40 +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.MachConcreteNode;
-import com.seaofnodes.simple.node.MachNode;
-import com.seaofnodes.simple.node.Node;
+import com.seaofnodes.isa.Arm64;
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.codegen.*;
+import com.seaofnodes.simple.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;
+public class OrIARM extends MachConcreteNode implements MachNode {
+ final int _imm;
+ OrIARM(Node or, int imm) {
+ super(or);
+ _inputs.pop();
+ _imm = imm;
}
- // 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();
+ @Override public String op() { return "ori"; }
+ @Override public RegMask regmap(int i) { return arm.RMASK; }
+ @Override public RegMask outregmap() { return arm.WMASK; }
+ @Override public void encoding( Encoding enc ) {
+ arm.imm_inst_n(enc, this, in(1), Arm64.OPI_OR, _imm);
}
-
- // General form
- // General form: "rd = rs1 | imm"
+ // General form: "ori rd = rs1 | imm"
@Override public void asm(CodeGen code, SB sb) {
- sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" | #");
- _ti.print(sb);
+ sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" | #").p(_imm);
}
-
- @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
index c3fa132c..b419fe0a 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/ParmARM.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/ParmARM.java
@@ -1,25 +1,17 @@
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.Encoding;
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);
+ _rmask = arm.callInMask(fun().sig(),_idx,0);
}
- // 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; }
+ @Override public void encoding( Encoding enc ) { }
}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/ProjARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/ProjARM.java
index b2add015..4b7d6f13 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/ProjARM.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/ProjARM.java
@@ -1,23 +1,15 @@
package com.seaofnodes.simple.node.cpus.arm;
-import com.seaofnodes.simple.Utils;
+import com.seaofnodes.simple.codegen.Encoding;
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();
- }
+ @Override public void encoding( Encoding enc ) { }
}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/RetARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/RetARM.java
index c7d0dd4c..a7ed5073 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/RetARM.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/RetARM.java
@@ -1,49 +1,16 @@
package com.seaofnodes.simple.node.cpus.arm;
+import com.seaofnodes.isa.Arm64;
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;
-
+import com.seaofnodes.simple.codegen.*;
+import com.seaofnodes.simple.node.*;
public class RetARM extends ReturnNode implements MachNode {
RetARM(ReturnNode ret, FunNode fun) { super(ret, fun); fun.setRet(this); }
-
- // 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("]");
+ @Override public void encoding( Encoding enc ) {
+ int frameAdjust = ((FunARM)fun())._frameAdjust;
+ if( frameAdjust > 0 )
+ enc.add4(Arm64.imm_inst(Arm64.OPI_ADD, frameAdjust, arm.RSP, arm.RSP));
+ enc.add4(Arm64.ret(Arm64.OP_RET));
}
-
- // 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
index c3945d1a..6809bb1c 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/SetARM.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/SetARM.java
@@ -1,20 +1,14 @@
package com.seaofnodes.simple.node.cpus.arm;
+import com.seaofnodes.isa.Arm64;
import com.seaofnodes.simple.*;
-import com.seaofnodes.simple.codegen.CodeGen;
-import com.seaofnodes.simple.codegen.RegMask;
+import com.seaofnodes.simple.codegen.*;
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
@@ -22,13 +16,13 @@ public class SetARM extends MachConcreteNode implements MachNode {
_inputs.push(cmp);
_bop = bop;
}
-
+ @Override public String op() { return "set"+_bop; }
@Override public RegMask regmap(int i) { assert i==1; return arm.FLAGS_MASK; }
- @Override public RegMask outregmap() { return arm.RMASK; }
+ @Override public RegMask outregmap() { return arm.WMASK; }
- // Encoding is appended into the byte array; size is returned
- @Override public int encoding(ByteArrayOutputStream bytes) {
- throw Utils.TODO();
+ @Override public void encoding( Encoding enc ) {
+ int body = Arm64.cset(Arm64.OP_CSET,0b11111, Arm64.make_condition(_bop), 0b011111, enc.reg(this));
+ enc.add4(body);
}
@Override public void asm(CodeGen code, SB sb) {
@@ -36,6 +30,4 @@ public class SetARM extends MachConcreteNode implements MachNode {
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
index 85771842..c0c60ea2 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/SplitARM.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/SplitARM.java
@@ -1,22 +1,67 @@
package com.seaofnodes.simple.node.cpus.arm;
+import com.seaofnodes.isa.Arm64;
+import com.seaofnodes.simple.SB;
import com.seaofnodes.simple.Utils;
-import com.seaofnodes.simple.codegen.RegMask;
+import com.seaofnodes.simple.codegen.*;
+import com.seaofnodes.simple.node.FunNode;
import com.seaofnodes.simple.node.Node;
import com.seaofnodes.simple.node.SplitNode;
-import java.io.ByteArrayOutputStream;
+import com.seaofnodes.simple.node.cpus.riscv.riscv;
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();
+ @Override public void encoding( Encoding enc ) {
+ short dst = enc.reg(this );
+ short src = enc.reg(in(1));
+
+ boolean dstX = dst >= arm.D_OFFSET;
+ boolean srcX = src >= arm.D_OFFSET;
+
+ if (dst >= arm.MAX_REG) {
+ // store to sp
+ if(src >= arm.MAX_REG) {
+ throw Utils.TODO();
+ }
+ int off = enc._fun.computeStackOffset(enc._code,dst);
+ if( srcX ) src -= arm.D_OFFSET;
+ enc.add4(Arm64.load_str_imm(Arm64.OP_STORE_IMM_64, off, arm.RSP, src, 8));
+ return;
+ }
+
+ if(src >= arm.MAX_REG) {
+ // Load from SP
+ int off = enc._fun.computeStackOffset(enc._code,src);
+ if( dstX ) dst -= arm.D_OFFSET;
+ enc.add4(Arm64.load_str_imm(Arm64.OP_LOAD_IMM_64, off, arm.RSP, dst, 8));
+ return;
+ }
+
+ // pick opcode based on regs
+ if(!dstX && !srcX) {
+ // GPR->GPR
+ enc.add4(Arm64.mov_reg(Arm64.OP_MOV, src, dst));
+ } else if(dstX && srcX) {
+ // FPR->FPR
+ // fmov reg
+ enc.add4(Arm64.f_mov_reg(Arm64.OP_FMOV_REG, src - arm.D_OFFSET,dst - arm.D_OFFSET));
+ } else if(!srcX && dstX) {
+ // GPR->FPR
+ // FMOV(general) 64 bits to DOUBLE-PRECISION
+ enc.add4(Arm64.f_mov_general(Arm64.OP_FMOV, 0b01, 0, 0b111, src, dst - arm.D_OFFSET));
+ } else if( srcX && !dstX) {
+ //FPF->GPR
+ // FMOV(general) DOUBLE-PRECISION to 64 bits
+ enc.add4(Arm64.f_mov_general(Arm64.OP_FMOV, 0b01, 0, 0b110, src - arm.D_OFFSET, dst));
+ }
+ }
+
+ // General form: "mov dst = src"
+ @Override public void asm(CodeGen code, SB sb) {
+ FunNode fun = code._encoding==null ? null : code._encoding._fun;
+ sb.p(code.reg(this,fun)).p(" = ").p(code.reg(in(1),fun));
}
}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/StoreARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/StoreARM.java
index d78fdcad..cf858c36 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/StoreARM.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/StoreARM.java
@@ -1,49 +1,35 @@
package com.seaofnodes.simple.node.cpus.arm;
+import com.seaofnodes.isa.Arm64;
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.codegen.*;
import com.seaofnodes.simple.node.StoreNode;
-import java.io.ByteArrayOutputStream;
-import java.util.BitSet;
+import com.seaofnodes.simple.node.Node;
// 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)
+// base - base pointer, offset is added to base
+// null - index never allowed (no [reg+reg] mode)
+// off - offset added to base
+// imm - immediate value to store, only if val is null
+// val - value to store or null
//e.g s.cs[0] = 67; // C
// base = s.cs, off = 4, imm = 67, val = null
public class StoreARM extends MemOpARM {
- StoreARM(StoreNode st, Node base, Node idx, int off, int imm, Node val) {
- super(st, base, idx, off, imm, val);
+ StoreARM(StoreNode st, Node base, Node idx, int off, Node val) {
+ super(st, base, idx, off, 0, val);
}
-
@Override public String op() { return "st"+_sz; }
-
- // 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;
- return null; // Trailing anti-dependence edges
- }
- // 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();
- }
+ private static final int[] OP_STORES = new int[]{ Arm64.OP_STORE_IMM_8, Arm64.OP_STORE_IMM_16, Arm64.OP_STORE_IMM_32, Arm64.OP_STORE_IMM_64, };
+ private static final int[] OP_STORE_RS = new int[]{ Arm64.OP_STORE_R_8, Arm64.OP_STORE_R_16, Arm64.OP_STORE_R_32, Arm64.OP_STORE_R_64, };
+
+ @Override public void encoding( Encoding enc ) {
+ ldst_encode(enc, OP_STORES[Integer.numberOfTrailingZeros(size())], OP_STORE_RS[_declaredType.log_size()], val(), size());
+ }
@Override public void asm(CodeGen code, SB sb) {
asm_address(code,sb).p(",");
if( val()==null ) sb.p("#").p(_imm);
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
index 27555b53..6024df68 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/SubARM.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/SubARM.java
@@ -1,30 +1,20 @@
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.isa.Arm64;
+import com.seaofnodes.simple.SB;
+import com.seaofnodes.simple.codegen.*;
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; }
+ SubARM(Node sub) { super(sub); }
+ @Override public String op() { return "sub"; }
+ @Override public RegMask regmap(int i) { return arm.RMASK; }
+ @Override public RegMask outregmap() { return arm.WMASK; }
- // Encoding is appended into the byte array; size is returned
- @Override public int encoding(ByteArrayOutputStream bytes) {
- throw Utils.TODO();
- }
+ @Override public void encoding( Encoding enc ) { arm.r_reg(enc,this,Arm64.OP_SUB); }
// General form: "sub # rd = rs1 - rs2"
@Override public void asm(CodeGen code, SB sb) {
sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" - ").p(code.reg(in(2)));
}
-
- @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
index 8ae05b40..dd147544 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/SubFARM.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/SubFARM.java
@@ -1,32 +1,19 @@
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.isa.Arm64;
+import com.seaofnodes.simple.SB;
+import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.*;
-import com.seaofnodes.simple.type.TypeInteger;
-import java.io.ByteArrayOutputStream;
-public class SubFARM extends MachConcreteNode implements MachNode{
+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 String op() { return "subf"; }
+ @Override public RegMask regmap(int i) { return arm.DMASK; }
@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();
- }
-
+ @Override public void encoding( Encoding enc ) { arm.f_scalar(enc,this,Arm64.OPF_SUB); }
// Default on double precision for now(64 bits)
// General form: "vsub.f32 rd = src1 + src2
@Override public void asm(CodeGen code, SB sb) {
sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" - ").p(code.reg(in(2)));
}
-
- @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
index d19210cb..e45bfa63 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/SubIARM.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/SubIARM.java
@@ -1,42 +1,30 @@
package com.seaofnodes.simple.node.cpus.arm;
+import com.seaofnodes.isa.Arm64;
+import com.seaofnodes.simple.codegen.*;
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) {
+ final int _imm;
+ SubIARM(Node sub, int imm) {
super(sub);
_inputs.pop();
- _ti = ti;
+ _imm = imm;
}
-
- // Register mask allowed on input i.
+ @Override public String op() { return _imm == -1 ? "dec" : "subi"; }
@Override public RegMask regmap(int i) { return arm.RMASK; }
+ @Override public RegMask outregmap() { return arm.WMASK; }
- // 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();
+ @Override public void encoding( Encoding enc ) {
+ arm.imm_inst(enc,this, in(1), Arm64.OPI_SUB,_imm);
}
// General form: "subi rd = rs1 - imm"
@Override public void asm(CodeGen code, SB sb) {
- sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" - #");
- _ti.print(sb);
+ sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" - #").p(_imm);
}
-
- @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
index cdb2b6ec..1f2b6a37 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/TFPARM.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/TFPARM.java
@@ -1,37 +1,54 @@
package com.seaofnodes.simple.node.cpus.arm;
+import com.seaofnodes.isa.Arm64;
import com.seaofnodes.simple.*;
-import com.seaofnodes.simple.codegen.CodeGen;
-import com.seaofnodes.simple.codegen.RegMask;
+import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.ConstantNode;
import com.seaofnodes.simple.node.MachNode;
-import com.seaofnodes.simple.type.Type;
+import com.seaofnodes.simple.node.cpus.riscv.riscv;
import com.seaofnodes.simple.type.TypeFunPtr;
-import java.io.ByteArrayOutputStream;
-public class TFPARM extends ConstantNode implements MachNode {
+public class TFPARM extends ConstantNode implements MachNode, RIPRelSize {
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); }
+ @Override public void encoding( Encoding enc ) {
+ enc.relo(this);
+ short self = enc.reg(this);
+ // adrp x0, 0
+ int adrp = Arm64.adrp(1,0, Arm64.OP_ADRP, 0,self);
+ // add x0, x0, 0
+ enc.add4(adrp);
+ arm.imm_inst(enc,Arm64.OPI_ADD,0, 0);
+ }
- // Encoding is appended into the byte array; size is returned
- @Override public int encoding(ByteArrayOutputStream bytes) {
+ @Override public byte encSize(int delta) {
+ if( -(1L<<12) <= delta && delta < (1L<<12) ) return 4;
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"
+ // Delta is from opcode start
+ @Override public void patch( Encoding enc, int opStart, int opLen, int delta ) {
+ short rpc = enc.reg(this);
+ if(opLen == 8 ) {
+ // opstart of add
+ int next = opStart + opLen;
+ int adrp_delta = delta >> 12;
+ // patch upper 20 bits via adrp
+ enc.patch4(opStart, Arm64.adrp(1, adrp_delta & 0b11, 0b10000, adrp_delta >> 2, rpc));
+ // low 12 bits via add
+ enc.patch4(next, Arm64.imm_inst_l(Arm64.OPI_ADD, delta & 0xfff, rpc));
+ } else {
+ // should not happen as one instruction is 4 byte, and TFP arm encodes 2.
+ throw Utils.TODO();
+ }
+ }
+
@Override public void asm(CodeGen code, SB sb) {
String reg = code.reg(this);
_con.print(sb.p(reg).p(" #"));
}
-
}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/UJmpARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/UJmpARM.java
new file mode 100644
index 00000000..acb32ddb
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/UJmpARM.java
@@ -0,0 +1,47 @@
+package com.seaofnodes.simple.node.cpus.arm;
+
+import com.seaofnodes.isa.Arm64;
+
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.codegen.*;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.type.Type;
+
+// unconditional jump
+public class UJmpARM extends CFGNode implements MachNode, RIPRelSize {
+ UJmpARM() { }
+ @Override public String op() { return "jmp"; }
+ @Override public String label() { return op(); }
+ @Override protected String format() { return "jmp "; }
+ @Override public RegMask regmap(int i) {return null; }
+ @Override public RegMask outregmap() { return null; }
+ @Override public Type compute() { throw Utils.TODO(); }
+ @Override public Node idealize() { throw Utils.TODO(); }
+ @Override public void encoding( Encoding enc ) {
+ enc.jump(this,uctrl());
+ int body = Arm64.b(Arm64.OP_UJMP, 0);
+ enc.add4(body);
+ }
+
+ // Delta is from opcode start
+ @Override public byte encSize(int delta) {
+ if( -(1<<26) <= delta && delta < (1<<26) ) return 4;
+ // 2 word encoding needs a tmp register, must teach RA
+ throw Utils.TODO();
+ }
+
+ // Delta is from opcode start
+ @Override public void patch( Encoding enc, int opStart, int opLen, int delta ) {
+ if( opLen==4 ) {
+ enc.patch4(opStart,Arm64.b(Arm64.OP_UJMP, delta));
+ } else {
+ throw Utils.TODO();
+ }
+ }
+
+ @Override public void asm(CodeGen code, SB sb) {
+ CFGNode target = uctrl();
+ //assert target.nOuts() > 1; // Should optimize jmp to empty targets
+ sb.p(label(target));
+ }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/XorARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/XorARM.java
index fd127b18..5b635303 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/XorARM.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/XorARM.java
@@ -1,41 +1,20 @@
package com.seaofnodes.simple.node.cpus.arm;
+import com.seaofnodes.isa.Arm64;
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;
+import com.seaofnodes.simple.codegen.*;
+import com.seaofnodes.simple.node.*;
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
+ XorARM(Node xor) {super(xor);}
+ @Override public String op() { return "xor"; }
+ @Override public String glabel() { return "^"; }
+ @Override public RegMask regmap(int i) { return arm.RMASK; }
+ @Override public RegMask outregmap() { return arm.WMASK; }
+ @Override public void encoding( Encoding enc ) { arm.r_reg(enc,this,Arm64.OP_XOR); }
// General form: "rd = x1 ^ x2"
@Override public void asm(CodeGen code, SB sb) {
sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" ^ ").p(code.reg(in(2)));
}
- @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
index d230ea47..017815f3 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/XorIARM.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/XorIARM.java
@@ -1,43 +1,28 @@
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.isa.Arm64;
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;}
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.codegen.*;
+import com.seaofnodes.simple.node.*;
- // 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();
+public class XorIARM extends MachConcreteNode implements MachNode {
+ final int _imm;
+ XorIARM(Node xor, int imm) {
+ super(xor);
+ _inputs.pop();
+ _imm = imm;
}
- // General form
- // General form: "rd = rs1 ^ imm"
+ @Override public String op() { return "xori"; }
+ @Override public RegMask regmap(int i) { return arm.RMASK; }
+ @Override public RegMask outregmap() { return arm.WMASK; }
+
+ // General form: "xori rd = rs1 ^ imm"
+ @Override public void encoding( Encoding enc ) { arm.imm_inst_n(enc,this, in(1), Arm64.OPI_XOR,_imm); }
@Override public void asm(CodeGen code, SB sb) {
- sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" ^ #");
- _ti.print(sb);
+ sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" ^ #").p(_imm);
}
-
- @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
index 72254310..66a59fde 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/arm.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/arm.java
@@ -1,30 +1,36 @@
package com.seaofnodes.simple.node.cpus.arm;
+import com.seaofnodes.isa.Arm64;
+import static com.seaofnodes.isa.Arm64.*;
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.codegen.*;
import com.seaofnodes.simple.node.*;
import com.seaofnodes.simple.type.*;
public class arm extends Machine {
+ public arm( CodeGen code ) {
+ if( !"SystemV".equals(code._callingConv) )
+ throw new IllegalArgumentException("Unknown calling convention "+code._callingConv);
+ }
- // X86-64 V2. Includes e.g. SSE4.2 and POPCNT.
+ // ARM64
@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;
+ public static final int X0 = 0, X1 = 1, X2 = 2, X3 = 3, X4 = 4, X5 = 5, X6 = 6, X7 = 7;
+ public static final int X8 = 8, X9 = 9, X10 = 10, X11 = 11, X12 = 12, X13 = 13, X14 = 14, X15 = 15;
+ public static final int X16 = 16, X17 = 17, X18 = 18, X19 = 19, X20 = 20, X21 = 21, X22 = 22, X23 = 23;
+ public static final int X24 = 24, X25 = 25, X26 = 26, X27 = 27, X28 = 28, X29 = 29, X30 = 30, RSP = 31;
// Floating point registers
- 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;
+ public static final int D0 = 32, D1 = 33, D2 = 34, D3 = 35, D4 = 36, D5 = 37, D6 = 38, D7 = 39;
+ public static final int D8 = 40, D9 = 41, D10 = 42, D11 = 43, D12 = 44, D13 = 45, D14 = 46, D15 = 47;
+ public static final int D16 = 48, D17 = 49, D18 = 50, D19 = 51, D20 = 52, D21 = 53, D22 = 54, D23 = 55;
+ public static final int D24 = 56, D25 = 57, D26 = 58, D27 = 59, D28 = 60, D29 = 61, D30 = 62, D31 = 63;
static final int FLAGS = 64;
+ static final int MAX_REG = 65;
+ public static final int D_OFFSET = 32;
static final String[] REGS = new String[] {
"X0", "X1", "X2", "X3", "X4", "X5", "X6", "X7",
@@ -37,9 +43,7 @@ public class arm extends Machine {
"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+"]";
- }
+ @Override public String[] regs() { return REGS; }
// from (x0-x30)
// General purpose register mask: pointers and ints, not floats
@@ -55,11 +59,9 @@ public class arm extends Machine {
// 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 SPLIT_MASK = new RegMask(WR_BITS | FP_BITS, -2L/*skip flags*/);
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);
@@ -105,13 +107,126 @@ public class arm extends Machine {
D7_MASK,
};
- static RegMask callInMask( TypeFunPtr tfp, int idx ) {
- if( idx==0 ) return RPC_MASK;
+ // ARM ENCODING
+
+ // True if signed 9-bit immediate
+ private static boolean imm9(TypeInteger ti) {
+ // 55 = 64-9
+ return ti.isConstant() && ((ti.value()<<55)>>55) == ti.value();
+ }
+ // True if signed 12-bit immediate
+ private static boolean imm12(TypeInteger ti) {
+ // 52 = 64-12
+ return ti.isConstant() && ((ti.value()<<52)>>52) == ti.value();
+ }
+
+ // Can we encode this in ARM's 12-bit LOGICAL immediate form?
+ // Some combination of shifted bit-masks.
+ private static int imm12Logical(TypeInteger ti) {
+ return ti.isConstant() ? Arm64.logicalImmediate(ti.value()) : -1;
+ }
+
+ public static void imm_inst(Encoding enc, Node n,Node n2, int opcode, int imm12) {
+ short self = enc.reg(n);
+ short reg1 = enc.reg(n2);
+
+ // ADD/SUB encode an unsigned magnitude; switch operation for a negative addend.
+ if( imm12<0 && (opcode==OPI_ADD || opcode==OPI_SUB) ) {
+ opcode ^= OPI_ADD ^ OPI_SUB;
+ imm12 = -imm12;
+ }
+ int body = Arm64.imm_inst(opcode, imm12&0xFFF, reg1, self);
+ enc.add4(body);
+ }
+
+ public static void imm_inst_subs(Encoding enc, Node n,Node n2, int opcode, int imm12) {
+ short reg1 = enc.reg(n2);
+ // 31 = 11111
+ int body = Arm64.imm_inst(opcode, imm12&0xFFF, reg1, 31);
+ enc.add4(body);
+ }
+
+ // for cases where rs1 and dst are the same, eg add x0, x0, 1
+ public static void imm_inst(Encoding enc, int opcode, int imm12, int self) {
+ int body = Arm64.imm_inst(opcode, imm12&0xFFF, self, self);
+ enc.add4(body);
+ }
+
+ public static void imm_inst_n(Encoding enc, Node n, Node n2, int opcode, int imm13) {
+ short self = enc.reg(n);
+ short reg1 = enc.reg(n2);
+ int body = Arm64.imm_inst_n(opcode, imm13, reg1, self);
+ enc.add4(body);
+ }
+
+ // for cases where rs1 and dst are the same, eg add x0, x0, 1
+ public static int imm_inst_l(Encoding enc, Node n, int opcode, int imm12) {
+ short self = enc.reg(n);
+ short reg1 = enc.reg(n.in(1));
+ int body = Arm64.imm_inst(opcode, imm12&0xFFF, reg1, self);
+ return body;
+ }
+
+ // for normal add, reg1, reg2 cases (reg-to-reg)
+ // using shifted-reg form
+
+ public static void r_reg(Encoding enc, Node n, int opcode) {
+ short self = enc.reg(n);
+ short reg1 = enc.reg(n.in(1));
+ short reg2 = enc.reg(n.in(2));
+ int body = Arm64.r_reg(opcode, 0, reg2, 0, reg1, self >= 32 ? reg1: self);
+ enc.add4(body);
+ }
+
+ public static void r_reg_subs(Encoding enc, Node n, int opcode) {
+ short reg1 = enc.reg(n.in(1));
+ short reg2 = enc.reg(n.in(2));
+ // 31 = 11111
+ int body = Arm64.r_reg(opcode, 0, reg2, 0, reg1, 31);
+ enc.add4(body);
+ }
+
+ public static void shift_reg(Encoding enc, Node n, int op2) {
+ short self = enc.reg(n);
+ short reg1 = enc.reg(n.in(1));
+ short reg2 = enc.reg(n.in(2));
+ int body = Arm64.shift_reg(0b10011010110, reg2, op2, reg1, self);
+ enc.add4(body);
+ }
+
+ // MUL can be considered an alias for MADD with the third operand Ra being set to 0
+
+ public static void madd(Encoding enc, Node n, int opcode, int ra) {
+ short self = enc.reg(n);
+ short reg1 = enc.reg(n.in(1));
+ short reg2 = enc.reg(n.in(2));
+ int body = Arm64.madd(opcode, reg2, ra, reg1, self);
+ enc.add4(body);
+ }
+
+ public static void f_scalar(Encoding enc, Node n, int op ) {
+ short self = (short)(enc.reg(n) -D_OFFSET);
+ short reg1 = (short)(enc.reg(n.in(1))-D_OFFSET);
+ short reg2 = (short)(enc.reg(n.in(2))-D_OFFSET);
+ int body = Arm64.f_scalar(0b00011110, 1, reg2, op, reg1, self);
+ enc.add4(body);
+ }
+
+ public static void f_cmp(Encoding enc, Node n) {
+ short reg1 = (short)(enc.reg(n.in(1))-D_OFFSET);
+ short reg2 = (short)(enc.reg(n.in(2))-D_OFFSET);
+ int body = Arm64.f_cmp(0b00011110, 3, reg2, reg1);
+ enc.add4(body);
+ }
+
+ @Override public RegMask callArgMask(TypeFunPtr tfp, int idx, int maxArgSlot ) { return callInMask(tfp,idx,maxArgSlot); }
+ static RegMask callInMask(TypeFunPtr tfp, int idx, int maxArgSlot ) {
+ if( idx==0 ) return CodeGen.CODE._rpcMask;
if( idx==1 ) return null;
// Count floats in signature up to index
int fcnt=0;
for( int i=2; i<idx; i++ )
- if( tfp.arg(i-2) instanceof TypeFloat )
+ if( tfp.arg(i-2) instanceof TypeFloat)
fcnt++;
// Floats up to XMMS in XMM registers
if( tfp.arg(idx-2) instanceof TypeFloat ) {
@@ -125,124 +240,99 @@ public class arm extends Machine {
throw Utils.TODO(); // Pass on stack slot
}
- static final long CALLEE_SAVE =
+ // Return the max stack slot used by this signature, or 0
+ @Override public short maxArgSlot( TypeFunPtr tfp ) {
+ int icnt=0, fcnt=0; // Count of ints, floats
+ for( int i=0; i<tfp.nargs(); i++ ) {
+ if( tfp.arg(i) instanceof TypeFloat ) fcnt++;
+ else icnt++;
+ }
+ int nstk = Math.max(icnt-8,0)+Math.max(fcnt-8,0);
+ return (short)nstk;
+ }
+
+ private static final long CALLEE_SAVE =
1L<<X19 |
1L<<X20 | 1L<<X21 | 1L<<X22 | 1L<<X23 |
1L<<X24 | 1L<<X25 | 1L<<X26 | 1L<<X27 |
1L<<X28 |
1L<<D9 | 1L<<D10 | 1L<<D11 |
1L<<D12 | 1L<<D13 | 1L<<D14 | 1L<<D15;
-
- static final 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];
+ static final long CALLER_SAVE = ~CALLEE_SAVE & ~(1L<<RSP);
+ @Override public long callerSave() { return CALLER_SAVE; }
+ @Override public long neverSave() { return 1L<<RSP; }
+ @Override public RegMask retMask( TypeFunPtr tfp ) {
+ return tfp.ret() instanceof TypeFloat ? D0_MASK : X0_MASK;
}
+ @Override public int rpc() { return X30; }
// Create a split op; any register to any register, including stack slots
@Override public SplitNode split(String kind, byte round, LRG lrg) { return new SplitARM(kind,round); }
// Return a MachNode unconditional branch
@Override public CFGNode jump() {
- throw Utils.TODO();
- }
-
- // Break an infinite loop
- @Override public IfNode never( CFGNode ctrl ) {
- throw Utils.TODO();
+ return new UJmpARM();
}
// Instruction selection
- @Override public Node instSelect( Node n ) {
+ @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 AddFNode addf -> new AddFARM(addf);
+ case AddNode add -> add(add);
+ case AndNode and -> and(and);
+ case BoolNode bool -> cmp(bool);
+ case CallNode call -> call(call);
+ case CastNode cast -> new CastARM(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 MinusNode neg -> new NegARM(neg);
+ case MulFNode mulf -> new MulFARM(mulf);
+ case MulNode mul -> new MulARM(mul);
+ case NewNode nnn -> new NewARM(nnn);
+ case NotNode not -> new NotARM(not);
+ case OrNode or -> or(or);
+ case ParmNode parm -> new ParmARM(parm);
case BulkMemPhiNode phi -> new BulkMemPhiNode(phi);
case MemPhiNode phi -> new MemPhiNode(phi);
- case PhiNode phi -> new PhiNode(phi);
- case ProjNode prj -> prj(prj);
+ case PhiNode phi -> new PhiNode(phi);
+ case ProjNode prj -> new ProjARM(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 ReturnNode ret -> new RetARM(ret,ret.fun());
+ case SarNode sar -> asr(sar);
+ case ShlNode shl -> lsl(shl);
+ case ShrNode shr -> lsr(shr);
+ case StartNode start -> new StartNode(start);
+ case StopNode stop -> new StopNode(stop);
+ case StoreNode st -> st(st);
+ case SubFNode subf -> new SubFARM(subf);
+ case SubNode sub -> sub(sub);
+ case ToFloatNode tfn-> new I2F8ARM(tfn);
case ToIntegerNode cvt -> new F8ToIARM(cvt);
- case XorNode xor -> xor(xor);
+ case XorNode xor -> xor(xor);
- case LoopNode loop -> new LoopNode(loop);
- case RegionNode region-> new RegionNode(region);
+ 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 _cmp(BoolNode bool) {
+ if( bool.isFloat() )
+ return new CmpFARM(bool);
+ return bool.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti
+ ? new CmpIARM(bool, (int)ti.value())
+ : new CmpARM(bool);
}
private Node ld(LoadNode ld) {
@@ -253,129 +343,100 @@ public class arm extends Machine {
// 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);
+ String op = "!=";
+ if( iff.in(1) instanceof BoolNode bool ) op = bool.op();
+ else if( iff.in(1)==null ) op = "=="; // Never-node cutout
+ else iff.setDef(1, new BoolNode.NE(iff.in(1), new ConstantNode(TypeInteger.ZERO)));
+ return new BranchARM(iff, op);
}
private Node add(AddNode add) {
- Node rhs = add.in(2);
- if( rhs instanceof ConstantNode off && off._con instanceof TypeInteger toff ) {
- return new AddIARM(add, toff);
- }
- return new AddARM(add);
+ return add.in(2) instanceof ConstantNode off && off._con instanceof TypeInteger ti && imm12(ti)
+ ? new AddIARM(add, (int)ti.value())
+ : new AddARM(add);
}
private Node sub(SubNode sub) {
- return sub.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti
- ? new AddIARM(sub, TypeInteger.constant(-ti.value()))
+ return sub.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti && imm12(ti)
+ ? new SubIARM(sub, (int)(ti.value()))
: new SubARM(sub);
}
- private Node 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) {
+ 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();
+ 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 Type t -> t==Type.NIL ? new IntARM(con) : 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 call(CallNode call){
+ return call.fptr() instanceof ConstantNode con && con._con instanceof TypeFunPtr tfp
+ ? new CallARM(call, tfp)
+ : new CallRRARM(call);
}
- 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 or(OrNode or) {
+ int imm12;
+ return or.in(2) instanceof ConstantNode off && off._con instanceof TypeInteger ti && (imm12 = imm12Logical(ti)) != -1
+ ? new OrIARM(or, imm12)
+ : new OrARM(or);
+ }
+ private Node xor(XorNode xor) {
+ int imm12;
+ return xor.in(2) instanceof ConstantNode off && off._con instanceof TypeInteger ti && (imm12 = imm12Logical(ti)) != -1
+ ? new XorIARM(xor, imm12)
+ : new XorARM(xor);
}
- private Node 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 and(AndNode and) {
+ int imm12;
+ return and.in(2) instanceof ConstantNode off && off._con instanceof TypeInteger ti && (imm12 = imm12Logical(ti)) != -1
+ ? new AndIARM(and, imm12)
+ : new AndARM(and);
}
- private Node 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 asr(SarNode asr) {
+ return asr.in(2) instanceof ConstantNode off && off._con instanceof TypeInteger ti && ti.value() >= 0 && ti.value() < 63
+ ? new AsrIARM(asr, (int)ti.value())
+ : new AsrARM(asr);
}
- private Node prj(ProjNode prj) {
- return new ProjARM(prj);
+ private Node lsl(ShlNode lsl) {
+ return lsl.in(2) instanceof ConstantNode off && off._con instanceof TypeInteger ti && ti.value() >= 0 && ti.value() < 63
+ ? new LslIARM(lsl, (int)ti.value())
+ : new LslARM(lsl);
}
+ private Node lsr(ShrNode lsr) {
+ return lsr.in(2) instanceof ConstantNode off && off._con instanceof TypeInteger ti && ti.value() >= 0 && ti.value() < 63
+ ? new LsrIARM(lsr, (int)ti.value())
+ : new LsrARM(lsr);
+ }
private static int off;
private static Node idx;
private Node st(StoreNode st) {
- 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);
+ return new StoreARM(address(st),st.ptr(),idx,off,st.val());
}
// Gather addressing mode bits prior to constructing. This is a builder
// pattern, but saving the bits in a *local* *global* here to keep mess
// contained.
- private <N extends MemOpNode> N address( N mop ) {
+ 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 ) {
+ if( mop.off() instanceof ConstantNode con && con._con instanceof TypeInteger ti && imm9(ti) ) {
off = (int)ti.value();
assert off == ti.value(); // In 32-bit range
} else {
@@ -384,5 +445,4 @@ public class arm extends Machine {
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
deleted file mode 100644
index e0792669..00000000
--- a/src/main/java/com/seaofnodes/simple/node/cpus/arm/i2f8.java
+++ /dev/null
@@ -1,4 +0,0 @@
-package com.seaofnodes.simple.node.cpus.arm;
-
-public class i2f8 {
-}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AUIPC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AUIPC.java
new file mode 100644
index 00000000..fc96cbe9
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AUIPC.java
@@ -0,0 +1,42 @@
+package com.seaofnodes.simple.node.cpus.riscv;
+
+import com.seaofnodes.isa.RiscV;
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.codegen.*;
+import com.seaofnodes.simple.node.ConstantNode;
+import com.seaofnodes.simple.node.MachNode;
+import com.seaofnodes.simple.type.Type;
+
+// Add upper 20bits to PC. Immediate comes from the relocation info.
+public class AUIPC extends ConstantNode implements MachNode, RIPRelSize {
+ AUIPC( Type tfp ) { super(tfp); }
+ @Override public RegMask regmap(int i) { return null; }
+ @Override public RegMask outregmap() { return riscv.WMASK; }
+ @Override public boolean isClone() { return true; }
+ @Override public AUIPC copy() { return new AUIPC(_con); }
+ @Override public String op() { return "auipc"; }
+ @Override public void encoding( Encoding enc ) {
+ enc.relo(this);
+ short dst = enc.reg(this);
+ enc.add4(RiscV.u_type(RiscV.OP_AUIPC, dst, 0));
+ }
+
+ // Delta is from opcode start, but X86 measures from the end of the 5-byte encoding
+ @Override public byte encSize(int delta) { return 4; }
+
+ // Delta is from opcode start
+ @Override public void patch( Encoding enc, int opStart, int opLen, int delta ) {
+ //short rpc = enc.reg(this);
+ //// High half is where the TFP constant used to be, the last input
+ //short auipc = enc.reg(in(_inputs._len-1));
+ //enc.patch4(opStart,RiscV.i_type(0x67, rpc, 0, auipc, delta));
+ throw Utils.TODO();
+ }
+
+ @Override public void asm(CodeGen code, SB sb) {
+ String reg = code.reg(this);
+ sb.p(reg).p(" = PC+#");
+ if( _con == null ) sb.p("---");
+ else _con.print(sb);
+ }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AddFRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AddFRISC.java
index 2417aeb6..b0921fa0 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,30 +1,18 @@
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.isa.RiscV;
+import com.seaofnodes.simple.SB;
+import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.*;
-import com.seaofnodes.simple.type.TypeInteger;
-import java.io.ByteArrayOutputStream;
-public class AddFRISC extends MachConcreteNode implements MachNode{
+// fadd.d
+public class AddFRISC extends MachConcreteNode implements MachNode {
AddFRISC(Node addf) {super(addf);}
-
- // Register mask allowed on input i.
+ @Override public String op() { return "addf"; }
@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; }
-
- // Encoding is appended into the byte array; size is returned
- @Override public int encoding(ByteArrayOutputStream bytes) {
- throw Utils.TODO();
- }
-
- // Default on double precision for now(64 bits)
- // General form: "fadd.d rd = src1 + src2
+ @Override public void encoding( Encoding enc ) { riscv.rf_type(enc,this,RiscV.RM.RNE,1); }
@Override public void asm(CodeGen code, SB sb) {
sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" + ").p(code.reg(in(2)));
}
-
- @Override public String op() { return "addf"; }
}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AddIRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AddIRISC.java
index e92a7b08..c3aa1276 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,11 +1,22 @@
package com.seaofnodes.simple.node.cpus.riscv;
+import com.seaofnodes.isa.RiscV;
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 "+"; }
+ // Used to inst-selection as a direct match against an ideal Add/Sub
+ public AddIRISC( Node add, int imm12, boolean pop ) { super(add,imm12,pop); }
@Override public String op() { return "addi"; }
+ @Override public String glabel() { return "+"; }
+ @Override int opcode() { return RiscV.OP_IMM; }
+ @Override int func3() {return 0;}
+ @Override public AddIRISC copy() {
+ // Clone the AddI, using the same inputs-only code used during inst select.
+ // Output edges are missing.
+ AddIRISC add = new AddIRISC(in(1),_imm12,false);
+ // Copys happen when output edges should be valid, so correct missing output edges.
+ in(1)._outputs.add(add);
+ return add;
+ }
}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AddRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AddRISC.java
index 344c217d..df392a0e 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,29 +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.SB;
+import com.seaofnodes.simple.codegen.*;
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); }
+ public AddRISC( Node add ) { super(add); }
+ AddRISC( Node base, Node off ) {
+ super(new Node[3]);
+ _inputs.set(1,base);
+ _inputs.set(2,off );
+ }
@Override public String op() { return "add"; }
-
- // 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; }
-
- // 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 encoding( Encoding enc ) { riscv.r_type(enc,this,0,0); }
@Override public void asm(CodeGen code, SB sb) {
sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" + ").p(code.reg(in(2)));
}
-
}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AndIRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AndIRISC.java
index ddbf5aa4..7639ea94 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,11 +1,13 @@
package com.seaofnodes.simple.node.cpus.riscv;
+import com.seaofnodes.isa.RiscV;
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 "&"; }
+ public AndIRISC( Node and, int imm) { super(and,imm); }
@Override public String op() { return "andi"; }
+ @Override public String glabel() { return "&"; }
+ @Override int opcode() { return RiscV.OP_IMM; }
+ @Override int func3() {return 7;}
}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AndRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AndRISC.java
index ca00bc98..93ef95c0 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,37 +1,15 @@
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.SB;
+import com.seaofnodes.simple.codegen.*;
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 AndRISC extends MachConcreteNode implements MachNode{
- AndRISC(Node and) {
- super(and);
- }
-
- // Register mask allowed on input i.
- // This is the normal calling convention
- @Override public RegMask regmap(int i) {
- assert i==1 || i==2;
- return riscv.RMASK;
- }
-
- // Register mask allowed as a result. 0 for no register.
+public class AndRISC extends MachConcreteNode implements MachNode {
+ public AndRISC(Node and) { super(and); }
+ @Override public String op() { return "and"; }
+ @Override public RegMask regmap(int i) { return riscv.RMASK; }
@Override public RegMask outregmap() { return riscv.WMASK; }
-
- // 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 encoding( Encoding enc ) { riscv.r_type(enc,this,0b111,0); }
@Override public void asm(CodeGen code, SB sb) {
sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" & ").p(code.reg(in(2)));
}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/BranchRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/BranchRISC.java
index 30dfd7fe..b5ad5f59 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/BranchRISC.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/BranchRISC.java
@@ -1,48 +1,94 @@
package com.seaofnodes.simple.node.cpus.riscv;
+import com.seaofnodes.isa.RiscV;
import com.seaofnodes.print.ExprPrinter;
import com.seaofnodes.simple.*;
-import com.seaofnodes.simple.codegen.CodeGen;
-import com.seaofnodes.simple.codegen.RegMask;
+import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.*;
-import java.io.ByteArrayOutputStream;
// Conditional branch such as: BEQ
-public class BranchRISC extends IfNode implements MachNode {
- final String _bop;
+public class BranchRISC extends IfNode implements MachNode, RIPRelSize {
+ String _bop;
// label is obtained implicitly
- BranchRISC( IfNode iff, String bop, Node n1, Node n2 ) {
+ public BranchRISC( IfNode iff, String bop, Node n1, Node n2 ) {
super(iff);
_bop = bop;
_inputs.setX(1,n1);
_inputs.setX(2,n2);
}
+ @Override public String op() { return "b"+_bop; }
@Override public String label() { return op(); }
-
+ @Override public String comment() { return "L"+cproj(1)._nid; }
@Override public RegMask regmap(int i) { return riscv.RMASK; }
@Override public RegMask outregmap() { return null; }
+ @Override public void negate() {
+ _bop = negate(_bop);
+ // Cannot encode ">" or "<=", so flip these
+ if( _bop.equals(">") || _bop.equals("<=") ) {
+ swap12(); // Cannot negate the test, so swap the operands
+ _bop = swap(_bop); // Swap test to match
+ }
+ }
@Override protected ExprPrinter<Node> _print1(ExprPrinter<Node> p) {
- p.p("if( ").n(in(1)).p(_bop);
+ p.p("if( ");
+ if( in(1)==null ) p.p("0");
+ else p.n(in(1));
+ p.p(_bop);
if( in(2)==null ) p.p("0");
else p.n(in(2));
return p.p(" )");
}
- // Encoding is appended into the byte array; size is returned
- @Override public int encoding(ByteArrayOutputStream bytes) {
- throw Utils.TODO();
+ @Override public void encoding( Encoding enc ) {
+ if( in(1)==null && _bop=="!=" ) return; // Inverted never-node, no code
+ enc.jump(this,cproj(0));
+ if( in(1)==null ) // Never node
+ enc.add4(RiscV.j_type(RiscV.OP_JAL, 0, 0));
+ else {
+ // Todo: relocs (for offset - immf)
+ short src1 = enc.reg(in(1));
+ short src2 = in(2)==null ? (short)riscv.ZERO : enc.reg(in(2));
+ enc.add4(RiscV.b_type(RiscV.OP_BRANCH, RiscV.jumpop(_bop), src1, src2, 0));
+ }
}
- @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)));
+ // Delta is from opcode start
+ @Override public byte encSize(int delta) {
+ if( in(1)==null && _bop=="!=" ) return 0; // Inverted never-node, no code
+ if( -4*1024 <= delta && delta < 4*1024 ) return 4;
+ // 2 word encoding needs a tmp register, must teach RA
+ throw Utils.TODO();
}
- @Override public String op() { return "b"+_bop; }
+ // Delta is from opcode start
+ @Override public void patch( Encoding enc, int opStart, int opLen, int delta ) {
+ assert !( in(1)==null && _bop=="!=" ); // Inverted never-node, no code no patch
+ if( in(1)==null ) { // Never node
+ enc.patch4(opStart,RiscV.j_type(RiscV.OP_JAL, 0, delta));
+ } else {
+ short src1 = enc.reg(in(1));
+ short src2 = in(2)==null ? (short)riscv.ZERO : enc.reg(in(2));
+ if( opLen==4 ) {
+ enc.patch4(opStart,RiscV.b_type(RiscV.OP_BRANCH, RiscV.jumpop(_bop), src1, src2, delta));
+ } else {
+ throw Utils.TODO();
+ }
+ }
+ }
- @Override public String comment() {
- return "L"+cproj(1)._nid+", L"+cproj(0)._nid;
+ @Override public void asm(CodeGen code, SB sb) {
+ if( in(1)==null && _bop=="!=" ) {
+ sb.p("never");
+ return;
+ }
+ String src1 = in(1)==null ? "#0" : code.reg(in(1));
+ String src2 = in(2)==null ? "#0" : code.reg(in(2));
+ sb.p(src1).p(" ").p(_bop).p(" ").p(src2).p(" ");
+ CFGNode prj = cproj(0).uctrlSkipEmpty();
+ if( !prj.blockHead() ) prj = prj.cfg0();
+ sb.p(label(prj));
}
}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CallEndRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CallEndRISC.java
index fd1ba7c3..7df68e77 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CallEndRISC.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CallEndRISC.java
@@ -1,35 +1,8 @@
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"; }
-
+ CallEndRISC( CallEndNode cend ) { super(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 54f4fba9..2b3232c3 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,44 +1,59 @@
package com.seaofnodes.simple.node.cpus.riscv;
+import com.seaofnodes.isa.RiscV;
import com.seaofnodes.simple.*;
-import com.seaofnodes.simple.codegen.CodeGen;
-import com.seaofnodes.simple.codegen.RegMask;
+import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.*;
import com.seaofnodes.simple.type.TypeFunPtr;
-import java.io.ByteArrayOutputStream;
-public class CallRISC extends CallNode implements MachNode{
+public class CallRISC extends CallNode implements MachNode, RIPRelSize {
final TypeFunPtr _tfp;
final String _name;
-
CallRISC( CallNode call, TypeFunPtr tfp ) {
super(call);
- _inputs.pop(); // Pop constant target
assert tfp.isConstant();
+ _inputs.pop(); // Pop constant target
_tfp = tfp;
_name = CodeGen.CODE.link(tfp)._name;
}
+ @Override public String op() { return "call"; }
@Override public String label() { return op(); }
@Override public RegMask regmap(int i) {
- return riscv.callInMask(_tfp,i); // Normal argument
+ return riscv.callInMask(_tfp,i,fun()._maxArgSlot);
}
- @Override public RegMask outregmap() { return null; }
-
+ @Override public RegMask outregmap() { return riscv.RPC_MASK; }
@Override public String name() { return _name; }
@Override public TypeFunPtr tfp() { return _tfp; }
- // Encoding is appended into the byte array; size is returned
- @Override public int encoding(ByteArrayOutputStream bytes) {
+ @Override public void encoding( Encoding enc ) {
+ // Short form +/-4K: beq r0,r0,imm12
+ // Long form: auipc rX,imm20/32; jal r0,[rX+imm12/32]
+ enc.relo(this);
+ enc.add4(RiscV.j_type(RiscV.OP_JAL, riscv.RPC, 0));
+ }
+
+ // Delta is from opcode start
+ @Override public byte encSize(int delta) {
+ if( -(1L<<20) <= delta && delta < (1L<<20) ) return 4;
+ // 2 word encoding needs a tmp register, must teach RA
throw Utils.TODO();
}
+ // Delta is from opcode start
+ @Override public void patch( Encoding enc, int opStart, int opLen, int delta ) {
+ if( opLen==4 ) {
+ enc.patch4(opStart,RiscV.j_type(RiscV.OP_JAL, riscv.RPC, delta));
+ } else {
+ throw Utils.TODO();
+ }
+ }
+
@Override public void asm(CodeGen code, SB sb) {
sb.p(_name).p(" ");
for( int i=0; i<nargs(); i++ )
- sb.p(code.reg(arg(i))).p(" ");
- sb.unchar(2);
- }
+ sb.p(code.reg(arg(i+2))).p(", ");
+ sb.unchar(2).p(" ").p(code.reg(fptr()));
- @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 3bae656d..55e36ebf 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,26 +1,26 @@
package com.seaofnodes.simple.node.cpus.riscv;
+import com.seaofnodes.isa.RiscV;
import com.seaofnodes.simple.*;
-import com.seaofnodes.simple.codegen.CodeGen;
-import com.seaofnodes.simple.codegen.RegMask;
+import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.*;
-import java.io.ByteArrayOutputStream;
-public class CallRRISC extends CallNode implements MachNode{
+public class CallRRISC extends CallNode implements MachNode {
CallRRISC( CallNode call ) { super(call); }
-
+ @Override public String op() { return "callr"; }
@Override public String label() { return op(); }
@Override public RegMask regmap(int i) {
- // Todo: float or int?
- return i==_inputs._len
+ return i==_inputs._len-1
? riscv.RMASK // Function call target
- : riscv.callInMask(tfp(),i); // Normal argument
+ : riscv.callInMask(tfp(),i,fun()._maxArgSlot); // 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 encoding( Encoding enc ) {
+ short rpc = enc.reg(this);
+ short src = enc.reg(in(_inputs._len-1));
+ int body = RiscV.i_type(RiscV.OP_JALR, rpc, 0, src, 0);
+ enc.add4(body);
}
@Override public void asm(CodeGen code, SB sb) {
@@ -29,7 +29,4 @@ public class CallRRISC extends CallNode implements MachNode{
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
index a0d0df90..c404aaa5 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CastRISC.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CastRISC.java
@@ -2,17 +2,17 @@ package com.seaofnodes.simple.node.cpus.riscv;
import com.seaofnodes.simple.SB;
import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.Encoding;
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); }
+ public CastRISC( CastNode cast ) { super(cast); }
+ @Override public String op() { return label(); }
@Override public RegMask regmap(int i) { assert i==1; return RegMask.FULL; }
@Override public RegMask outregmap() { return RegMask.FULL; }
@Override public int twoAddress( ) { return 1; }
- @Override public int encoding(ByteArrayOutputStream bytes) { return 0; /*no encoding*/ }
+ @Override public void encoding( Encoding enc ) { }
@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/DivFRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/DivFRISC.java
index d360bea6..12a798b5 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,30 +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.isa.RiscV;
+import com.seaofnodes.simple.SB;
+import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.*;
-import java.io.ByteArrayOutputStream;
+// fdiv.d
public class DivFRISC extends MachConcreteNode implements MachNode{
DivFRISC(Node divf) {super(divf);}
-
- // Register mask allowed on input i.
+ @Override public String op() { return "divf"; }
@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; }
-
- // Encoding is appended into the byte array; size is returned
- @Override public int encoding(ByteArrayOutputStream bytes) {
- throw Utils.TODO();
- }
-
+ @Override public void encoding( Encoding enc ) { riscv.rf_type(enc,this,RiscV.RM.RUP,13); }
// Default on double precision for now(64 bits)
// General form: "fdiv.d rd = src1 / src2
@Override public void asm(CodeGen code, SB sb) {
sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" / ").p(code.reg(in(2)));
}
-
- @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 ee49a883..8367ee68 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,32 +1,17 @@
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.SB;
+import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.*;
-import com.seaofnodes.simple.type.TypeInteger;
-import java.io.ByteArrayOutputStream;
public class DivRISC extends MachConcreteNode implements MachNode{
- DivRISC(Node div) {super(div);}
-
- // Register mask allowed on input i.
- @Override public RegMask regmap(int i) {
- assert i==1 || i==2;
- return riscv.RMASK; }
- // Register mask allowed as a result. 0 for no register.
+ public DivRISC(Node div) {super(div);}
+ @Override public String op() { return "div"; }
+ @Override public RegMask regmap(int i) { return riscv.RMASK; }
@Override public RegMask outregmap() { return riscv.WMASK; }
-
- // Encoding is appended into the byte array; size is returned
- @Override public int encoding(ByteArrayOutputStream bytes) {
- throw Utils.TODO();
- }
-
- // Signed division
- // General form: "rd = rs1 / rs"
+ @Override public void encoding( Encoding enc ) { riscv.r_type(enc,this,4,1); }
@Override public void asm(CodeGen code, SB sb) {
sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" / ").p(code.reg(in(2)));
}
- @Override public String op() { return "div"; }
}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/F8ToIRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/F8ToIRISC.java
index f4de5d48..b2d4efea 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/F8ToIRISC.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/F8ToIRISC.java
@@ -1,28 +1,19 @@
package com.seaofnodes.simple.node.cpus.riscv;
+import com.seaofnodes.isa.RiscV;
import com.seaofnodes.simple.*;
-import com.seaofnodes.simple.codegen.CodeGen;
-import com.seaofnodes.simple.codegen.RegMask;
+import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.*;
-import com.seaofnodes.simple.type.TypeInteger;
-import java.io.ByteArrayOutputStream;
public class F8ToIRISC extends MachConcreteNode implements MachNode {
F8ToIRISC(Node f8toi) {super(f8toi);}
-
- // Register mask allowed on input i.
+ @Override public String op() { return "cvti"; }
@Override public RegMask regmap(int i) { assert i==1; return riscv.FMASK; }
- // Register mask allowed as a result. 0 for no register.
@Override public RegMask outregmap() { return riscv.WMASK; }
-
- // Encoding is appended into the byte array; size is returned
- @Override public int encoding(ByteArrayOutputStream bytes) {
- throw Utils.TODO();
+ @Override public void encoding( Encoding enc ) {
+ RiscV.floatToInteger(enc,enc.reg(this),enc.reg(in(1))-riscv.F_OFFSET);
}
-
@Override public void asm(CodeGen code, SB sb) {
sb.p(code.reg(this)).p(" = ").p("(int)").p(code.reg(in(1)));
}
-
- @Override public String op() { return "f8toi"; }
}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/FltRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/FltRISC.java
index 2832ac52..9f6dec78 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,31 +1,48 @@
package com.seaofnodes.simple.node.cpus.riscv;
+import com.seaofnodes.isa.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.codegen.*;
import com.seaofnodes.simple.node.ConstantNode;
import com.seaofnodes.simple.node.MachNode;
-import java.io.ByteArrayOutputStream;
+import com.seaofnodes.simple.type.TypeFloat;
-public class FltRISC extends ConstantNode implements MachNode{
- FltRISC(ConstantNode con) { super(con); }
- @Override public String op() { return "flw"; }
- // Register mask allowed on input i. 0 for no register.
+public class FltRISC extends ConstantNode implements MachNode, RIPRelSize {
+ FltRISC(ConstantNode con) { super(con); }
+ @Override public String op() { return "ld8"; }
@Override public RegMask regmap(int i) { return null; }
- // General int registers! Must-split into the floats
- @Override public RegMask outregmap() { return riscv.RMASK; }
+ @Override public RegMask outregmap() { return riscv.FMASK; }
+ @Override public boolean isClone() { return true; }
+ @Override public FltRISC copy() { return new FltRISC(this); }
- // Encoding is appended into the byte array; size is returned
- @Override public int encoding(ByteArrayOutputStream bytes) {
- throw Utils.TODO();
+ @Override public void encoding( Encoding enc ) {
+ enc.largeConstant(this,_con, 0, -1/*TODO: RISC5 style patching*/);
+ short dst = (short)(enc.reg(this) - riscv.F_OFFSET);
+ short tmp = (short)riscv.T6;
+ // AUIPC dst,#hi20_constant_pool
+ enc.add4(RiscV.u_type(RiscV.OP_AUIPC, tmp, 0));
+ // Load dst,[dst+#low12_constant_pool]
+ enc.add4(RiscV.i_type(RiscV.OP_LOADFP, dst, 0b11, tmp, 0));
+ }
+ @Override public RegMask killmap() { return new RegMask(riscv.T6); }
+
+ // Delta is from opcode start.
+ // TODO: always size 8?
+ @Override public byte encSize(int delta) { return 8; }
+
+ // Delta is from opcode start
+ @Override public void patch( Encoding enc, int opStart, int opLen, int delta ) {
+ short dst = (short)(enc.reg(this) - riscv.F_OFFSET);
+ short tmp = (short)riscv.T6;
+ // AUIPC dst,#hi20_constant_pool
+ enc.patch4(opStart , RiscV.u_type(RiscV.OP_AUIPC, tmp, delta>>12));
+ // Load dst,[dst+#low12_constant_pool]
+ enc.patch4(opStart+4, RiscV.i_type(RiscV.OP_LOADFP, dst, 0b11, tmp, delta & 0xFFF));
}
- // 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(" #"));
+ _con.print(sb.p(code.reg(this)).p(" = #"));
}
+
}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/FunRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/FunRISC.java
index 3b7d9a34..4ebdec15 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,21 +1,23 @@
package com.seaofnodes.simple.node.cpus.riscv;
+import com.seaofnodes.isa.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.codegen.*;
import com.seaofnodes.simple.node.FunNode;
import com.seaofnodes.simple.node.MachNode;
-import java.io.ByteArrayOutputStream;
public class FunRISC extends FunNode implements MachNode {
FunRISC(FunNode fun) {super(fun);}
- // Register mask allowed on input i. 0 for no register.
- @Override public RegMask regmap(int i) { return null; }
- // Register mask allowed as a result. 0 for no register.
- @Override public RegMask outregmap() { return null; }
-
- // Encoding is appended into the byte array; size is returned
- @Override public int encoding(ByteArrayOutputStream bytes) {
- throw Utils.TODO();
+ @Override public void computeFrameAdjust(CodeGen code, int maxReg) {
+ super.computeFrameAdjust(code,maxReg);
+ if( _hasCalls ) // If non-leaf, pad to 16b
+ _frameAdjust = ((_frameAdjust+8) & -16);
+ }
+ @Override public void encoding( Encoding enc ) {
+ int sz = _frameAdjust;
+ if( sz == 0 ) return; // Skip if no frame adjust
+ if( sz >= 1L<<12 ) throw Utils.TODO();
+ enc.add4(RiscV.i_type(RiscV.OP_IMM, riscv.RSP, 0, riscv.RSP, -sz & 0xFFF));
}
}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/I2F8RISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/I2F8RISC.java
index 560d1b38..c9b20939 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,30 +1,24 @@
package com.seaofnodes.simple.node.cpus.riscv;
+import com.seaofnodes.isa.RiscV;
import com.seaofnodes.simple.*;
-import com.seaofnodes.simple.codegen.CodeGen;
-import com.seaofnodes.simple.codegen.RegMask;
+import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.*;
-import com.seaofnodes.simple.type.TypeInteger;
-import java.io.ByteArrayOutputStream;
// fcvt.d.w
+// Converts a 32-bit signed integer, in integer register rs1 into a double-precision floating-point number in floating-point register rd.
public class I2F8RISC extends MachConcreteNode implements MachNode {
I2F8RISC(Node i2f8) {super(i2f8);}
-
- // Register mask allowed on input i.
+ @Override public String op() { return "cvtf"; }
@Override public RegMask regmap(int i) { assert i==1; return riscv.RMASK; }
- // Register mask allowed as a result. 0 for no register.
@Override public RegMask outregmap() { return riscv.FMASK; }
-
- // Encoding is appended into the byte array; size is returned
- @Override public int encoding(ByteArrayOutputStream bytes) {
- throw Utils.TODO();
+ @Override public void encoding( Encoding enc ) {
+ short dst = (short)(enc.reg(this )-riscv.F_OFFSET);
+ short src1 = enc.reg(in(1));
+ int body = RiscV.r_type(RiscV.OP_FP,dst,RiscV.RM.RNE.ordinal(),src1,0,0x69);
+ enc.add4(body);
}
-
- // General form: "i2f8 (flt)int_value"
@Override public void asm(CodeGen code, SB sb) {
sb.p(code.reg(this)).p(" = ").p("(flt)").p(code.reg(in(1)));
}
-
- @Override public String op() { return "i2f8"; }
}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/ImmRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/ImmRISC.java
index 330de299..dd41589c 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/ImmRISC.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/ImmRISC.java
@@ -1,40 +1,56 @@
package com.seaofnodes.simple.node.cpus.riscv;
+import com.seaofnodes.isa.RiscV;
import com.seaofnodes.print.ExprPrinter;
import com.seaofnodes.simple.*;
-import com.seaofnodes.simple.codegen.CodeGen;
-import com.seaofnodes.simple.codegen.RegMask;
+import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.*;
import com.seaofnodes.simple.type.TypeInteger;
-import java.io.ByteArrayOutputStream;
-public abstract class ImmRISC extends MachConcreteNode implements MachNode {
- final int _imm;
- ImmRISC( Node add, int imm ) {
+abstract public class ImmRISC extends MachConcreteNode implements MachNode {
+ public final int _imm12;
+ ImmRISC( Node add, int imm12, boolean pop ) {
super(add);
- _inputs.pop();
- _imm = imm;
+ _imm12 = imm12;
+ // If pop, we are moving from an ideal Node to MachNode; the ideal Node
+ // inputs got copied here, but we are absorbing the immediate into the
+ // MachNode and so do not want the constant input.
+ if( pop ) _inputs.pop();
+ // if NOT pop, we are the 2nd-half of a 2-part expansion, reset the
+ // input edges to no-control
+ else { _inputs.set(0,null); _inputs.setX(1,add); }
}
-
- // Register mask allowed on input i.
+ ImmRISC( Node add, int imm12 ) { this(add,imm12,true); }
@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 boolean isClone() {
+ // Allow cloning of the 2nd half of a 2-part constant; this might
+ // trigger cloning the first part also.
+ return in(1) instanceof LUI || in(1) instanceof AUIPC;
+ }
@Override protected ExprPrinter<Node> _print1(ExprPrinter<Node> p) {
- return p.p("( ").n(in(1)).p(String.format(" %s #%d )",glabel(),_imm));
+ int imm12 = (_imm12<<20)>>20; // Sign extend 12 bits
+ p.p("( ").n(in(1));
+ return p.p(String.format(" %s #%d )",glabel(),imm12));
}
abstract int opcode();
+ abstract int func3();
- // Encoding is appended into the byte array; size is returned
- @Override public int encoding(ByteArrayOutputStream bytes) {
- throw Utils.TODO();
+ @Override public void encoding( Encoding enc ) {
+ short dst = enc.reg(this );
+ short src = enc.reg(in(1));
+ int body = RiscV.i_type(opcode(), dst, func3(), src, _imm12 & 0xFFF);
+ enc.add4(body);
}
// 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);
+ int imm12 = (_imm12<<20)>>20; // Sign extend 12 bits
+ sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" ").p(glabel()).p(" #").p(imm12);
+ if( in(1) instanceof LUI lui )
+ sb.p(" // #").hex4((int)(((TypeInteger)lui._con).value()) + imm12);
}
}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/IntRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/IntRISC.java
index e792b055..e4dd39dd 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,33 +1,29 @@
package com.seaofnodes.simple.node.cpus.riscv;
+import com.seaofnodes.isa.RiscV;
import com.seaofnodes.simple.*;
-import com.seaofnodes.simple.codegen.CodeGen;
-import com.seaofnodes.simple.codegen.RegMask;
+import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.ConstantNode;
import com.seaofnodes.simple.node.MachNode;
-import java.io.ByteArrayOutputStream;
+import com.seaofnodes.simple.type.Type;
+import com.seaofnodes.simple.type.TypeInteger;
-// Integer constants
+// 12-bit integer constant. Larger constants are made up in the instruction
+// selection by adding with a LUI.
public class IntRISC extends ConstantNode implements MachNode {
- IntRISC(ConstantNode con) { super(con); }
+ public 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.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) {
- throw Utils.TODO();
+ @Override public void encoding( Encoding enc ) {
+ short dst = enc.reg(this);
+ int val = _con==Type.NIL ? 0 : (int)(((TypeInteger)_con).value() & 0xFFF);
+ // Explicit truncation of larger immediates; this will sign-extend on
+ // load and this is handled during instruction selection.
+ enc.add4(RiscV.i_type(RiscV.OP_IMM, dst, 0, riscv.ZERO, val));
}
-
- // 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/riscv/LUI.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/LUI.java
new file mode 100644
index 00000000..596e11cd
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/LUI.java
@@ -0,0 +1,33 @@
+package com.seaofnodes.simple.node.cpus.riscv;
+
+import com.seaofnodes.isa.RiscV;
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.codegen.*;
+import com.seaofnodes.simple.node.ConstantNode;
+import com.seaofnodes.simple.node.MachNode;
+import com.seaofnodes.simple.type.TypeInteger;
+
+// Load upper 20bits.
+public class LUI extends ConstantNode implements MachNode {
+ public LUI( int imm20 ) {
+ super(TypeInteger.constant(imm20));
+ assert riscv.imm20Exact((TypeInteger)_con);
+ }
+ @Override public RegMask regmap(int i) { return null; }
+ @Override public RegMask outregmap() { return riscv.WMASK; }
+ @Override public boolean isClone() { return true; }
+ @Override public LUI copy() { return new LUI((int)((TypeInteger)_con).value()); }
+ @Override public String op() { return "lui"; }
+ @Override public void encoding( Encoding enc ) {
+ long x = ((TypeInteger)_con).value();
+ int imm20 = (int)(x>>12) & 0xFFFFF;
+ short dst = enc.reg(this);
+ int lui = RiscV.u_type(RiscV.OP_LUI, dst, imm20);
+ enc.add4(lui);
+ }
+
+ @Override public void asm(CodeGen code, SB sb) {
+ String reg = code.reg(this);
+ sb.p(reg).p(" = #").hex4((int)(((TypeInteger)_con).value()));
+ }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/LoadRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/LoadRISC.java
index 3fe8f44e..5e1845b8 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,40 +1,30 @@
package com.seaofnodes.simple.node.cpus.riscv;
+import com.seaofnodes.isa.RiscV;
import com.seaofnodes.simple.*;
-import com.seaofnodes.simple.codegen.CodeGen;
-import com.seaofnodes.simple.codegen.RegMask;
+import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.*;
-import com.seaofnodes.simple.type.TypeInteger;
-import java.io.ByteArrayOutputStream;
-
// 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
-
+// off = off - imm12 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) {
- return riscv.RMASK;
- }
- // Wide mask loads both ints and floats; encoding varies.
+ public LoadRISC(LoadNode ld, Node base, int off) { super(ld, base, off, null); }
+ @Override public String op() { return "ld" +_sz; }
+ @Override public RegMask regmap(int i) { return riscv.RMASK; }
@Override public RegMask outregmap() { return riscv.MEM_MASK; }
-
-
- // Encoding is appended into the byte array; size is returned
- @Override public int encoding(ByteArrayOutputStream bytes) {
- throw Utils.TODO();
+ @Override public void encoding( Encoding enc ) {
+ short dst = enc.reg(this );
+ short ptr = enc.reg(ptr());
+ int op = dst >= riscv.F_OFFSET ? RiscV.OP_LOADFP : RiscV.OP_LOAD;
+ if( dst >= riscv.F_OFFSET ) dst -= riscv.F_OFFSET;
+ enc.add4(RiscV.i_type(op, dst, func3(), ptr, _off));
}
@Override public void asm(CodeGen code, SB sb) {
sb.p(code.reg(this)).p(",");
asm_address(code,sb);
}
-
- @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 19869285..41ab37be 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
@@ -2,59 +2,74 @@ package com.seaofnodes.simple.node.cpus.riscv;
import com.seaofnodes.print.ExprPrinter;
-import com.seaofnodes.simple.*;
-import com.seaofnodes.simple.codegen.CodeGen;
-import com.seaofnodes.simple.codegen.RegMask;
+import com.seaofnodes.simple.SB;
+import com.seaofnodes.simple.Utils;
+import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.*;
import com.seaofnodes.simple.type.*;
-import java.io.ByteArrayOutputStream;
-import java.lang.StringBuilder;
-
-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( MemOpNode mop, Node base, Node idx, int off, int imm) {
+public abstract class MemOpRISC extends MemOpNode implements MachNode {
+ final int _off; // Limit 12 bits
+ final char _sz;
+ MemOpRISC(MemOpNode mop, Node base, int off, Node val) {
super(mop,mop);
- assert base._type instanceof TypeMemPtr && !(base instanceof AddNode);
- assert ptr() == base;
- _inputs.setX(3,idx);
+ assert base._type instanceof TypeMemPtr;
+ _inputs.setX(2, base ); // Base can be an Add, is no longer raw object base
+ _inputs.setX(3, null); // Never an index
+ _inputs.setX(4, val );
_off = off;
- _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);
+ _sz = (char)('0'+(1<<_declaredType.log_size()));
}
- Node idx() { return in(3); }
- Node val() { return in(4); } // Only for stores, including op-to-memory
+ @Override public String label() { return op(); }
+ Node val() { return in(4); } // Only for stores
@Override protected ExprPrinter<Node> _printMach(ExprPrinter<Node> p) { return p.p(".").p(_name); }
- @Override public boolean isMem() { return _type instanceof TypeMem; }
-
- @Override public String label() { return op(); }
@Override public Type compute() { throw Utils.TODO(); }
@Override public Node idealize() { throw Utils.TODO(); }
+ // func3 is based on load/store size and extend
+ int func3() {
+ int func3 = -1;
+ // no unsigned flavour for store, so both signed and unsigned trigger the same
+ if(this instanceof StoreRISC) {
+ if( _declaredType == TypeInteger. I8 || _declaredType == TypeInteger.U8 || _declaredType == TypeInteger.BOOL) func3=0; // SB
+ if( _declaredType == TypeInteger.I16 || _declaredType == TypeInteger.U16 ) func3=1; // SH
+ if( _declaredType == TypeInteger.I32 || _declaredType == TypeInteger.U32 || _declaredType instanceof TypeMemPtr) func3=2; // SW
+ if( _declaredType == TypeInteger.BOT ) func3=3; // SD
+ if( func3 == -1 ) throw Utils.TODO();
+ return func3;
+ }
+ if( _declaredType == TypeInteger. I8 ) func3=0; // LB
+ if( _declaredType == TypeInteger.I16 ) func3=1; // LH
+ if( _declaredType == TypeInteger.I32 ) func3=2; // LW
+ if( _declaredType == TypeInteger.BOT ) func3=3; // LD
+ if( _declaredType == TypeInteger. U8 ) func3=4; // LBU
+ if( _declaredType == TypeInteger.BOOL) func3=4; // LBU
+ if( _declaredType == TypeInteger.U16 ) func3=5; // LHU
+ if( _declaredType == TypeInteger.U32 ) func3=6; // LWU
+
+ // float
+ if(_declaredType == TypeFloat.F32) func3 = 2; // fLW fSW
+ if(_declaredType == TypeFloat.F64) func3 = 3; // fLD fSD
+ if( _declaredType instanceof TypeMemPtr ) func3=3; // 8 byte pointers
+ if( func3 == -1 ) throw Utils.TODO();
+ return func3;
+ }
// Register mask allowed on input i.
@Override public RegMask regmap(int i) {
+ // 0 - ctrl
+ // 1 - memory
+ if( i==2 ) return riscv.RMASK; // base
+ // 2 - index
+ if( i==4 ) return riscv.MEM_MASK; // value
throw Utils.TODO();
}
- // 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("]");
+ return sb.p(_off).p("]");
}
}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/MulFRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/MulFRISC.java
index 8a066ab1..40609f96 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,30 +1,18 @@
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.isa.RiscV;
+import com.seaofnodes.simple.SB;
+import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.*;
-import java.io.ByteArrayOutputStream;
-public class MulFRISC extends MachConcreteNode implements MachNode{
+// fmul.d
+public class MulFRISC extends MachConcreteNode implements MachNode {
MulFRISC(Node mulf) {super(mulf);}
-
- // Register mask allowed on input i.
+ @Override public String op() { return "mulf"; }
@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; }
-
- // Encoding is appended into the byte array; size is returned
- @Override public int encoding(ByteArrayOutputStream bytes) {
- throw Utils.TODO();
- }
-
- // Default on double precision for now(64 bits)
- // General form: "fmul.d rd = src1 * src2
+ @Override public void encoding( Encoding enc ) { riscv.rf_type(enc,this,RiscV.RM.RNE,9); }
@Override public void asm(CodeGen code, SB sb) {
sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" * ").p(code.reg(in(2)));
}
-
- @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 0e2d35a3..a7961627 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,32 +1,17 @@
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.SB;
+import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.*;
-import com.seaofnodes.simple.type.TypeInteger;
-import java.io.ByteArrayOutputStream;
-
// mulh signed multiply instruction(no-imm form)
public class MulRISC extends MachConcreteNode implements MachNode{
- MulRISC(Node mul) {super(mul);}
-
- // Register mask allowed on input i.
+ public MulRISC(Node mul) {super(mul);}
+ @Override public String op() { return "mul"; }
@Override public RegMask regmap(int i) { assert i==1 || i==2; return riscv.RMASK; }
- // Register mask allowed as a result. 0 for no register.
@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();
- }
-
- // General form: "rd = rs1 * rs2"
+ @Override public void encoding( Encoding enc ) { riscv.r_type(enc,this,0,1); }
@Override public void asm(CodeGen code, SB sb) {
sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" * ").p(code.reg(in(2)));
}
-
- @Override public String op() { return "mul"; }
-
}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/NegRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/NegRISC.java
new file mode 100644
index 00000000..58340b32
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/NegRISC.java
@@ -0,0 +1,21 @@
+package com.seaofnodes.simple.node.cpus.riscv;
+
+import com.seaofnodes.isa.RiscV;
+import com.seaofnodes.simple.SB;
+import com.seaofnodes.simple.codegen.*;
+import com.seaofnodes.simple.node.*;
+
+public class NegRISC extends MachConcreteNode implements MachNode {
+ public NegRISC( Node neg ) { super(neg); }
+ @Override public String op() { return "neg"; }
+ @Override public RegMask regmap(int i) { assert i==1; return riscv.RMASK; }
+ @Override public RegMask outregmap() { return riscv.WMASK; }
+ @Override public void encoding( Encoding enc ) {
+ short dst = enc.reg(this );
+ short src = enc.reg(in(1));
+ RiscV.r_type(RiscV.OP,dst,0,0,src,0x20);
+ }
+ @Override public void asm(CodeGen code, SB sb) {
+ sb.p(code.reg(this)).p(" = -").p(code.reg(in(1)));
+ }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/NewRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/NewRISC.java
index e8d32570..8e3daaba 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,29 +1,47 @@
package com.seaofnodes.simple.node.cpus.riscv;
+import com.seaofnodes.isa.RiscV;
import com.seaofnodes.simple.*;
-import com.seaofnodes.simple.codegen.CodeGen;
-import com.seaofnodes.simple.codegen.RegMask;
+import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.*;
-import java.io.ByteArrayOutputStream;
+import com.seaofnodes.simple.print.ASMPrinter;
-public class NewRISC extends NewNode implements MachNode {
+public class NewRISC extends NewNode implements MachNode, RIPRelSize {
// A pre-zeroed chunk of memory.
NewRISC( NewNode nnn ) { super(nnn); }
- // Size and pointer result in standard calling convention; null for all the
- // memory and ordering edges
- @Override public RegMask regmap(int i) { return i == 2 ? riscv.A0_MASK : null; }
- @Override public RegMask outregmap(int i) { return i == 0 ? riscv.A0_MASK : null; }
- @Override public RegMask outregmap() { return null; }
+ @Override public void encoding( Encoding enc ) {
+ // Generic external encoding; 2 ops.
+ enc.external(this,"calloc");
+ // A1 is a caller-save, allowed to crush building external address
+ // auipc
+ enc.add4(RiscV.i_type(RiscV.OP_IMM , _arg2Reg , 0, riscv.ZERO, 1));
+ enc.add4(RiscV.u_type(RiscV.OP_AUIPC, riscv.A2 , 0));
+ enc.add4(RiscV.i_type(RiscV.OP_JALR , riscv.RPC, 0, riscv.A2, 0));
+ }
+
+ // Patch is for running "new" in a JIT.
+ // Delta is from opcode start
+ @Override public byte encSize(int delta) { return 4*3; }
- // Encoding is appended into the byte array; size is returned
- @Override public int encoding(ByteArrayOutputStream bytes) {
- throw Utils.TODO();
+ // Patch is for running "new" in a JIT.
+ // Delta is from opcode start
+ @Override public void patch( Encoding enc, int opStart, int opLen, int delta ) {
+ // Negative patches are JIT emulator targets.
+ if( opStart+delta < 0 ) {
+ delta -= 4; // Offset from the AUIPC start
+ int imm20 = delta>>12;
+ if( ((delta>>11)&1)==1 ) imm20++; // Correct accidental sign extension
+ int imm12 = delta&0xFFF;
+ enc.patch4(opStart+4,RiscV.u_type(RiscV.OP_AUIPC, riscv.A2 , imm20));
+ enc.patch4(opStart+8,RiscV.i_type(RiscV.OP_JALR , riscv.RPC, 0, riscv.A2, imm12));
+ } else
+ throw Utils.TODO();
}
- // General form: "alloc #bytes"
+ // General form: "alloc #bytes PC"
@Override public void asm(CodeGen code, SB sb) {
- sb.p(code.reg(size()));
+ sb.p("ldi a1=#1\n");
+ sb.p("auipc a2=#calloc\n");
+ sb.p("call a2+#calloc, a0");
}
-
- @Override public String op() { return "alloc"; }
}
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
index 6d30cd1c..6aefc54b 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/NotRISC.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/NotRISC.java
@@ -1,25 +1,22 @@
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.isa.RiscV;
+import com.seaofnodes.simple.SB;
+import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.MachConcreteNode;
-import com.seaofnodes.simple.type.TypeInteger;
-import java.io.ByteArrayOutputStream;
+import com.seaofnodes.simple.node.MachNode;
+import com.seaofnodes.simple.node.NotNode;
public class NotRISC extends MachConcreteNode implements MachNode {
- NotRISC(NotNode not) {super(not);}
+ public NotRISC(NotNode not) { super(not); }
+ @Override public String op() { return "not"; }
@Override public RegMask regmap(int i) { return riscv.RMASK; }
- @Override public RegMask outregmap() { return riscv.RMASK; }
-
- @Override public int twoAddress( ) { return 0; }
-
- @Override public int encoding(ByteArrayOutputStream bytes) {
- throw Utils.TODO();
+ @Override public RegMask outregmap() { return riscv.WMASK; }
+ @Override public void encoding( Encoding enc ) {
+ short dst = enc.reg(this );
+ short src = enc.reg(in(1));
+ // SEQZ is unsigned: negative nonzero inputs must also produce false.
+ enc.add4(RiscV.i_type(RiscV.OP_IMM, dst, 3, src, 1));
}
-
@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 f41d7162..53abcf0b 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,11 +1,13 @@
package com.seaofnodes.simple.node.cpus.riscv;
+import com.seaofnodes.isa.RiscV;
import com.seaofnodes.simple.Utils;
import com.seaofnodes.simple.node.Node;
public class OrIRISC extends ImmRISC {
- OrIRISC( Node and, int imm) { super(and,imm); }
- @Override int opcode() { throw Utils.TODO(); }
+ public OrIRISC( Node and, int imm) { super(and,imm); }
+ @Override int opcode() { return RiscV.OP_IMM; }
+ @Override int func3() { return 6; }
@Override public String glabel() { return "|"; }
@Override public String op() { return "ori"; }
}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/OrRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/OrRISC.java
index 7f808f90..103d4c7d 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,39 +1,16 @@
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.MachConcreteNode;
-import com.seaofnodes.simple.node.MachNode;
-import com.seaofnodes.simple.node.Node;
-import java.io.ByteArrayOutputStream;
+import com.seaofnodes.simple.codegen.*;
+import com.seaofnodes.simple.node.*;
public class OrRISC extends MachConcreteNode implements MachNode {
- OrRISC(Node or) {
- super(or);
- }
-
- // Register mask allowed on input i.
- // This is the normal calling convention
- @Override public RegMask regmap(int i) {
- assert i==1 || i==2;
- return riscv.RMASK;
- }
-
- // Register mask allowed as a result. 0 for no register.
+ public OrRISC(Node or) { super(or); }
+ @Override public String op() { return "or"; }
+ @Override public RegMask regmap(int i) { return riscv.RMASK; }
@Override public RegMask outregmap() { return riscv.WMASK; }
-
- // 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 encoding( Encoding enc ) { riscv.r_type(enc,this,6,0); }
@Override public void asm(CodeGen code, SB sb) {
sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" | ").p(code.reg(in(2)));
}
-
- @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 75674f37..0f8db4e5 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,26 +1,17 @@
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.Encoding;
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 {
final RegMask _rmask;
ParmRISC(ParmNode parm) {
super(parm);
- // Assume int
- _rmask = riscv.callInMask(fun().sig(),_idx);
+ _rmask = riscv.callInMask(fun().sig(),_idx,0);
}
-
- // Register mask allowed on input i. 0 for no register.
- @Override public RegMask regmap(int i) { return _rmask; }
- // Register mask allowed as a result. Calling convention register
+ @Override public RegMask regmap(int i) { return null; }
@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 void encoding( Encoding enc ) { }
}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/ProjRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/ProjRISC.java
index 42d5dc9b..2f9ac469 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,25 +1,15 @@
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.Encoding;
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); }
-
- // Register mask allowed on input i. 0 for no register.
@Override public RegMask regmap(int i) { 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) {
- throw Utils.TODO();
- }
+ @Override public void encoding( Encoding enc ) { }
}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/RetRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/RetRISC.java
index c9fa7fac..d28311dc 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,48 +1,22 @@
package com.seaofnodes.simple.node.cpus.riscv;
+import com.seaofnodes.isa.RiscV;
import com.seaofnodes.simple.*;
import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.Encoding;
import com.seaofnodes.simple.codegen.RegMask;
import com.seaofnodes.simple.node.FunNode;
import com.seaofnodes.simple.node.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); }
-
- // Register mask allowed on input i.
- // This is the normal calling convention
- @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; }
-
- // Encoding is appended into the byte array; size is returned
- @Override public int encoding(ByteArrayOutputStream bytes) {
- throw Utils.TODO();
+public class RetRISC extends ReturnNode implements MachNode {
+ public RetRISC(ReturnNode ret, FunNode fun) { super(ret, fun); fun.setRet(this); }
+ @Override public void encoding( Encoding enc ) {
+ int sz = fun()._frameAdjust;
+ if( sz >= 1L<<12 ) throw Utils.TODO();
+ if( sz != 0 )
+ enc.add4(RiscV.i_type(RiscV.OP_IMM, riscv.RSP, 0, riscv.RSP, sz & 0xFFF));
+ short rpc = enc.reg(rpc());
+ enc.add4(RiscV.i_type(RiscV.OP_JALR, riscv.ZERO, 0, rpc, 0));
}
-
- @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 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
- @Override public void postSelect() {
- FunNode fun = (FunNode)rpc().in(0);
- _fun = fun;
- fun.setRet(this);
- }
-
- @Override public String op() { return "ret"; }
}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SetFRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SetFRISC.java
index 3f9fc192..fabb9f92 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SetFRISC.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SetFRISC.java
@@ -1,15 +1,15 @@
package com.seaofnodes.simple.node.cpus.riscv;
+import com.seaofnodes.isa.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.codegen.*;
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
+// corresponds to feq.d = equal
+// fle.d = less than or equal
+// flt.d = less than
public class SetFRISC extends MachConcreteNode implements MachNode {
final String _bop; // One of <,<=,==
SetFRISC( BoolNode bool ) {
@@ -17,17 +17,18 @@ public class SetFRISC extends MachConcreteNode implements MachNode {
assert bool.isFloat();
_bop = bool.op();
}
+ @Override public String op() { return "set"+_bop; }
@Override public RegMask regmap(int i) { return riscv.FMASK; }
@Override public RegMask outregmap() { return riscv.WMASK; }
-
- // Encoding is appended into the byte array; size is returned
- @Override public int encoding(ByteArrayOutputStream bytes) {
- throw Utils.TODO();
+ @Override public void encoding( Encoding enc ) {
+ short dst = enc.reg(this );
+ short src1 = (short)(enc.reg(in(1))-riscv.F_OFFSET);
+ short src2 = (short)(enc.reg(in(2))-riscv.F_OFFSET);
+ int body = RiscV.r_type(RiscV.OP_FP,dst,RiscV.fsetop(_bop),src1,src2,0b1010001/*FCMP*/);
+ enc.add4(body);
}
@Override public void asm(CodeGen code, SB sb) {
- sb.p(code.reg(in(1))).p(" ").p(_bop).p(" ").p(code.reg(in(2)));
+ sb.p(code.reg(this)).p(" = ").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
index 3c0f76ae..8b6512e1 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SetIRISC.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SetIRISC.java
@@ -1,17 +1,21 @@
package com.seaofnodes.simple.node.cpus.riscv;
import com.seaofnodes.simple.Utils;
-import com.seaofnodes.simple.node.BoolNode;
+import com.seaofnodes.simple.node.Node;
-// corresponds to slt,sltu,slti,sltiu, seqz
+// corresponds to slti,sltiu
public class SetIRISC extends ImmRISC {
- final String _bop; // One of <,<=,==
- SetIRISC( BoolNode bool, int imm ) {
- super(bool,imm);
- assert !bool.isFloat();
- _bop = bool.op();
+ final boolean _unsigned; // slti vs sltiu
+ public SetIRISC( Node src, int imm12, boolean unsigned ) {
+ this(src,imm12,unsigned,true);
}
- @Override int opcode() { throw Utils.TODO(); }
- @Override public String glabel() { return _bop; }
- @Override public String op() { return "set"+_bop; }
+ public SetIRISC( Node src, int imm12, boolean unsigned, boolean pop ) {
+ super(src,imm12,pop);
+ if( !pop ) _inputs.setLen(2);
+ _unsigned = unsigned;
+ }
+ @Override public String op() { return "slt" + (_unsigned ? "u":"") + "i"; }
+ @Override public String glabel() { return "<"+ (_unsigned ? "u":""); }
+ @Override int opcode() { return 0b0010011; }
+ @Override int func3() { return _unsigned ? 0b011 : 0b010; }
}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SetRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SetRISC.java
index fb7650c7..8d4a2f71 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,33 +1,20 @@
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;
+import com.seaofnodes.simple.codegen.*;
+import com.seaofnodes.simple.node.*;
-// corresponds to slt,sltu,slti,sltiu, seqz
+// corresponds to slt sltu
public class SetRISC extends MachConcreteNode implements MachNode {
- final String _bop; // One of <,<=,==
- SetRISC( BoolNode bool ) {
- super(bool);
- assert !bool.isFloat();
- _bop = bool.op();
- }
- @Override public RegMask regmap(int i) { throw Utils.TODO(); }
+ final boolean _unsigned;
+ public SetRISC( Node cmp, boolean unsigned ) { super(cmp); _unsigned=unsigned; }
+ @Override public String op() { return "slt" + (_unsigned ? "u":""); }
+ @Override public RegMask regmap(int i) { return riscv.RMASK; }
@Override public RegMask outregmap() { return riscv.WMASK; }
-
- // Encoding is appended into the byte array; size is returned
- @Override public int encoding(ByteArrayOutputStream bytes) {
- throw Utils.TODO();
+ @Override public void encoding( Encoding enc ) {
+ riscv.r_type(enc,this,_unsigned ? 0b011 : 0b010,0);
}
-
@Override public void asm(CodeGen code, SB sb) {
- sb.p(code.reg(in(1))).p(" ").p(_bop).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 "set"+_bop; }
-}
+} \ No newline at end of file
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SllIRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SllIRISC.java
index b5202908..52b238ec 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,11 +1,13 @@
package com.seaofnodes.simple.node.cpus.riscv;
+import com.seaofnodes.isa.RiscV;
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(); }
+ public SllIRISC( Node and, int imm) { super(and,imm); }
+ @Override int opcode() { return RiscV.OP_IMM; }
+ @Override int func3() { return 1; }
@Override public String glabel() { return "<<"; }
@Override public String op() { return "slli"; }
}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SllRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SllRISC.java
index 447f604a..87e6693e 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,37 +1,17 @@
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.SB;
+import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.*;
-import com.seaofnodes.simple.type.Type;
-import com.seaofnodes.simple.type.TypeInteger;
-import java.io.ByteArrayOutputStream;
-import java.util.BitSet;
-public class SllRISC extends MachConcreteNode implements MachNode{
- SllRISC(Node sll) {super(sll);}
-
- // Register mask allowed on input i.
- // This is the normal calling convention
- @Override public RegMask regmap(int i) {
- //assert i==1;
- return riscv.RMASK; }
- // Register mask allowed as a result. 0 for no register.
+// Shift left logical
+public class SllRISC extends MachConcreteNode implements MachNode {
+ public SllRISC(Node sll) { super(sll); }
+ @Override public String op() { return "shl"; }
+ @Override public RegMask regmap(int i) { return riscv.RMASK; }
@Override public RegMask outregmap() { return riscv.WMASK; }
-
- // Encoding is appended into the byte array; size is returned
- @Override public int encoding(ByteArrayOutputStream bytes) {
- throw Utils.TODO();
- }
-
- // General form
- // General form: "sll rd, rs1, rs2"
+ @Override public void encoding( Encoding enc ) { riscv.r_type(enc,this,1,0); }
@Override public void asm(CodeGen code, SB sb) {
-
sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" << ").p(code.reg(in(2)));
-
}
-
- @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
index b5076ba0..ca1bf614 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SplitRISC.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SplitRISC.java
@@ -1,21 +1,67 @@
package com.seaofnodes.simple.node.cpus.riscv;
+import com.seaofnodes.isa.RiscV;
+import com.seaofnodes.simple.SB;
import com.seaofnodes.simple.Utils;
-import com.seaofnodes.simple.codegen.RegMask;
+import com.seaofnodes.simple.codegen.*;
+import com.seaofnodes.simple.node.FunNode;
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; }
+ @Override public void encoding( Encoding enc ) {
+ short dst = enc.reg(this );
+ short src = enc.reg(in(1));
+
+ // Flag for being either XMM or stack
+ boolean dstX = dst >= riscv.F_OFFSET;
+ boolean srcX = src >= riscv.F_OFFSET;
+
+ if( dst >= riscv.MAX_REG ) {
+ // Store to SP
+ if( src >= riscv.MAX_REG ) {
+ throw Utils.TODO(); // Very rare stack-stack move
+ }
+ int off = enc._fun.computeStackOffset(enc._code,dst);
+ int op = srcX ? RiscV.OP_STOREFP : RiscV.OP_STORE;
+ if( srcX ) src -= riscv.F_OFFSET;
+ enc.add4(RiscV.s_type(op, 0b011, riscv.RSP, src, off));
+ return;
+ }
+ if( src >= riscv.MAX_REG ) {
+ // Load from SP
+ int off = enc._fun.computeStackOffset(enc._code,src);
+ int op = dstX ? RiscV.OP_LOADFP : RiscV.OP_LOAD;
+ if( dstX ) dst -= riscv.F_OFFSET;
+ enc.add4(RiscV.i_type(op, dst, 0b011, riscv.RSP, off));
+ return;
+ }
+ // pick opcode based on regs
+ if( !dstX && !srcX ) {
+ // GPR->GPR
+ enc.add4(RiscV.r_type(RiscV.OP,dst,0,src,riscv.ZERO,0));
+ } else if( dstX && srcX ) {
+ // FPR->FPR
+ src -= riscv.F_OFFSET;
+ dst -= riscv.F_OFFSET;
+ enc.add4(RiscV.r_type(RiscV.OP_FP, dst, 0b000, src, src, 0b0010101)); // dst = FMIN(src,src)
+ } else if(!srcX && dstX) {
+ //GPR->FPR
+ // fmv.d.x
+ enc.add4(RiscV.r_type(RiscV.OP_FP, dst - riscv.F_OFFSET, 0, src, 0, 0b1111001));
+ } else if(srcX && !dstX) {
+ //FPR->GPR
+ //fmv.x.d
+ enc.add4(RiscV.r_type(RiscV.OP_FP, dst, 0, src - riscv.F_OFFSET, 0, 0b1110001));
+ }
+ }
- // Encoding is appended into the byte array; size is returned
- @Override public int encoding(ByteArrayOutputStream bytes) {
- throw Utils.TODO();
+ // General form: "mov dst = src"
+ @Override public void asm(CodeGen code, SB sb) {
+ FunNode fun = code._encoding==null ? null : code._encoding._fun;
+ sb.p(code.reg(this,fun)).p(" = ").p(code.reg(in(1),fun));
}
}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SraIRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SraIRISC.java
index d5ab5e08..49b1cad9 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,11 +1,13 @@
package com.seaofnodes.simple.node.cpus.riscv;
+import com.seaofnodes.isa.RiscV;
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(); }
+ public SraIRISC( Node and, int imm) { super(and,imm); }
+ @Override int opcode() { return RiscV.OP_IMM; }
+ @Override int func3() { return 5;}
@Override public String glabel() { return ">>"; }
@Override public String op() { return "srai"; }
}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SraRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SraRISC.java
index 0726e292..1e930e72 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,38 +1,17 @@
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.MachConcreteNode;
-import com.seaofnodes.simple.node.MachNode;
-import com.seaofnodes.simple.node.Node;
-import com.seaofnodes.simple.type.TypeInteger;
-import java.io.ByteArrayOutputStream;
+import com.seaofnodes.simple.codegen.*;
+import com.seaofnodes.simple.node.*;
// Right Shift Arithmetic
public class SraRISC extends MachConcreteNode implements MachNode {
-
- SraRISC(Node sra) {super(sra);}
-
- // Register mask allowed on input i.
- // This is the normal calling convention
- @Override public RegMask regmap(int i) {
- // assert i==1;
- return riscv.RMASK; }
- // Register mask allowed as a result. 0 for no register.
+ public SraRISC(Node sra) { super(sra); }
+ @Override public String op() { return "sar"; }
+ @Override public RegMask regmap(int i) { return riscv.RMASK; }
@Override public RegMask outregmap() { return riscv.WMASK; }
-
- // Encoding is appended into the byte array; size is returned
- @Override public int encoding(ByteArrayOutputStream bytes) {
- throw Utils.TODO();
- }
-
- // General form
- // General form: "sra rd, rs1, rs2"
+ @Override public void encoding( Encoding enc ) { riscv.r_type(enc,this,0b101,0b00100000); }
@Override public void asm(CodeGen code, SB sb) {
sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" >> ").p(code.reg(in(2)));
}
-
- @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 a4eb99bc..985abe2c 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,11 +1,13 @@
package com.seaofnodes.simple.node.cpus.riscv;
+import com.seaofnodes.isa.RiscV;
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(); }
+ public SrlIRISC( Node and, int imm, boolean pop) { super(and,imm,pop); }
+ @Override int opcode() { return RiscV.OP_IMM; }
+ @Override int func3() { return 5; }
@Override public String glabel() { return ">>>"; }
@Override public String op() { return "srli"; }
}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SrlRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SrlRISC.java
index d1435540..47712585 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,38 +1,17 @@
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.SB;
+import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.*;
-import com.seaofnodes.simple.type.Type;
-import com.seaofnodes.simple.type.TypeInteger;
-import java.io.ByteArrayOutputStream;
-import java.util.BitSet;
-import java.lang.StringBuilder;
// Right Shift Logical
public class SrlRISC extends MachConcreteNode implements MachNode {
- SrlRISC(Node srli) {super(srli);}
-
- // Register mask allowed on input i.
- // This is the normal calling convention
- @Override public RegMask regmap(int i) {
- // assert i==1;
- return riscv.RMASK; }
- // Register mask allowed as a result. 0 for no register.
+ public SrlRISC(Node srli) { super(srli); }
+ @Override public String op() { return "shr"; }
+ @Override public RegMask regmap(int i) { return riscv.RMASK; }
@Override public RegMask outregmap() { return riscv.WMASK; }
-
- // Encoding is appended into the byte array; size is returned
- @Override public int encoding(ByteArrayOutputStream bytes) {
- throw Utils.TODO();
- }
-
- // General form
- // General form: "srl rd, rs1, rs2"
+ @Override public void encoding( Encoding enc ) { riscv.r_type(enc,this,7,0); }
@Override public void asm(CodeGen code, SB sb) {
-
sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" >>> ").p(code.reg(in(2)));
}
-
- @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 22e443fd..3cb7cc2d 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,15 +1,11 @@
package com.seaofnodes.simple.node.cpus.riscv;
+import com.seaofnodes.isa.RiscV;
import com.seaofnodes.simple.SB;
import com.seaofnodes.simple.Utils;
-import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.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;
+import com.seaofnodes.simple.node.*;
// Store memory addressing on ARM
// Support imm, reg(direct), or reg+off(indirect) addressing
@@ -24,33 +20,29 @@ import java.util.BitSet;
// sw rs2,offset(rs1)
public class StoreRISC extends MemOpRISC {
- 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
+ StoreRISC( StoreNode st, Node base, int off, Node val ) { super(st, base, off, val); }
+ @Override public String op() { return "st"+_sz; }
@Override public RegMask regmap(int i) {
- if( i==1 ) return riscv.MEM_MASK;
- if( i==2 ) return riscv.RMASK;
- if( i==3 ) return riscv.RMASK;
- if( i==4 ) return riscv.RMASK;
- return null; // Trailing anti-dependence edges
+ // 0 - ctrl
+ if( i==1 ) return null; // mem
+ if( i==2 ) return riscv.RMASK; // ptr
+ // 2 - index
+ if( i==4 ) return _sz >= '4' ? riscv.MEM_MASK : riscv.RMASK; // No byte/short FP stores
+ return null; // Anti-dependence
}
-
-
- // 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 encoding( Encoding enc ) {
+ short val = enc.reg(val());
+ short ptr = enc.reg(ptr());
+ int op = val >= riscv.F_OFFSET ? RiscV.OP_STOREFP : RiscV.OP_STORE;
+ if( val >= riscv.F_OFFSET ) val -= riscv.F_OFFSET;
+ enc.add4(RiscV.s_type(op, func3()&7, ptr, val == -1 ? riscv.ZERO : val, _off));
}
@Override public void asm(CodeGen code, SB sb) {
asm_address(code,sb).p(",");
- if( val()==null ) sb.p("#").p(_imm);
+ if( val()==null ) sb.p("#").p("0");
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 e6fe3012..caf751bb 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,30 +1,18 @@
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.isa.RiscV;
+import com.seaofnodes.simple.SB;
+import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.*;
-import com.seaofnodes.simple.type.TypeInteger;
-import java.io.ByteArrayOutputStream;
-public class SubFRISC extends MachConcreteNode implements MachNode{
+// fsub.d
+public class SubFRISC extends MachConcreteNode implements MachNode {
SubFRISC(Node subf) {super(subf);}
-
- // Register mask allowed on input i.
+ @Override public String op() { return "subf"; }
@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; }
-
- // Encoding is appended into the byte array; size is returned
- @Override public int encoding(ByteArrayOutputStream bytes) {
- throw Utils.TODO();
- }
-
- // Default on double precision for now(64 bits)
- // General form: "fsub.d rd = src1 - src2
+ @Override public void encoding( Encoding enc ) { riscv.rf_type(enc,this,RiscV.RM.RNE,5); }
@Override public void asm(CodeGen code, SB sb) {
sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" + ").p(code.reg(in(2)));
}
-
- @Override public String op() { return "subf"; }
}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SubRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SubRISC.java
index c37aea98..7ab4f904 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,29 +1,16 @@
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.codegen.*;
import com.seaofnodes.simple.node.*;
-import com.seaofnodes.simple.type.TypeInteger;
-import java.io.ByteArrayOutputStream;
public class SubRISC extends MachConcreteNode implements MachNode {
- SubRISC( Node sub ) { super(sub); }
-
- // Register mask allowed on input i.
+ public SubRISC( Node sub ) { super(sub); }
+ @Override public String op() { return "sub"; }
@Override public RegMask regmap(int i) { assert i==1 || i==2; return riscv.RMASK; }
- // Register mask allowed as a result. 0 for no register.
@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();
- }
-
- // General form: "sub # rd = rs1 - rs2"
+ @Override public void encoding( Encoding enc ) { riscv.r_type(enc,this,0,0x20); }
@Override public void asm(CodeGen code, SB sb) {
sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" - ").p(code.reg(in(2)));
}
-
- @Override public String op() { return "sub"; }
}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/TFPRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/TFPRISC.java
index 23fb6a74..1d3c5920 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,39 +1,49 @@
package com.seaofnodes.simple.node.cpus.riscv;
+import com.seaofnodes.isa.RiscV;
import com.seaofnodes.simple.*;
-import com.seaofnodes.simple.codegen.CodeGen;
-import com.seaofnodes.simple.codegen.RegMask;
+import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.ConstantNode;
import com.seaofnodes.simple.node.MachNode;
-import com.seaofnodes.simple.type.Type;
import com.seaofnodes.simple.type.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.
+public class TFPRISC extends ConstantNode implements MachNode, RIPRelSize {
+ TFPRISC(ConstantNode con) { super(con); }
+ @Override public String op() { return "ldx"; }
@Override public RegMask regmap(int i) { return null; }
- // General int registers
@Override public RegMask outregmap() { return riscv.WMASK; }
-
@Override public boolean isClone() { return true; }
@Override public TFPRISC copy() { return new TFPRISC(this); }
+ @Override public void encoding( Encoding enc ) {
+ enc.relo(this);
+ // TODO: 1 op encoding, plus a TODO if it does not fit
+ short dst = enc.reg(this);
+ TypeFunPtr tfp = (TypeFunPtr)_con;
+ // auipc t0,0
+ int auipc = RiscV.u_type(RiscV.OP_AUIPC, dst, 0);
+ // addi t1,t0 + #0
+ int addi = RiscV.i_type(RiscV.OP_IMM, dst, 0, dst, 0);
+ enc.add4(auipc);
+ enc.add4(addi);
+ }
- // Encoding is appended into the byte array; size is returned
- @Override public int encoding(ByteArrayOutputStream bytes) {
- throw Utils.TODO();
+ @Override public byte encSize(int delta) { return 8; }
+
+ // Delta is from opcode start
+ @Override public void patch( Encoding enc, int opStart, int opLen, int delta ) {
+ short rpc = enc.reg(this);
+ if( opLen==8 ) {
+ // ADDI sign-extends its low 12 bits, so round the AUIPC high part.
+ enc.patch4(opStart ,RiscV.u_type(RiscV.OP_AUIPC, rpc, (delta+0x800)>>12));
+ enc.patch4(opStart+4,RiscV.i_type(RiscV.OP_IMM, rpc, 0, rpc, delta & 0xFFF));
+ } else {
+ // should not happen as one instruction is 4 byte, and TFP arm encodes 2.
+ throw Utils.TODO();
+ }
}
- // Human-readable form appended to the SB. Things like the encoding,
- // indentation, leading address or block labels not printed here.
- // Just something like "ld4\tR17=[R18+12] // Load array base".
- // General form: "op\tdst=src+src"
@Override public void asm(CodeGen code, SB sb) {
- String reg = code.reg(this);
- if( _con == Type.NIL )
- sb.p(reg).p(",").p(reg);
- else
- _con.print(sb.p(reg).p(" #"));
+ _con.print(sb.p(code.reg(this)).p(" #"));
}
+
}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/UJmpRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/UJmpRISC.java
new file mode 100644
index 00000000..cc47b7ed
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/UJmpRISC.java
@@ -0,0 +1,48 @@
+package com.seaofnodes.simple.node.cpus.riscv;
+
+import com.seaofnodes.isa.RiscV;
+import com.seaofnodes.print.ExprPrinter;
+
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.codegen.*;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.type.Type;
+
+// unconditional jump
+public class UJmpRISC extends CFGNode implements MachNode, RIPRelSize {
+ @Override public String op() { return "jmp"; }
+ @Override public String label() { return op(); }
+ @Override protected ExprPrinter<Node> _print1(ExprPrinter<Node> p) { return p.p("jmp "); }
+ @Override public RegMask regmap(int i) {return null; }
+ @Override public RegMask outregmap() { return null; }
+ @Override public Type compute() { throw Utils.TODO(); }
+ @Override public Node idealize() { throw Utils.TODO(); }
+ @Override public void encoding( Encoding enc ) {
+ // Short form +/-4K: beq r0,r0,imm12
+ // Long form: auipc rX,imm20/32; jal r0,[rX+imm12/32]
+ enc.jump(this,uctrl());
+ enc.add4(RiscV.j_type(RiscV.OP_JAL, 0, 0));
+ }
+
+ // Delta is from opcode start
+ @Override public byte encSize(int delta) {
+ if( -(1L<<20) <= delta && delta < (1L<<20) ) return 4;
+ // 2 word encoding needs a tmp register, must teach RA
+ throw Utils.TODO();
+ }
+
+ // Delta is from opcode start
+ @Override public void patch( Encoding enc, int opStart, int opLen, int delta ) {
+ if( opLen==4 ) {
+ enc.patch4(opStart,RiscV.j_type(RiscV.OP_JAL, 0, delta));
+ } else {
+ throw Utils.TODO();
+ }
+ }
+
+ @Override public void asm(CodeGen code, SB sb) {
+ CFGNode target = uctrl();
+ //assert target.nOuts() > 1; // Should optimize jmp to empty targets
+ sb.p(label(target));
+ }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/XorIRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/XorIRISC.java
index a2e32f06..df96d10b 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,11 +1,13 @@
package com.seaofnodes.simple.node.cpus.riscv;
+import com.seaofnodes.isa.RiscV;
import com.seaofnodes.simple.Utils;
import com.seaofnodes.simple.node.Node;
public class XorIRISC extends ImmRISC {
- XorIRISC( Node and, int imm) { super(and,imm); }
- @Override int opcode() { throw Utils.TODO(); }
+ public XorIRISC( Node and, int imm) { super(and,imm); }
+ @Override int opcode() { return RiscV.OP_IMM; }
+ @Override int func3() { return 4; }
@Override public String glabel() { return "^"; }
@Override public String op() { return "xori"; }
}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/XorRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/XorRISC.java
index f4ecb430..725d2b78 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,41 +1,16 @@
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.MachConcreteNode;
-import com.seaofnodes.simple.node.MachNode;
-import com.seaofnodes.simple.node.Node;
-import com.seaofnodes.simple.type.TypeInteger;
-import java.io.ByteArrayOutputStream;
+import com.seaofnodes.simple.codegen.*;
+import com.seaofnodes.simple.node.*;
public class XorRISC extends MachConcreteNode implements MachNode {
-
- XorRISC(Node or) {
- super(or);
- }
-
- // Register mask allowed on input i.
- // This is the normal calling convention
- @Override public RegMask regmap(int i) {
- assert i==1 || i==2;
- return riscv.RMASK;
- }
-
- // Register mask allowed as a result. 0 for no register.
+ public XorRISC(Node or) { super(or); }
+ @Override public String op() { return "xor"; }
+ @Override public RegMask regmap(int i) { return riscv.RMASK; }
@Override public RegMask outregmap() { return riscv.WMASK; }
-
- // 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 encoding( Encoding enc ) { riscv.r_type(enc,this,4,0); }
@Override public void asm(CodeGen code, SB sb) {
-
sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" ^ ").p(code.reg(in(2)));
-
}
}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/riscv.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/riscv.java
index 0542085c..335347e9 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,49 +1,65 @@
package com.seaofnodes.simple.node.cpus.riscv;
+import com.seaofnodes.isa.RiscV;
+import static com.seaofnodes.isa.RiscV.*;
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.codegen.*;
import com.seaofnodes.simple.node.*;
import com.seaofnodes.simple.type.*;
public class riscv extends Machine {
+
+ public riscv(CodeGen code) {
+ if( !"SystemV".equals(code._callingConv) )
+ throw new IllegalArgumentException("Unknown calling convention "+code._callingConv);
+ }
+
@Override public String name() {return "riscv";}
+ //@Override public int maxReg() { return MAX_REG; }
// Using ABI names instead of register names
- 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;
+ public static int ZERO = 0, RPC= 1, RSP= 2, S12= 3, S13= 4, T0 = 5, T1 = 6, T2 = 7;
+ public static int S0 = 8, S1 = 9, A0 = 10, A1 = 11, A2 = 12, A3 = 13, A4 = 14, A5 = 15;
+ public static int A6 = 16, A7 = 17, S2 = 18, S3 = 19, S4 = 20, S5 = 21, S6 = 22, S7 = 23;
+ public static int S8 = 24, S9 = 25, S10= 26, S11= 27, T3 = 28, T4 = 29, T5 = 30, T6 = 31;
// FP registers
- 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;
+ public static int F0 = 32, F1 = 33, F2 = 34, F3 = 35, F4 = 36, F5 = 37, F6 = 38, F7 = 39;
+ public static int FS0 = 40, FS1 = 41, FA0 = 42, FA1 = 43, FA2 = 44, FA3 = 45, FA4 = 46, FA5 = 47;
+ public static int FA6 = 48, FA7 = 49, FS2 = 50, FS3 = 51, FS4 = 52, FS5 = 53, FS6 = 54, FS7 = 55;
+ public static int FS8 = 56, FS9 = 57, FS10 = 58, FS11 = 59, FT8 = 60, FT9 = 61, FT10 = 62, FT11 = 63;
+
+ static final int MAX_REG = 64;
- static final int MAX_REG = 61;
+ public static final int F_OFFSET = 32;
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" ,
+ "zero","rpc" , "rsp" , "s12" , "s13" , "t0" , "t1" , "t2" ,
+ "s0" , "s1" , "a0" , "a1" , "a2" , "a3" , "a4" , "a5" ,
+ "a6" , "a7" , "s2" , "s3" , "s4" , "s5" , "s6" , "s7" ,
+ "s8" , "s9" , "s10" , "s11" , "t3" , "t4" , "t5" , "t6" ,
"f0" , "f1" , "f2" , "f3" , "f4" , "f5" , "f6" , "f7" ,
"fs0" , "fs1" , "fa0" , "fa1" , "fa2" , "fa3" , "fa4" , "fa5" ,
"fa6" , "fa7" , "fs2" , "fs3" , "fs4" , "fs5" , "fs6" , "fs7" ,
"fs8" , "fs9" , "fs10", "fs11", "ft8" , "ft9" , "ft10", "ft11"
+ // Register-based naming hella easier to debug than "official" register
+ // names when single-stepping in debugger.
+ //"zero", "r1" , "rsp" , "r3" , "r4" , "r5" , "r6" , "r7" ,
+ //"r8" , "r9" , "r10" , "r11", "r12", "r13", "r14", "r15",
+ //"r16" , "r17" , "r18" , "r19", "r20", "r21", "r22", "r23",
+ //"r24" , "r25" , "r26" , "r27", "r28", "r29", "r30", "r31",
+ //"f0" , "f1" , "f2" , "f3" , "f4" , "f5" , "f6" , "f7" ,
+ //"fs0" , "fs1" , "fa0" , "fa1" , "fa2" , "fa3" , "fa4" , "fa5" ,
+ //"fa6" , "fa7" , "fs2" , "fs3" , "fs4" , "fs5" , "fs6" , "fs7" ,
+ //"fs8" , "fs9" , "fs10", "fs11", "ft8" , "ft9" , "ft10", "ft11"
};
- @Override public String reg( int reg ) {
- return reg < REGS.length ? REGS[reg] : "[rsp+"+(reg-REGS.length)*4+"]";
- }
-
+ @Override public String[] regs() { return REGS; }
// General purpose register mask: pointers and ints, not floats
static final long RD_BITS = 0b11111111111111111111111111111111L; // All the GPRs
static final RegMask RMASK = new RegMask(RD_BITS);
- static final long WR_BITS = 0b11111111111111111111111111111010L; // All the GPRs, minus ZERO and SP
+ static final long WR_BITS = 0b11111111111111111111111111111010L; // All the GPRs, minus ZERO and RSP
static final RegMask WMASK = new RegMask(WR_BITS);
// Float mask from(f0-ft10). TODO: ft10,ft11 needs a larger RegMask
static final long FP_BITS = 0b11111111111111111111111111111111L<<F0;
@@ -54,28 +70,27 @@ public class riscv extends Machine {
static final RegMask SPLIT_MASK = new RegMask( WR_BITS | FP_BITS, -1L );
-
// Arguments masks
- 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 A0_MASK = new RegMask(A0);
+ public static RegMask A1_MASK = new RegMask(A1);
+ public static RegMask A2_MASK = new RegMask(A2);
+ public static RegMask A3_MASK = new RegMask(A3);
+ public static RegMask A4_MASK = new RegMask(A4);
+ public static RegMask A5_MASK = new RegMask(A5);
+ public static RegMask A6_MASK = new RegMask(A6);
+ public static RegMask A7_MASK = new RegMask(A7);
- static final RegMask RPC_MASK = new RegMask(RPC);
+ public static final RegMask RPC_MASK = new RegMask(RPC);
// Float arguments masks
- 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);
+ public static RegMask FA0_MASK = new RegMask(FA0);
+ public static RegMask FA1_MASK = new RegMask(FA1);
+ public static RegMask FA2_MASK = new RegMask(FA2);
+ public static RegMask FA3_MASK = new RegMask(FA3);
+ public static RegMask FA4_MASK = new RegMask(FA4);
+ public static RegMask FA5_MASK = new RegMask(FA5);
+ public static RegMask FA6_MASK = new RegMask(FA6);
+ public static RegMask FA7_MASK = new RegMask(FA7);
// Int arguments calling conv
static RegMask[] CALLINMASK = new RegMask[] {
@@ -90,17 +105,36 @@ public class riscv extends Machine {
};
static RegMask[] XMMS = new RegMask[] {
- FA0_MASK,
- FA1_MASK,
- FA2_MASK,
- FA3_MASK,
- FA4_MASK,
- FA5_MASK,
- FA6_MASK,
- FA7_MASK
+ FA0_MASK,
+ FA1_MASK,
+ FA2_MASK,
+ FA3_MASK,
+ FA4_MASK,
+ FA5_MASK,
+ FA6_MASK,
+ FA7_MASK
};
- static RegMask callInMask( TypeFunPtr tfp, int idx ) {
+ // Since riscv instructions are fixed we can just or them together
+
+ public static void r_type(Encoding enc, Node n, int func3, int func7) {
+ short dst = enc.reg(n);
+ short src1 = enc.reg(n.in(1));
+ short src2 = enc.reg(n.in(2));
+ int body = RiscV.r_type(OP,dst,func3,src1,src2,func7);
+ enc.add4(body);
+ }
+
+ public static void rf_type(Encoding enc, Node n, RM func3, int func7) {
+ short dst = (short)(enc.reg(n )-F_OFFSET);
+ short src1 = (short)(enc.reg(n.in(1))-F_OFFSET);
+ short src2 = (short)(enc.reg(n.in(2))-F_OFFSET);
+ int body = RiscV.r_type(OP_FP,dst,func3.ordinal(),src1,src2,func7);
+ enc.add4(body);
+ }
+
+ @Override public RegMask callArgMask( TypeFunPtr tfp, int idx, int maxArgSlot ) { return callInMask(tfp,idx,maxArgSlot); }
+ static RegMask callInMask( TypeFunPtr tfp, int idx, int maxArgSlot ) {
if( idx==0 ) return RPC_MASK;
if( idx==1 ) return null;
// Count floats in signature up to index
@@ -117,112 +151,96 @@ public class riscv extends Machine {
if( idx-2-fcnt < cargs.length )
return cargs[idx-2-fcnt];
}
- throw Utils.TODO(); // Pass on stack slot
+ // Pass on stack slot(8 and higher)
+ if( maxArgSlot>0 ) throw Utils.TODO();
+ return new RegMask(MAX_REG + 1 + (idx - 2));
}
- // callee saved(riscv)
- 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);
+ @Override public short maxArgSlot( TypeFunPtr tfp ) {
+ int icnt=0, fcnt=0; // Count of ints, floats
+ for( int i=0; i<tfp.nargs(); i++ ) {
+ if( tfp.arg(i) instanceof TypeFloat ) fcnt++;
+ else icnt++;
}
- }
- static RegMask retMask( TypeFunPtr tfp, int i ) {
- return i==2
- ? (tfp.ret() instanceof TypeFloat ? FA0_MASK : A0_MASK)
- : RET_MASKS[i];
+ int nstk = Math.max(icnt-8,0)+Math.max(fcnt-8,0);
+ return (short)nstk;
}
+ static final long CALLER_SAVE =
+ (1L<<RPC) |
+ (1L<<A0 ) | (1L<<A1 ) | (1L<<A2 ) | (1L<<A3 ) | (1L<<A4 ) | (1L<<A5 ) | (1L<<A6 ) | (1L<<A7 ) |
+ (1L<<T0 ) | (1L<<T1 ) | (1L<<T2 ) | (1L<<T3 ) | (1L<<T4 ) | (1L<<T5 ) | (1L<<T6 ) |
+ (1L<<FA0) | (1L<<FA1) | (1L<<FA2) | (1L<<FA3) | (1L<<FA4) | (1L<<FA5) | (1L<<FA6) | (1L<<FA7) |
+ (1L<<F0 ) | (1L<<F1 ) | (1L<<F2 ) | (1L<<F3 ) | (1L<<F4 ) | (1L<<F5 ) | (1L<<F6 ) | (1L<<F7 ) |
+ (1L<<FT8) | (1L<<FT9) | (1L<<FT10)| (1L<<FT11);
+
+ @Override public long callerSave() { return CALLER_SAVE; }
+ @Override public long neverSave() { return (1L<<RSP) | (1L<<ZERO); }
+ @Override public RegMask retMask( TypeFunPtr tfp ) {
+ return tfp.ret() instanceof TypeFloat ? FA0_MASK : A0_MASK;
+ }
+ @Override public int rpc() { return RPC; }
// Return a MachNode unconditional branch
- @Override public CFGNode jump() {
- throw Utils.TODO();
- }
+ @Override public CFGNode jump() { return new UJmpRISC(); }
// Create a split op; any register to any register, including stack slots
@Override public SplitNode split(String kind, byte round, LRG lrg) { return new SplitRISC(kind,round); }
- // 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
+ public static boolean imm12(TypeInteger ti) {
+ // 52 = 64-12
return ti.isConstant() && ((ti.value()<<52)>>52) == ti.value();
}
-
+ // True if HIGH 20-bit signed immediate, with all zeros low.
+ public static boolean imm20Exact(TypeInteger ti) {
+ // shift left 32 to clear out the upper 32 bits.
+ // shift right SIGNED to sign-extend upper 32 bits; then shift 12 more to clear out lower 12 bits.
+ // shift left 12 to re-center the bits.
+ return ti.isConstant() && (((ti.value()<<32)>>>44)<<12) == ti.value();
+ }
@Override public Node instSelect( Node n ) {
return switch (n) {
- case AddFNode addf -> addf(addf);
- case AddNode add -> add(add);
- case AndNode and -> and(and);
- case BoolNode bool -> cmp(bool);
- case CallNode call -> call(call);
- case CastNode cast -> new CastRISC(cast);
+ 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 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 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 MinusNode neg -> new NegRISC(neg);
+ case MulFNode mulf -> new MulFRISC(mulf);
+ case MulNode mul -> new MulRISC(mul);
+ case NewNode nnn -> nnn(nnn);
+ case NotNode not -> new NotRISC(not);
+ case OrNode or -> or(or);
+ case ParmNode parm -> new ParmRISC(parm);
case BulkMemPhiNode phi -> new BulkMemPhiNode(phi);
case MemPhiNode phi -> new MemPhiNode(phi);
- case PhiNode phi -> new PhiNode(phi);
- case ProjNode prj -> prj(prj);
- case ReadOnlyNode read -> new ReadOnlyNode(read);
- case ReturnNode ret -> new RetRISC(ret, ret.fun());
- case SarNode sar -> sra(sar);
- case ShlNode shl -> sll(shl);
- case ShrNode shr -> srl(shr);
- case StartNode start -> new StartNode(start);
- case StopNode stop -> new StopNode(stop);
- case StoreNode st -> st(st);
- case SubFNode subf -> new SubFRISC(subf);
- case SubNode sub -> sub(sub);
- case ToFloatNode tfn -> i2f8(tfn);
+ case PhiNode phi -> new PhiNode(phi);
+ case ProjNode prj -> prj(prj);
+ case ReadOnlyNode read-> new ReadOnlyNode(read);
+ case ReturnNode ret -> new RetRISC(ret, ret.fun());
+ case SarNode sar -> sra(sar);
+ case ShlNode shl -> sll(shl);
+ case ShrNode shr -> srl(shr);
+ case StartNode start -> new StartNode(start);
+ case StopNode stop -> new StopNode(stop);
+ case StoreNode st -> st(st);
+ case SubFNode subf -> new SubFRISC(subf);
+ case SubNode sub -> sub(sub);
+ case ToFloatNode tfn -> i2f8(tfn);
case ToIntegerNode cvt -> new F8ToIRISC(cvt);
- case XorNode xor -> xor(xor);
+ case XorNode xor -> xor(xor);
case LoopNode loop -> new LoopNode(loop);
case RegionNode region -> new RegionNode(region);
@@ -235,36 +253,81 @@ public class riscv extends Machine {
}
private Node add(AddNode add) {
- if( add.in(2) instanceof ConstantNode off && off._con instanceof TypeInteger ti && imm12(ti) )
- return new AddIRISC(add, (int)ti.value());
+ if( add.in(2) instanceof ConstantNode off2 && off2._con instanceof TypeInteger ti && imm12(ti) )
+ return new AddIRISC(add, (int)ti.value(),true);
return new AddRISC(add);
}
private Node and(AndNode and) {
- if( and.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti && imm12(ti) )
- return new AndIRISC(and, (int)ti.value());
+ if( and.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti ) {
+ if( imm12(ti) )
+ return new AndIRISC(and, (int)ti.value());
+ // Could be any size low bit mask
+ if( ti.value() == 0xFFFFFFFFL )
+ return new SrlIRISC(new SllIRISC(and,32),32,false);
+ }
return new AndRISC(and);
}
private Node call(CallNode call) {
- if( call.fptr() instanceof ConstantNode con && con._con instanceof TypeFunPtr tfp )
- return new CallRISC(call, tfp);
- return new CallRRISC(call);
+ return call.fptr() instanceof ConstantNode con && con._con instanceof TypeFunPtr tfp
+ ? new CallRISC(call, tfp)
+ : new CallRRISC(call);
+ }
+ private Node nnn(NewNode nnn) {
+ // TODO: pass in the TFP for alloc
+ return new NewRISC(nnn);
}
private Node cmp(BoolNode bool) {
- if( bool.in(2) instanceof ConstantNode off && off._con instanceof TypeInteger toff )
- return new SetIRISC(bool, (int)toff.value());
- // Float variant
+ // Float variant directly implemented in hardware
if( bool.isFloat() )
return new SetFRISC(bool);
- return new SetRISC(bool);
+
+ // Only < and <u are implemented in hardware.
+ // x < y - as-is
+ // x <= y - flip and negate; !(y < x); `slt tmp=y,x; xori dst=tmp,#1`
+ // x == y - sub and vs0 == `sub tmp=x-y; sltu dst=tmp,#1`
+
+ // x > y - swap; y < x
+ // x >= y - swap and negate; !(x < y); `slt tmp=y,x;` then NOT.
+ // x != y - sub and vs0 == `sub tmp=x-y; sltu dst=tmp,#1` then NOT.
+
+ // The ">", ">=" and "!=" in Simple include a NotNode, which can be
+ // implemented with a XOR. If one of the above is followed by a NOT
+ // we can remove the double XOR in the encodings.
+
+ return switch( bool.op() ) {
+ case "<" -> bool.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti && imm12(ti)
+ ? new SetIRISC(bool, (int)ti.value(),false)
+ : new SetRISC(bool, false);
+ // x <= y - flip and negate; !(y < x); `slt tmp=y,x; xori dst=tmp,#1`
+ case "<=" -> new XorIRISC(new SetRISC(bool.swap12(), false),1);
+ // x == y - sub and vs0 == `sub tmp=x-y; sltu dst=tmp,#1`
+ case "==" -> new SetIRISC(new SubRISC(bool),1,true,false);
+ default -> throw Utils.TODO();
+ };
}
private Node con( ConstantNode con ) {
if( !con._con.isConstant() ) return new ConstantNode( con ); // Default unknown caller inputs
return switch( con._con ) {
- case TypeInteger ti -> new IntRISC(con);
+ case TypeInteger ti -> {
+ if( imm12(ti) ) yield new IntRISC(con);
+ long x = ti.value();
+ if( imm20Exact(ti) ) yield new LUI((int)x);
+ if( (x<<32)>>32 == x ) { // Signed lower 32-bit immediate
+ // Here, the low 12 bits get sign-extended, which means if
+ // bit11 is set, the value is negative and lowers the LUI
+ // value. Add a bit 12 to compensate
+ if( ((x>>11)&1)==1 ) x += 0x1000;
+ yield new AddIRISC(new LUI((int)(x & ~0xFFF)), (int)(x & 0xFFF),false);
+ }
+ // Need more complex sequence for larger constants... or a load
+ // from a constant pool, which does not need an extra register
+ throw Utils.TODO();
+ }
+ // Load from constant pool
case TypeFloat tf -> new FltRISC(con);
case TypeFunPtr tfp -> new TFPRISC(con);
case TypeMemPtr tmp -> throw Utils.TODO();
@@ -275,45 +338,50 @@ public class riscv extends Machine {
}
private Node jmp( IfNode iff ) {
- if( iff.in(1) instanceof BoolNode bool && !bool.isFloat() )
- return new BranchRISC(iff, bool.op(), bool.in(1), bool.in(2));
+ if( iff.in(1) instanceof BoolNode bool && !bool.isFloat() ) {
+ // if less than or equal switch inputs
+ String bop = bool.op();
+ if( bop.equals("<=") || bop.equals(">") )
+ return new BranchRISC(iff, IfNode.swap(bop), bool.in(2), bool.in(1));
+ return new BranchRISC(iff, bop, bool.in(1), bool.in(2));
+ }
// Vs zero
- return new BranchRISC(iff, "==", iff.in(1), null);
+ return new BranchRISC(iff, iff.in(1)==null ? "==" : "!=", iff.in(1), null);
}
private Node or(OrNode or) {
- if( or.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti && imm12(ti) )
+ if( or.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti && imm12(ti))
return new OrIRISC(or, (int)ti.value());
return new OrRISC(or);
}
private Node xor(XorNode xor) {
- if( xor.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti && imm12(ti) )
+ if( xor.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti && imm12(ti))
return new XorIRISC(xor, (int)ti.value());
return new XorRISC(xor);
}
private Node sra(SarNode sar) {
- if( sar.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti && imm12(ti) )
+ if( sar.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti && imm12(ti))
return new SraIRISC(sar, (int)ti.value());
return new SraRISC(sar);
}
private Node srl(ShrNode shr) {
- if( shr.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti && imm12(ti) )
- return new SrlIRISC(shr, (int)ti.value());
+ if( shr.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti && imm12(ti))
+ return new SrlIRISC(shr, (int)ti.value(),true);
return new SrlRISC(shr);
}
private Node sll(ShlNode sll) {
- if( sll.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti && imm12(ti) )
+ if( sll.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti && imm12(ti))
return new SllIRISC(sll, (int)ti.value());
return new SllRISC(sll);
}
private Node sub(SubNode sub) {
return sub.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti && imm12(ti)
- ? new AddIRISC(sub, (int)(-ti.value()))
+ ? new AddIRISC(sub, (int)(-ti.value()),true)
: new SubRISC(sub);
}
@@ -327,28 +395,20 @@ public class riscv extends Machine {
}
private Node ld(LoadNode ld) {
- return new LoadRISC(address(ld),ld.ptr(),idx,off);
+ return new LoadRISC(ld,address(ld),off);
}
private Node st(StoreNode 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);
+ Node xval = st.val() instanceof ConstantNode con && con._con == TypeInteger.ZERO ? null : st.val();
+ return new StoreRISC(st,address(st), off, xval);
}
// Gather addressing mode bits prior to constructing. This is a builder
// pattern, but saving the bits in a *local* *global* here to keep mess
// contained.
private static int off;
- private static Node idx;
- private <N extends MemOpNode> N address( N mop ) {
+ private Node address( MemOpNode 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);
@@ -356,9 +416,10 @@ public class riscv extends Machine {
if( mop.off() instanceof ConstantNode con && con._con instanceof TypeInteger ti && imm12(ti) ) {
off = (int)ti.value();
} else {
- idx = mop.off();
+ base = new AddRISC(base,mop.off());
+ base._type = mop.ptr()._type;
}
- return mop;
+ return base;
}
}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddFMemX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddFMemX86.java
index ffc6966e..76a2d3f7 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,16 +1,16 @@
package com.seaofnodes.simple.node.cpus.x86_64_v2;
+import com.seaofnodes.isa.X86;
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.codegen.*;
import com.seaofnodes.simple.node.*;
public class AddFMemX86 extends MemOpX86 {
AddFMemX86( AddFNode add, LoadNode ld , Node base, Node idx, int off, int scale, Node val ) {
super(add,ld, base, idx, off, scale, 0, val );
}
-
+ @Override public String op() { return "addf"+_sz; }
@Override public RegMask regmap(int i) {
if( i==1 ) return null; // Memory
if( i==2 ) return x86_64_v2.RMASK; // base
@@ -18,10 +18,11 @@ public class AddFMemX86 extends MemOpX86 {
if( i==4 ) return x86_64_v2.XMASK; // value
throw Utils.TODO();
}
-
- // Register mask allowed as a result. 0 for no register.
@Override public RegMask outregmap() { return x86_64_v2.XMASK; }
-
+ @Override public int twoAddress() { return 4; }
+ @Override public void encoding( Encoding enc ) {
+ X86.memory(enc,0xF2,0x0F58,false,enc.reg(this)-x86_64_v2.XMM_OFFSET,enc.reg(ptr()),enc.reg(idx()),_off,_scale);
+ }
// General form: "add dst = src + [base + idx<<2 + 12]"
@Override public void asm(CodeGen code, SB sb) {
@@ -29,5 +30,4 @@ public class AddFMemX86 extends MemOpX86 {
sb.p(val()==null ? "#"+_imm : code.reg(val())).p(" + ");
asm_address(code,sb);
}
- @Override public String op() { return "addf"+_sz; }
}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddFX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddFX86.java
index d4ea9162..b3266cb0 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,33 +1,24 @@
package com.seaofnodes.simple.node.cpus.x86_64_v2;
+import com.seaofnodes.isa.X86;
import com.seaofnodes.simple.*;
-import com.seaofnodes.simple.codegen.CodeGen;
-import com.seaofnodes.simple.codegen.RegMask;
+import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.*;
-import com.seaofnodes.simple.type.TypeInteger;
-import java.io.ByteArrayOutputStream;
public class AddFX86 extends MachConcreteNode implements MachNode {
AddFX86( Node addf ) { super(addf); }
-
- // Register mask allowed on input i.
+ @Override public String op() { return "addf"; }
@Override public RegMask regmap(int i) { assert i==1 || i==2; return x86_64_v2.XMASK; }
- // Register mask allowed as a result. 0 for no register.
@Override public RegMask outregmap() { return x86_64_v2.XMASK; }
- // Output is same register as input#1
@Override public int twoAddress() { return 1; }
- // 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) {
- throw Utils.TODO();
+ @Override public void encoding( Encoding enc ) {
+ X86.sse(enc,0xF2,0x0F58,false,enc.reg(this)-x86_64_v2.XMM_OFFSET,enc.reg(in(2))-x86_64_v2.XMM_OFFSET);
}
// General form: "addf dst += src"
@Override public void asm(CodeGen code, SB sb) {
- sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" + ").p(code.reg(in(2)));
+ sb.p(code.reg(this)).p(" += ").p(code.reg(in(2)));
}
-
- @Override public String op() { return "addf"; }
}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddIX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddIX86.java
index c7f92ff1..569be754 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,41 +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 AddIX86 extends MachConcreteNode implements MachNode {
- final TypeInteger _ti;
- AddIX86( Node add, TypeInteger ti ) {
- super(add);
- _inputs.pop();
- _ti = ti;
- }
-
- // Register mask allowed on input i.
- @Override public RegMask regmap(int i) {
- // assert i== i;
- return x86_64_v2.WMASK; }
- // Register mask allowed as a result. 0 for no register.
- @Override public RegMask outregmap() { return x86_64_v2.WMASK; }
- // Output is same register as input#1
- @Override public int twoAddress() { return 1; }
-
- // Encoding is appended into the byte array; size is returned
- @Override public int encoding(ByteArrayOutputStream bytes) {
- throw Utils.TODO();
- }
-
- // General form: "addi dst += #imm"
- @Override public void asm(CodeGen code, SB sb) {
- sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" + #");
- _ti.print(sb);
- }
+import com.seaofnodes.simple.node.Node;
+public class AddIX86 extends ImmX86 {
+ AddIX86( Node add, int imm ) { super(add,imm); }
@Override public String op() {
- return _ti.value() == 1 ? "inc" : (_ti.value() == -1 ? "dec" : "addi");
+ return _imm == 1 ? "inc" : (_imm == -1 ? "dec" : "addi");
}
+ @Override public String glabel() { return "+"; }
+ @Override int opcode() { return 0x81; }
+ @Override int mod() { return 0; }
}
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 3d4fac41..d2111d07 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,26 +1,24 @@
package com.seaofnodes.simple.node.cpus.x86_64_v2;
+import com.seaofnodes.isa.X86;
import com.seaofnodes.simple.SB;
-import com.seaofnodes.simple.codegen.CodeGen;
-import com.seaofnodes.simple.codegen.RegMask;
+import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.*;
public class AddMemX86 extends MemOpX86 {
AddMemX86( AddNode add, LoadNode ld , Node base, Node idx, int off, int scale, int imm, Node val ) {
super(add,ld, base, idx, off, scale, imm, val );
}
-
@Override public String op() { return "add"+_sz; }
-
- // Register mask allowed as a result. 0 for no register.
@Override public RegMask outregmap() { return x86_64_v2.WMASK; }
- // Output is same register as val()
@Override public int twoAddress() { return 4; }
+ @Override public void encoding( Encoding enc ) {
+ X86.memory(enc,0,0x03,true,enc.reg(this),enc.reg(ptr()),enc.reg(idx()),_off,_scale);
+ }
- // General form: "add dst = src + [base + idx<<2 + 12]"
+ // General form: "add dst += [base + idx<<2 + 12]"
@Override public void asm(CodeGen code, SB sb) {
- sb.p(code.reg(this)).p(" = ");
- sb.p(val()==null ? "#"+_imm : code.reg(val())).p(" + ");
+ sb.p(code.reg(this)).p(" += ");
asm_address(code,sb);
}
}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddX86.java
index 9553d499..84543e8f 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,33 +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.*;
-import com.seaofnodes.simple.type.TypeInteger;
-import java.io.ByteArrayOutputStream;
+import com.seaofnodes.simple.node.Node;
-public class AddX86 extends MachConcreteNode {
+public class AddX86 extends RegX86 {
AddX86( Node add ) { super(add); }
-
- // Register mask allowed on input i.
- @Override public RegMask regmap(int i) { assert i==1 || i==2; return x86_64_v2.RMASK; }
- // Register mask allowed as a result. 0 for no register.
- @Override public RegMask outregmap() { return x86_64_v2.WMASK; }
- // Output is same register as input#1
- @Override public int twoAddress() { return 1; }
- // 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) {
- throw Utils.TODO();
- }
-
- // General form: "add dst += src"
- @Override public void asm(CodeGen code, SB sb) {
- sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" + ").p(code.reg(in(2)));
- }
-
@Override public String op() { return "add"; }
+ @Override public String glabel() { return "+"; }
+ @Override int opcode() { return 0x03; }
+ @Override public boolean commutes() { return true; }
}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AndIX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AndIX86.java
index a7f1801e..3c1b567c 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,44 +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.*;
-import com.seaofnodes.simple.type.Type;
-import com.seaofnodes.simple.type.TypeInteger;
-import java.io.ByteArrayOutputStream;
-import java.util.BitSet;
-import java.lang.StringBuilder;
-
-public class AndIX86 extends MachConcreteNode implements MachNode {
- final TypeInteger _ti;
- AndIX86(Node and, TypeInteger ti) {
- super(and);
- _inputs.pop();
- _ti = ti;
- }
-
- // Register mask allowed on input i.
- // This is the normal calling convention
- @Override public RegMask regmap(int i) { assert i==1; return x86_64_v2.WMASK; }
- // Register mask allowed as a result. 0 for no register.
- @Override public RegMask outregmap() { return x86_64_v2.WMASK; }
-
-
- // Output is same register as input#1
- @Override public int twoAddress() { return 1; }
-
- // Encoding is appended into the byte array; size is returned
- @Override public int encoding(ByteArrayOutputStream bytes) {
- throw Utils.TODO();
- }
-
- // General form
- // General form: "andi dst & #imm"
- @Override public void asm(CodeGen code, SB sb) {
- sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" & #");
- _ti.print(sb);
- }
+import com.seaofnodes.simple.node.Node;
+public class AndIX86 extends ImmX86 {
+ AndIX86( Node add, int imm ) { super(add,imm); }
@Override public String op() { return "andi"; }
+ @Override public String glabel() { return "&"; }
+ @Override int opcode() { return 0x81; }
+ @Override int mod() { return 4; }
}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AndX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AndX86.java
index d7fb3613..dedbce45 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,34 +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.*;
-import java.io.ByteArrayOutputStream;
-
-public class AndX86 extends MachConcreteNode implements MachNode{
- AndX86(Node and) {
- super(and);
- }
- // Register mask allowed on input i.
- // This is the normal calling convention
- @Override public RegMask regmap(int i) { assert i==1; return x86_64_v2.WMASK; }
-
- @Override public RegMask outregmap() { return x86_64_v2.WMASK; }
-
- // Output is same register as input#1
- @Override public int twoAddress() { return 1; }
-
- // Encoding is appended into the byte array; size is returned
- @Override public int encoding(ByteArrayOutputStream bytes) {
- throw Utils.TODO();
- }
-
- // General form
- // General form: "and dst & 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)));;
- }
+import com.seaofnodes.simple.node.Node;
+public class AndX86 extends RegX86 {
+ AndX86( Node add ) { super(add); }
@Override public String op() { return "and"; }
+ @Override public String glabel() { return "&"; }
+ @Override int opcode() { return 0x23; }
+ @Override public boolean commutes() { return true; }
}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CallEndX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CallEndX86.java
index b9cc5659..ce8b993f 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
@@ -1,35 +1,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.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 CallEndX86 extends CallEndNode implements MachNode {
- final TypeFunPtr _tfp;
- CallEndX86( CallEndNode cend ) {
- super(cend);
- _tfp = (TypeFunPtr)(cend.call().fptr()._type);
- }
-
- @Override public String label() { return op(); }
- @Override public RegMask regmap(int i) { return null; }
- @Override public RegMask outregmap() { 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) {
- throw Utils.TODO();
- }
-
- @Override public void asm(CodeGen code, SB sb) { }
-
- @Override public String op() { return "cend"; }
-
+ CallEndX86( CallEndNode cend ) { super(cend); }
}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CallRX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CallRX86.java
index ac35d17b..15b83bfc 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,32 +1,23 @@
package com.seaofnodes.simple.node.cpus.x86_64_v2;
+import com.seaofnodes.isa.X86;
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.codegen.*;
import com.seaofnodes.simple.node.CallNode;
import com.seaofnodes.simple.node.MachNode;
-import java.io.ByteArrayOutputStream;
public class CallRX86 extends CallNode implements MachNode {
CallRX86( CallNode call ) { super(call); }
-
+ @Override public String op() { return "callr"; }
@Override public String label() { return op(); }
@Override public RegMask regmap(int i) {
return i==_inputs._len
? x86_64_v2.WMASK // Function call target
- : x86_64_v2.callInMask(tfp(),i); // Normal argument
+ : x86_64_v2.callInMask(tfp(),i,fun()._maxArgSlot); // Normal argument
}
@Override public RegMask outregmap() { return null; }
-
- // Encoding is appended into the byte array; size is returned
- @Override public int encoding(ByteArrayOutputStream bytes) {
- bytes.write(0xe8);
- int beforeSize = bytes.size();
- // address
- //TODO: relocs
- bytes.write(0x00);
- return bytes.size() - beforeSize;
+ @Override public void encoding( Encoding enc ) {
+ X86.callRegister(enc,enc.reg(fptr()));
}
@Override public void asm(CodeGen code, SB sb) {
@@ -36,5 +27,4 @@ public class CallRX86 extends CallNode implements MachNode {
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 e2f68c1f..c97037e2 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,15 +1,12 @@
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.RegMask;
+import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.CallNode;
import com.seaofnodes.simple.node.MachNode;
import com.seaofnodes.simple.type.TypeFunPtr;
-import java.io.ByteArrayOutputStream;
-public class CallX86 extends CallNode implements MachNode {
+public class CallX86 extends CallNode implements MachNode, RIPRelSize {
final TypeFunPtr _tfp;
final String _name;
CallX86( CallNode call, TypeFunPtr tfp ) {
@@ -19,26 +16,24 @@ public class CallX86 extends CallNode implements MachNode {
_tfp = tfp;
_name = CodeGen.CODE.link(tfp)._name;
}
-
+ @Override public String op() { return "call"; }
@Override public String label() { return op(); }
- @Override public RegMask regmap(int i) {
- return x86_64_v2.callInMask(_tfp,i); // Normal argument
- }
+ @Override public String name() { return _name; }
+ @Override public TypeFunPtr tfp() { return _tfp; }
+ @Override public RegMask regmap(int i) { return x86_64_v2.callInMask(_tfp,i,fun()._maxArgSlot); }
@Override public RegMask outregmap() { return null; }
@Override public int nargs() { return nIns()-2; } // Minus control, memory, fptr
- @Override public String name() { return _name; }
- @Override public TypeFunPtr tfp() { return _tfp; }
+ @Override public void encoding( Encoding enc ) {
+ enc.relo(this).add1(0xe8).add4(0);
+ }
+
+ // Delta is from opcode start, but X86 measures from the end of the 5-byte encoding
+ @Override public byte encSize(int delta) { return 5; }
- // Encoding is appended into the byte array; size is returned
- @Override public int encoding(ByteArrayOutputStream bytes) {
- // linker will fix this up
- bytes.write(0xe8);
- int beforeSize = bytes.size();
- // TODO: relocs
- // address
- bytes.write(0x00);
- return bytes.size() - beforeSize;
+ // Delta is from opcode start
+ @Override public void patch( Encoding enc, int opStart, int opLen, int delta ) {
+ enc.patch4(opStart+1, delta-5);
}
@Override public void asm(CodeGen code, SB sb) {
@@ -47,7 +42,4 @@ public class CallX86 extends CallNode implements MachNode {
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
index b620b89c..9537cfb3 100644
--- 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
@@ -1,18 +1,16 @@
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.codegen.*;
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 String op() { return label(); }
@Override public RegMask regmap(int i) { assert i==1; return RegMask.FULL; }
@Override public RegMask outregmap() { return RegMask.FULL; }
@Override public int twoAddress( ) { return 1; }
- @Override public int encoding(ByteArrayOutputStream bytes) { return 0; /*no encoding*/ }
+ @Override public void encoding( Encoding enc ) { }
@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
index e9b0dacd..5a0c7a37 100644
--- 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
@@ -1,21 +1,18 @@
package com.seaofnodes.simple.node.cpus.x86_64_v2;
+import com.seaofnodes.isa.X86;
import com.seaofnodes.simple.*;
-import com.seaofnodes.simple.codegen.CodeGen;
-import com.seaofnodes.simple.codegen.RegMask;
+import com.seaofnodes.simple.codegen.*;
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 String op() { return "cmpf"; }
+ @Override public RegMask regmap(int i) { return x86_64_v2.XMASK; }
@Override public RegMask outregmap() { return x86_64_v2.FLAGS_MASK; }
- // Encoding is appended into the byte array; size is returned
- @Override public int encoding(ByteArrayOutputStream bytes) {
- throw Utils.TODO();
+ @Override public void encoding( Encoding enc ) {
+ X86.sse(enc,0x66,0x0F2E,false,enc.reg(in(1))-x86_64_v2.XMM_OFFSET,enc.reg(in(2))-x86_64_v2.XMM_OFFSET);
}
// General form: "cmp src1,src2"
@@ -24,6 +21,4 @@ public class CmpFX86 extends MachConcreteNode implements MachNode {
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 fcba397d..2811a640 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,53 +1,38 @@
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.isa.X86;
+import com.seaofnodes.simple.SB;
+import com.seaofnodes.simple.codegen.*;
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.
public class CmpIX86 extends MachConcreteNode implements MachNode {
final int _imm;
- final String _bop;
- CmpIX86( BoolNode bool, TypeInteger ti ) {
- super(bool);
- _inputs.pop(); // Toss ideal ConstantNode away, embedded into machine op
- _bop = bool.op(); // One of <,<=,==
- _imm = (int)ti.value();
- assert _imm == ti.value();
+ CmpIX86( Node cmp, int imm, boolean swap ) {
+ super(cmp);
+ _inputs.del(swap ? 1 : 2);
+ _imm = imm;
}
- // 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 = cmp._bop;
+ super((Node)null);
+ addDef(null);
+ addDef(cmp.in(1));
_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 String op() { return _imm==0 ? "test" : "cmp"; }
+ @Override public RegMask regmap(int i) { return x86_64_v2.RMASK; }
@Override public RegMask outregmap() { return x86_64_v2.FLAGS_MASK; }
@Override public boolean isClone() { return true; }
@Override public Node copy() { return new CmpIX86(this); }
- // Encoding is appended into the byte array; size is returned
- @Override public int encoding(ByteArrayOutputStream bytes) {
- throw Utils.TODO();
+ @Override public final void encoding( Encoding enc ) {
+ X86.imm(enc,0x81,7,enc.reg(in(1)),_imm);
}
-
@Override public void asm(CodeGen code, SB sb) {
String dst = code.reg(this);
if( dst!="flags" ) sb.p(dst).p(" = ");
sb.p(code.reg(in(1)));
if( _imm != 0 ) sb.p(", #").p(_imm);
}
-
- @Override public String op() { return _imm==0 ? "test" : "cmp"; }
-
}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CmpMemX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CmpMemX86.java
index f9ace702..bf8f1631 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,33 +1,44 @@
package com.seaofnodes.simple.node.cpus.x86_64_v2;
+import com.seaofnodes.isa.X86;
import com.seaofnodes.simple.SB;
-import com.seaofnodes.simple.codegen.CodeGen;
-import com.seaofnodes.simple.codegen.RegMask;
+import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.type.Type;
+import com.seaofnodes.simple.type.TypeFloat;
public class CmpMemX86 extends MemOpX86 {
- final boolean _invert; // Op switched LHS, RHS
- CmpMemX86( BoolNode bool, LoadNode ld , Node base, Node idx, int off, int scale, int imm, Node val, boolean invert ) {
+ final boolean _swap; // Op switched LHS, RHS
+ CmpMemX86( BoolNode bool, LoadNode ld, Node base, Node idx, int off, int scale, int imm, Node val, boolean swap ) {
super(bool,ld, base, idx, off, scale, imm, val );
- _invert = invert;
+ _swap = swap;
+ assert !_swap || val() == null || bool.op()=="==" || bool.op()=="!="; // Cannot swap with immediate
}
-
- // Register mask allowed as a result. 0 for no register.
+ @Override public String op() { return ((val()==null && _imm==0) ? "test" : "cmp") + _sz; }
@Override public RegMask outregmap() { return x86_64_v2.FLAGS_MASK; }
+ @Override public void encoding( Encoding enc ) {
+ short ptr=enc.reg(ptr()), idx=enc.reg(idx()), src=enc.reg(val());
+ if( src==-1 ) {
+ assert !_swap;
+ X86.cmpImm(enc,_declaredType.log_size(),_imm,ptr,idx,_off,_scale);
+ } else encVal(enc,_declaredType,ptr,idx,src,_off,_scale,_swap);
+ }
+
+ static void encVal(Encoding enc, Type decl, short ptr, short idx, short src, int off, int scale, boolean _swap) {
+ assert !(decl instanceof TypeFloat); // Selected separately as CmpFX86.
+ X86.cmpMem(enc,decl.log_size(),src,ptr,idx,off,scale,_swap);
+ }
// General form: "cmp dst = src, [base + idx<<2 + 12]"
@Override public void asm(CodeGen code, SB sb) {
String dst = code.reg(this);
- if( dst!="FLAGS" ) sb.p(dst).p(" = ");
- if( _invert ) asm_address(code,sb).p(",");
+ if( dst!="flags" ) sb.p(dst).p(" = ");
+ if( _swap ) asm_address(code,sb).p(",");
if( val()==null ) {
if( _imm!=0 ) sb.p("#").p(_imm);
} else {
sb.p(code.reg(val()));
}
- if( !_invert ) asm_address(code,sb.p(", "));
+ if( !_swap ) asm_address(code,sb.p(", "));
}
-
- @Override public String op() { return ((val()==null && _imm==0) ? "test" : "cmp") + _sz; }
-
}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CmpX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CmpX86.java
index 41ce33d2..e3cbe75f 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,29 +1,22 @@
package com.seaofnodes.simple.node.cpus.x86_64_v2;
+import com.seaofnodes.isa.X86;
import com.seaofnodes.simple.*;
-import com.seaofnodes.simple.codegen.CodeGen;
-import com.seaofnodes.simple.codegen.RegMask;
+import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.*;
-import com.seaofnodes.simple.type.TypeInteger;
-import java.io.ByteArrayOutputStream;
public class CmpX86 extends MachConcreteNode implements MachNode {
- CmpX86( Node cmp ) { super(cmp); }
-
- @Override public RegMask regmap(int i) { assert i==1 || i==2; return x86_64_v2.RMASK; }
+ CmpX86( Node add ) { super(add); }
+ @Override public String op() { return "cmp"; }
+ @Override public RegMask regmap(int i) { return x86_64_v2.RMASK; }
@Override public RegMask outregmap() { return x86_64_v2.FLAGS_MASK; }
- // Encoding is appended into the byte array; size is returned
- @Override public int encoding(ByteArrayOutputStream bytes) {
- throw Utils.TODO();
+ @Override public void encoding( Encoding enc ) {
+ X86.reg(enc,0x3B,enc.reg(in(1)),enc.reg(in(2)));
}
-
- // 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(" = ");
+ if( dst!="flags" ) sb.p(dst).p(" = ");
sb.p(code.reg(in(1))).p(", ").p(code.reg(in(2)));
}
-
- @Override public String op() { return "cmp"; }
}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/DivFX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/DivFX86.java
index f74b7348..962ec44e 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,35 +1,24 @@
package com.seaofnodes.simple.node.cpus.x86_64_v2;
+import com.seaofnodes.isa.X86;
import com.seaofnodes.simple.*;
-import com.seaofnodes.simple.codegen.CodeGen;
-import com.seaofnodes.simple.codegen.RegMask;
+import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.*;
-import com.seaofnodes.simple.type.TypeInteger;
-import java.io.ByteArrayOutputStream;
public class DivFX86 extends MachConcreteNode implements MachNode {
DivFX86( Node divf) { super(divf); }
-
- // Register mask allowed on input i.
- @Override public RegMask regmap(int i) { assert i==1 || i==2; return x86_64_v2.XMASK; }
- // Register mask allowed as a result. 0 for no register.
- @Override public RegMask outregmap() { return x86_64_v2.XMASK; }
- // Output is same register as input#1
+ @Override public String op() { return "divf"; }
+ @Override public RegMask regmap(int i) { return x86_64_v2.XMASK; }
+ @Override public RegMask outregmap() { return x86_64_v2.XMASK; }
@Override public int twoAddress() { return 1; }
- // Encoding is appended into the byte array; size is returned
- @Override public int encoding(ByteArrayOutputStream bytes) {
- throw Utils.TODO();
+ @Override public void encoding( Encoding enc ) {
+ X86.sse(enc,0xF2,0x0F5E,false,enc.reg(this)-x86_64_v2.XMM_OFFSET,enc.reg(in(2))-x86_64_v2.XMM_OFFSET);
}
// General form: "divf dst /= src"
@Override public void asm(CodeGen code, SB sb) {
- sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" / ").p(code.reg(in(2)));
- }
-
- @Override public String op() {
- // 8 bytes = 64 bits
- return "divf";
+ sb.p(code.reg(this)).p(" /= ").p(code.reg(in(2)));
}
}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/DivX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/DivX86.java
index 54372629..e61f576c 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,35 +1,25 @@
package com.seaofnodes.simple.node.cpus.x86_64_v2;
+import com.seaofnodes.isa.X86;
import com.seaofnodes.simple.*;
-import com.seaofnodes.simple.codegen.CodeGen;
-import com.seaofnodes.simple.codegen.RegMask;
+import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.*;
-import com.seaofnodes.simple.type.TypeInteger;
-import java.io.ByteArrayOutputStream;
public class DivX86 extends MachConcreteNode implements MachNode {
DivX86( Node div ) { super(div); }
-
- // Register mask allowed on input i.
+ @Override public String op() { return "div"; }
@Override public RegMask regmap(int i) {
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.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) {
- throw Utils.TODO();
+ @Override public void encoding( Encoding enc ) {
+ X86.divide(enc,enc.reg(in(2)));
}
-
// 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"; }
@Override public String comment() { return "kill rdx"; }
}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/F8ToIX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/F8ToIX86.java
index 49d666b7..44bcbe7b 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/F8ToIX86.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/F8ToIX86.java
@@ -1,30 +1,22 @@
package com.seaofnodes.simple.node.cpus.x86_64_v2;
+import com.seaofnodes.isa.X86;
import com.seaofnodes.simple.*;
-import com.seaofnodes.simple.codegen.CodeGen;
-import com.seaofnodes.simple.codegen.RegMask;
+import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.*;
-import com.seaofnodes.simple.type.TypeInteger;
-import java.io.ByteArrayOutputStream;
public class F8ToIX86 extends MachConcreteNode implements MachNode {
F8ToIX86(Node f8toi ) { super(f8toi); }
-
- // Register mask allowed on input i.
+ @Override public String op() { return "cvti"; }
@Override public RegMask regmap(int i) { assert i==1; return x86_64_v2.XMASK; }
- // Register mask allowed as a result. 0 for no register.
@Override public RegMask killmap() { return x86_64_v2.FLAGS_MASK; }
@Override public RegMask outregmap() { return x86_64_v2.WMASK; }
- // Encoding is appended into the byte array; size is returned
- @Override public int encoding(ByteArrayOutputStream bytes) {
- throw Utils.TODO();
+ @Override public void encoding( Encoding enc ) {
+ X86.floatToInteger(enc,enc.reg(this),enc.reg(in(1))-x86_64_v2.XMM_OFFSET);
}
-
@Override public void asm(CodeGen code, SB sb) {
sb.p(code.reg(this)).p(" = ").p("(int)").p(code.reg(in(1)));
}
-
- @Override public String op() { return "f8toi"; }
}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/FltX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/FltX86.java
index 47f7149d..b17ea305 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,35 +1,36 @@
package com.seaofnodes.simple.node.cpus.x86_64_v2;
+import com.seaofnodes.isa.X86;
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;
+import com.seaofnodes.simple.codegen.*;
+import com.seaofnodes.simple.node.*;
-public class FltX86 extends ConstantNode implements MachNode {
+public class FltX86 extends ConstantNode implements MachNode, RIPRelSize{
+ private byte _encSize;
FltX86(ConstantNode con ) { super(con); }
- @Override public String op() { return "fld"; }
-
- // Register mask allowed on input i. 0 for no register.
+ @Override public String op() { return "ld8"; }
@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); }
+ @Override public Node copy() { return new FltX86(this); }
- // Encoding is appended into the byte array; size is returned
- @Override public int encoding(ByteArrayOutputStream bytes) {
- throw Utils.TODO();
+ @Override public void encoding( Encoding enc ) {
+ _encSize=X86.rip(enc,0xF2,0x0F10,false,enc.reg(this)-x86_64_v2.XMM_OFFSET);
+ enc.largeConstant(this,_con,_encSize-4,2/*ELF PC32*/);
}
- // 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(" #"));
+ // Delta is from opcode start
+ @Override public byte encSize(int delta) {
+ // TODO: 1-byte RIP offset?
+ return (byte)_encSize;
+ }
+
+ // Delta is from opcode start
+ @Override public void patch( Encoding enc, int opStart, int opLen, int delta ) {
+ enc.patch4(opStart+opLen-4,delta-opLen);
}
+ @Override public void asm(CodeGen code, SB sb) {
+ _con.print(sb.p(code.reg(this)).p(" = #"));
+ }
}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/FunX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/FunX86.java
index 7346ae05..24c2a78b 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,22 +1,31 @@
package com.seaofnodes.simple.node.cpus.x86_64_v2;
+import com.seaofnodes.isa.X86;
+import com.seaofnodes.simple.SB;
import com.seaofnodes.simple.codegen.CodeGen;
-import com.seaofnodes.simple.codegen.RegMask;
-import com.seaofnodes.simple.Utils;
+import com.seaofnodes.simple.codegen.Encoding;
import com.seaofnodes.simple.node.FunNode;
import com.seaofnodes.simple.node.MachNode;
-import java.io.ByteArrayOutputStream;
public class FunX86 extends FunNode implements MachNode {
FunX86( FunNode fun ) { super(fun); }
+ @Override public void postSelect(CodeGen code) {
+ super.postSelect(code);
+ // One more for RPC pre-pushed; X86 special
+ _maxArgSlot++;
+ }
- // 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; }
+ @Override public void computeFrameAdjust(CodeGen code, int maxReg) {
+ super.computeFrameAdjust(code,maxReg);
+ // Alignment, but off by RPC
+ if( _hasCalls ) // If non-leaf, pad to 16b
+ _frameAdjust = ((_frameAdjust+8+8) & -16)-8;
+ }
- // Encoding is appended into the byte array; size is returned
- @Override public int encoding(ByteArrayOutputStream bytes) {
- return 0;
+ @Override public void encoding( Encoding enc ) {
+ int sz = _frameAdjust;
+ if( sz == 0 ) return; // Skip if no frame adjust
+ X86.imm(enc,0x81,5,x86_64_v2.RSP,sz);
}
+
}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/I2f8X86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/I2f8X86.java
index d99c92a8..4a17b92b 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,30 +1,21 @@
package com.seaofnodes.simple.node.cpus.x86_64_v2;
+import com.seaofnodes.isa.X86;
import com.seaofnodes.simple.*;
-import com.seaofnodes.simple.codegen.CodeGen;
-import com.seaofnodes.simple.codegen.RegMask;
+import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.*;
-import com.seaofnodes.simple.type.TypeInteger;
-import java.io.ByteArrayOutputStream;
public class I2f8X86 extends MachConcreteNode implements MachNode {
I2f8X86(Node i2f8 ) { super(i2f8); }
-
- // Register mask allowed on input i.
+ @Override public String op() { return "cvtf"; }
@Override public RegMask regmap(int i) { assert i==1; return x86_64_v2.WMASK; }
- // Register mask allowed as a result. 0 for no register.
@Override public RegMask outregmap() { return x86_64_v2.XMASK; }
- // Encoding is appended into the byte array; size is returned
- @Override public int encoding(ByteArrayOutputStream bytes) {
- throw Utils.TODO();
+ @Override public void encoding( Encoding enc ) {
+ X86.sse(enc,0xF2,0x0F2A,true,enc.reg(this)-x86_64_v2.XMM_OFFSET,enc.reg(in(1)));
}
- // General form: "i2f8 (flt)int_value"
-
@Override public void asm(CodeGen code, SB sb) {
sb.p(code.reg(this)).p(" = ").p("(flt)").p(code.reg(in(1)));
}
-
- @Override public String op() { return "i2f8"; }
}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ImmX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ImmX86.java
new file mode 100644
index 00000000..b87e7f47
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ImmX86.java
@@ -0,0 +1,31 @@
+package com.seaofnodes.simple.node.cpus.x86_64_v2;
+
+import com.seaofnodes.isa.X86;
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.codegen.*;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.type.TypeInteger;
+
+public abstract class ImmX86 extends MachConcreteNode implements MachNode {
+ final int _imm;
+ ImmX86( Node add, int imm ) {
+ super(add);
+ _inputs.pop(); // Pop constant input off
+ _imm = imm;
+ }
+ @Override public RegMask regmap(int i) { return x86_64_v2.RMASK; }
+ @Override public RegMask outregmap() { return x86_64_v2.WMASK; }
+ @Override public int twoAddress() { return 1; }
+
+ abstract int opcode();
+ abstract int mod();
+
+ @Override public final void encoding( Encoding enc ) {
+ X86.imm(enc,opcode(),mod(),enc.reg(this),_imm);
+ }
+
+ // General form: "addi dst += #imm"
+ @Override public void asm(CodeGen code, SB sb) {
+ sb.p(code.reg(this)).p(" ").p(glabel()).p("= #").p(_imm);
+ }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/IntX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/IntX86.java
index fcd39e3f..29513d98 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,40 +1,29 @@
package com.seaofnodes.simple.node.cpus.x86_64_v2;
+import com.seaofnodes.isa.X86;
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.codegen.*;
+import com.seaofnodes.simple.node.*;
import com.seaofnodes.simple.type.Type;
import com.seaofnodes.simple.type.TypeInteger;
-import java.io.ByteArrayOutputStream;
-
// Integer constants
public class IntX86 extends ConstantNode implements MachNode {
IntX86( ConstantNode con ) { super(con); }
-
@Override public String op() {
return _con == Type.NIL || _con == TypeInteger.ZERO ? "xor" : "ldi";
}
-
- // Register mask allowed on input i. 0 for no register.
+ @Override public boolean isClone() { return true; }
+ @Override public Node copy() { return new IntX86(this); }
@Override public RegMask regmap(int i) { return null; }
- // 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();
+ @Override public void encoding( Encoding enc ) {
+ X86.constant(enc,enc.reg(this),_con==Type.NIL ? 0 : ((TypeInteger)_con).value());
}
// Human-readable form appended to the SB. Things like the encoding,
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/JmpX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/JmpX86.java
index 173f9ee7..92156c58 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,48 +1,64 @@
package com.seaofnodes.simple.node.cpus.x86_64_v2;
+import com.seaofnodes.isa.X86;
import com.seaofnodes.simple.*;
-import com.seaofnodes.simple.codegen.CodeGen;
-import com.seaofnodes.simple.codegen.RegMask;
+import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.*;
-import com.seaofnodes.simple.type.Type;
-import com.seaofnodes.simple.type.TypeInteger;
-import java.io.ByteArrayOutputStream;
// Jump on flags, uses flags
-public class JmpX86 extends IfNode implements MachNode {
- final String _bop;
+public class JmpX86 extends IfNode implements MachNode, RIPRelSize {
+ String _bop;
JmpX86( IfNode iff, String bop ) {
super(iff);
_bop = bop;
}
-
+ @Override public String op() { return "j"+_bop; }
@Override public String label() { return op(); }
-
- @Override public void postSelect() {
+ @Override public void postSelect(CodeGen code) {
Node set = in(1);
+ if( set==null ) return; // Never-node cutout
Node cmp = set.in(1);
// Bypass an expected Set and just reference the cmp directly
- if( set instanceof SetX86 && (cmp instanceof CmpX86 || cmp instanceof CmpIX86 || cmp instanceof CmpMemX86 || cmp instanceof CmpFX86) )
- _inputs.set(1,cmp);
- else
+ if( set instanceof SetX86 setx && (cmp instanceof CmpX86 || cmp instanceof CmpIX86 || cmp instanceof CmpMemX86 || cmp instanceof CmpFX86) ) {
+ _inputs.set( 1, cmp );
+ _bop = setx._bop;
+ } else
throw Utils.TODO();
}
@Override public RegMask regmap(int i) { assert i==1; return x86_64_v2.FLAGS_MASK; }
@Override public RegMask outregmap() { return null; }
+ @Override public void negate() { _bop = negate(_bop); }
- // Encoding is appended into the byte array; size is returned
- @Override public int encoding(ByteArrayOutputStream bytes) {
- throw Utils.TODO();
+ @Override public void encoding( Encoding enc ) {
+ if( in(1)==null && _bop=="!=" ) return;
+ X86.branch(enc,in(1)==null ? -1 : x86_64_v2.jumpop(_bop)&15);
+ enc.jump(this,cproj(0));
}
- @Override public void asm(CodeGen code, SB sb) {
- String src = code.reg(in(1));
- if( src!="flags" ) sb.p(src);
+ // Delta is from opcode start, but X86 measures from the end of the 2-byte encoding
+ @Override public byte encSize(int delta) {
+ if( in(1)==null && _bop=="!=" ) return 0;
+ return X86.branchSize(delta,in(1)!=null);
}
- @Override public String op() { return "j"+_bop; }
+ // Delta is from opcode start
+ @Override public void patch( Encoding enc, int opStart, int opLen, int delta ) {
+ assert !(in(1)==null && _bop=="!=");
+ X86.patchBranch(enc.bits(),opStart,opLen,delta,in(1)==null ? -1 : x86_64_v2.jumpop(_bop)&15);
+ }
- @Override public String comment() {
- return "L"+cproj(1)._nid+", L"+cproj(0)._nid;
+ @Override public void asm(CodeGen code, SB sb) {
+ if( in(1)!=null ) { // Never-node
+ String src = code.reg(in(1));
+ if( src!="flags" ) sb.p(src).p(" ");
+ } else if( _bop=="!=" ) {
+ sb.p("never");
+ return;
+ }
+ CFGNode prj = cproj(0).uctrlSkipEmpty(); // 0 is True is jump target
+ if( !prj.blockHead() ) prj = prj.cfg0();
+ sb.p(label(prj));
}
+
+ @Override public String comment() { return "L"+cproj(1)._nid; }
}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/LeaX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/LeaX86.java
index 229c0151..e20fd1ec 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,18 +1,16 @@
package com.seaofnodes.simple.node.cpus.x86_64_v2;
+import com.seaofnodes.isa.X86;
import com.seaofnodes.simple.*;
-import com.seaofnodes.simple.codegen.CodeGen;
-import com.seaofnodes.simple.codegen.RegMask;
+import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.*;
-import com.seaofnodes.simple.type.TypeInteger;
-import java.io.ByteArrayOutputStream;
public class LeaX86 extends MachConcreteNode implements MachNode {
final int _scale;
- final long _offset;
- LeaX86( Node add, Node base, Node idx, int scale, long offset ) {
+ final int _offset;
+ LeaX86( Node add, Node base, Node idx, int scale, int offset ) {
super(add);
- assert scale==1 || scale==2 || scale==4 || scale==8;
+ assert scale==0 || scale==1 || scale==2 || scale==3;
_inputs.pop();
_inputs.pop();
_inputs.push(base);
@@ -21,14 +19,12 @@ public class LeaX86 extends MachConcreteNode implements MachNode {
_offset = offset;
}
- // Register mask allowed on input i.
+ @Override public String op() { return "lea"; }
@Override public RegMask regmap(int i) { assert i==1 || i==2; return x86_64_v2.RMASK; }
- // Register mask allowed as a result. 0 for no register.
@Override public RegMask outregmap() { return x86_64_v2.WMASK; }
- // Encoding is appended into the byte array; size is returned
- @Override public int encoding(ByteArrayOutputStream bytes) {
- throw Utils.TODO();
+ @Override public void encoding( Encoding enc ) {
+ X86.lea(enc,enc.reg(this),enc.reg(in(1)),enc.reg(in(2)),_offset,_scale);
}
// General form: "lea dst = base + 4*idx + 12"
@@ -36,9 +32,7 @@ public class LeaX86 extends MachConcreteNode implements MachNode {
sb.p(code.reg(this)).p(" = ");
if( in(1) != null )
sb.p(code.reg(in(1))).p(" + ");
- sb.p(code.reg(in(2))).p("*").p(_scale);
+ sb.p(code.reg(in(2))).p("<<").p(_scale);
if( _offset!=0 ) sb.p(" + #").p(_offset);
}
-
- @Override public String op() { return "lea"; }
}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/LoadX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/LoadX86.java
index 893772a6..c5329e1d 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,20 +1,42 @@
package com.seaofnodes.simple.node.cpus.x86_64_v2;
+import com.seaofnodes.isa.X86;
import com.seaofnodes.simple.SB;
-import com.seaofnodes.simple.codegen.CodeGen;
-import com.seaofnodes.simple.codegen.RegMask;
+import com.seaofnodes.simple.Utils;
+import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.LoadNode;
import com.seaofnodes.simple.node.Node;
+import com.seaofnodes.simple.type.Type;
+import com.seaofnodes.simple.type.TypeFloat;
+import com.seaofnodes.simple.type.TypeInteger;
+import com.seaofnodes.simple.type.TypeMemPtr;
public class LoadX86 extends MemOpX86 {
LoadX86( LoadNode ld, Node base, Node idx, int off, int scale ) {
super(ld,ld, base, idx, off, scale, 0);
}
-
@Override public String op() { return "ld"+_sz; }
-
- // Register mask allowed as a result. 0 for no register.
@Override public RegMask outregmap() { return x86_64_v2.MEM_MASK; }
+ @Override public void encoding( Encoding enc ) {
+ // REX.W + 8B /r MOV r64, r/m64
+ // Zero extension for u8, u16 and u32 but sign extension i8, i16, i32
+ // Use movsx and movzx
+ short dst = enc.reg(this );
+ short ptr = enc.reg(ptr());
+ short idx = enc.reg(idx());
+ enc(enc, _declaredType, dst, ptr, idx, _off, _scale);
+ }
+
+ static void enc( Encoding enc, Type decl, short dst, short ptr, short idx, int off, int scale ) {
+ // The chapter chooses the register bank and extension from its own type facts.
+ boolean xmm=dst>=x86_64_v2.XMM_OFFSET;
+ if( xmm && decl.log_size()==3 ) decl=TypeFloat.F64;
+ else if( !xmm && decl instanceof TypeFloat )
+ decl=decl==TypeFloat.F32 ? TypeInteger.U32 : TypeInteger.BOT;
+ boolean signed=decl.isa(TypeInteger.I8) || decl==TypeInteger.I16 || decl==TypeInteger.I32;
+ X86.load(enc,decl.log_size(),signed,xmm,
+ xmm ? dst-x86_64_v2.XMM_OFFSET : dst,ptr,idx,off,scale);
+ }
// 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 6d39a2b4..6c7ecbbc 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,19 +1,22 @@
package com.seaofnodes.simple.node.cpus.x86_64_v2;
+import com.seaofnodes.isa.X86;
import com.seaofnodes.simple.SB;
-import com.seaofnodes.simple.codegen.CodeGen;
-import com.seaofnodes.simple.codegen.RegMask;
+import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.*;
public class MemAddX86 extends MemOpX86 {
MemAddX86( StoreNode st, Node base, Node idx, int off, int scale, int imm, Node val ) {
super(st, st, base, idx, off, scale, imm, val );
}
-
+ @Override public String op() {
+ return (_imm == 1 ? "inc" : (_imm == -1 ? "dec" : "add")) + _sz;
+ }
// Register mask allowed as a result. 0 for no register.
@Override public RegMask outregmap() { return null; }
-
-
+ @Override public void encoding( Encoding enc ) {
+ X86.addMem(enc,enc.reg(val()),_imm,enc.reg(ptr()),enc.reg(idx()),_off,_scale);
+ }
// General form: "add [base + idx<<2 + 12] += src"
@Override public void asm(CodeGen code, SB sb) {
asm_address(code,sb);
@@ -21,8 +24,4 @@ public class MemAddX86 extends MemOpX86 {
if( _imm != 1 && _imm != -1 ) sb.p(" += #").p(_imm);
} else sb.p(" += ").p(code.reg(val()));
}
-
- @Override public String op() {
- return (_imm == 1 ? "inc" : (_imm == -1 ? "dec" : "add")) + _sz;
- }
}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MemOpX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MemOpX86.java
index e74de677..fa3cdd13 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
@@ -3,11 +3,10 @@ package com.seaofnodes.simple.node.cpus.x86_64_v2;
import com.seaofnodes.print.ExprPrinter;
import com.seaofnodes.simple.*;
-import com.seaofnodes.simple.codegen.CodeGen;
-import com.seaofnodes.simple.codegen.RegMask;
+import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.*;
import com.seaofnodes.simple.type.*;
-import java.io.ByteArrayOutputStream;
+
import java.lang.StringBuilder;
// Generic X86 memory operand base.
@@ -16,7 +15,7 @@ import java.lang.StringBuilder;
// mem - memory dependence edge
// base - Basic object pointer
// idx/null - Scaled index offset, or null if none
-// val/null - Value to store, as part of a op-to-mem or op-from-mem. Null for loads, or if an immediate is being used
+// val/null - Value to store, as part of an op-to-mem or op-from-mem. Null for loads, or if an immediate is being used
// Constants:
// offset - offset added to base. Can be zero.
// scale - scale on index; only 0,1,2,4,8 allowed, 0 is only when index is null
@@ -25,13 +24,13 @@ public abstract class MemOpX86 extends MemOpNode implements MachNode {
final int _off; // Limit 32 bits
final int _scale; // Limit 0,1,2,3
final int _imm; // Limit 32 bits
- final char _sz = (char)('0'+(1<<_declaredType.log_size()));
+ final char _sz; // Handy print name
MemOpX86( Node op, MemOpNode mop, Node base, Node idx, int off, int scale, int imm ) {
super(op,mop);
assert base._type instanceof TypeMemPtr && !(base instanceof AddNode);
assert (idx==null && scale==0) || (idx!=null && 0<= scale && scale<=3);
- // Copy memory parts from eg the LoadNode over the opcode, e.g. an Add
+ // Copy memory parts from e.g. the LoadNode over the opcode, e.g. an Add
if( op != mop ) {
_inputs.set(0,mop.in(0)); // Control from mem op
_inputs.set(1,mop.in(1)); // Memory from mem op
@@ -43,6 +42,7 @@ public abstract class MemOpX86 extends MemOpNode implements MachNode {
_off = off;
_scale = scale;
_imm = imm;
+ _sz = (char)('0'+(1<<_declaredType.log_size()));
}
// Store-based flavors have a value edge
@@ -58,8 +58,6 @@ public abstract class MemOpX86 extends MemOpNode implements MachNode {
return p.p(".").p(_name);
}
- @Override public boolean isMem() { return _type instanceof TypeMem; }
-
@Override public String label() { return op(); }
@Override public Type compute() { throw Utils.TODO(); }
@Override public Node idealize() { throw Utils.TODO(); }
@@ -69,20 +67,19 @@ public abstract class MemOpX86 extends MemOpNode implements MachNode {
if( i==1 ) return null; // Memory
if( i==2 ) return x86_64_v2.RMASK; // base in GPR
if( i==3 ) return x86_64_v2.RMASK; // index in GPR
- if( i==4 ) return _sz >= '4' ? x86_64_v2.MEM_MASK : x86_64_v2.RMASK; // Narrow stores need GPRs
- return null; // Trailing anti-dependence edges
+ if( i==4 ) return _sz >= '4'
+ ? x86_64_v2.MEM_MASK // value in GPR or XMM
+ : x86_64_v2.RMASK; // Bytes and shorts in GPR only
+ return null; // Anti-dependence
}
- @Override public int encoding(ByteArrayOutputStream bytes) { throw Utils.TODO(); }
-
-
// "[base + idx<<2 + 12]"
SB asm_address(CodeGen code, SB sb) {
sb.p("[").p(code.reg(ptr()));
if( idx() != null ) {
sb.p("+").p(code.reg(idx()));
if( _scale != 0 )
- sb.p("<<").p(_scale);
+ sb.p("*").p((1<<_scale));
}
if( _off != 0 )
sb.p("+").p(_off);
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 9e996130..3371a8ba 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,34 +1,24 @@
package com.seaofnodes.simple.node.cpus.x86_64_v2;
+import com.seaofnodes.isa.X86;
import com.seaofnodes.simple.*;
-import com.seaofnodes.simple.codegen.CodeGen;
-import com.seaofnodes.simple.codegen.RegMask;
+import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.*;
-import com.seaofnodes.simple.type.TypeFloat;
-import com.seaofnodes.simple.type.TypeInteger;
-import java.io.ByteArrayOutputStream;
public class MulFX86 extends MachConcreteNode implements MachNode {
MulFX86( Node mulf) { super(mulf); }
-
- // Register mask allowed on input i.
+ @Override public String op() { return "mulf"; }
@Override public RegMask regmap(int i) { assert i==1 || i==2; return x86_64_v2.XMASK; }
- // Register mask allowed as a result. 0 for no register.
@Override public RegMask outregmap() { return x86_64_v2.XMASK; }
- // Output is same register as input#1
@Override public int twoAddress() { return 1; }
- // 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) {
- throw Utils.TODO();
+ @Override public void encoding( Encoding enc ) {
+ X86.sse(enc,0xF2,0x0F59,false,enc.reg(this)-x86_64_v2.XMM_OFFSET,enc.reg(in(2))-x86_64_v2.XMM_OFFSET);
}
// General form: "mulf dst *= src"
@Override public void asm(CodeGen code, SB sb) {
- sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" * ").p(code.reg(in(2)));
+ sb.p(code.reg(this)).p(" *= ").p(code.reg(in(2)));
}
-
- @Override public String op() { return "mulf"; }
}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MulIX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MulIX86.java
index b41a5720..2c1ce261 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,41 +1,30 @@
package com.seaofnodes.simple.node.cpus.x86_64_v2;
+import com.seaofnodes.isa.X86;
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;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.codegen.*;
-public class MulIX86 extends MachConcreteNode implements MachNode {
- final TypeInteger _ti;
- MulIX86(Node mul, TypeInteger ti) {super(mul); _inputs.pop(); _ti = ti;}
-
- // Register mask allowed on input i.
- // This is the normal calling convention
- @Override public RegMask regmap(int i) {
- // assert i==1;
- return x86_64_v2.WMASK; }
- // Register mask allowed as a result. 0 for no register.
+public class MulIX86 extends MachConcreteNode implements MachNode {
+ final int _imm;
+ MulIX86( Node mul, int imm ) {
+ super(mul);
+ _inputs.pop(); // Pop the constant input
+ _imm = imm; // Record the constant
+ }
+ @Override public String op() { return "muli"; }
+ @Override public String glabel() { return "*"; }
+ @Override public RegMask regmap(int i) { return x86_64_v2.RMASK; }
@Override public RegMask outregmap() { return x86_64_v2.WMASK; }
-
-
- // Output is same register as input#1
+ // Retain Chapter 21's allocation contract: output is also 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();
+ // IMUL encodes a destination register, unlike ImmX86's opcode extension.
+ @Override public final void encoding( Encoding enc ) {
+ X86.imul(enc,enc.reg(this),enc.reg(in(1)),_imm);
}
- // General form
- // General form: "muli dst * #imm"
+ // General form: "muli dst = src * #imm"
@Override public void asm(CodeGen code, SB sb) {
- sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" * #");
- _ti.print(sb);
+ sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" * #").p(_imm);
}
-
- @Override public String op() { return "muli"; }
}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MulX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MulX86.java
index d880208e..cdc67ea8 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,33 +1,24 @@
package com.seaofnodes.simple.node.cpus.x86_64_v2;
+import com.seaofnodes.isa.X86;
import com.seaofnodes.simple.*;
-import com.seaofnodes.simple.codegen.CodeGen;
-import com.seaofnodes.simple.codegen.RegMask;
+import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.*;
-import com.seaofnodes.simple.type.TypeInteger;
-import java.io.ByteArrayOutputStream;
public class MulX86 extends MachConcreteNode implements MachNode {
MulX86( Node mul ) { super(mul); }
-
- // Register mask allowed on input i.
+ @Override public String op() { return "mul"; }
@Override public RegMask regmap(int i) { assert i==1 || i==2; return x86_64_v2.RMASK; }
- // Register mask allowed as a result. 0 for no register.
@Override public RegMask outregmap() { return x86_64_v2.WMASK; }
- // Output is same register as input#1
@Override public int twoAddress() { return 1; }
- // 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) {
- throw Utils.TODO();
+ @Override public void encoding( Encoding enc ) {
+ X86.reg(enc,0x0FAF,enc.reg(this),enc.reg(in(2)));
}
// General form: "mul dst *= src"
@Override public void asm(CodeGen code, SB sb) {
sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" * ").p(code.reg(in(2)));
}
-
- @Override public String op() { return "mul"; }
}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/NegX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/NegX86.java
new file mode 100644
index 00000000..3d3798cd
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/NegX86.java
@@ -0,0 +1,19 @@
+package com.seaofnodes.simple.node.cpus.x86_64_v2;
+
+import com.seaofnodes.isa.X86;
+import com.seaofnodes.simple.SB;
+import com.seaofnodes.simple.codegen.*;
+import com.seaofnodes.simple.node.*;
+
+public class NegX86 extends MachConcreteNode implements MachNode {
+ NegX86(MinusNode not) { super(not); }
+ @Override public String op() { return "neg"; }
+ @Override public RegMask regmap(int i) { return x86_64_v2.RMASK; }
+ @Override public RegMask outregmap() { return x86_64_v2.RMASK; }
+ @Override public RegMask killmap() { return x86_64_v2.FLAGS_MASK; }
+
+ @Override public void encoding( Encoding enc ) {
+ X86.unary(enc,0xF7,3,enc.reg(this));
+ }
+ @Override public void asm(CodeGen code, SB sb) { sb.p(code.reg(this)); }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/NewX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/NewX86.java
index f4803b2e..877ec224 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,29 +1,23 @@
package com.seaofnodes.simple.node.cpus.x86_64_v2;
+import com.seaofnodes.isa.X86;
import com.seaofnodes.simple.*;
-import com.seaofnodes.simple.codegen.CodeGen;
-import com.seaofnodes.simple.codegen.RegMask;
+import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.*;
-import java.io.ByteArrayOutputStream;
+import com.seaofnodes.simple.type.TypeFunPtr;
public class NewX86 extends NewNode implements MachNode {
// A pre-zeroed chunk of memory.
NewX86( NewNode nnn ) { super(nnn); }
- // Size and pointer result in standard calling convention; null for all the
- // memory and ordering edges
- @Override public RegMask regmap(int i) { return i == 2 ? x86_64_v2.RDI_MASK : null; }
- @Override public RegMask outregmap(int i) { return i == 0 ? x86_64_v2.RAX_MASK : null; }
- @Override public RegMask outregmap() { return null; }
-
- // Encoding is appended into the byte array; size is returned
- @Override public int encoding(ByteArrayOutputStream bytes) {
- throw Utils.TODO();
+ @Override public void encoding( Encoding enc ) {
+ enc.external(this,"calloc");
+ X86.constant(enc,_arg2Reg,1);
+ X86.call(enc);
}
-
- // General form: "alloc #bytes"
+ // General form: "alloc #bytes PC"
@Override public void asm(CodeGen code, SB sb) {
- sb.p(code.reg(size()));
+ sb.p("ldi rcx = #1\n");
+ sb.p("call #calloc");
}
- @Override public String op() { return "alloc"; }
}
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
index a4b82de8..46227dd4 100644
--- 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
@@ -1,26 +1,21 @@
package com.seaofnodes.simple.node.cpus.x86_64_v2;
+import com.seaofnodes.isa.X86;
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.codegen.*;
import com.seaofnodes.simple.node.*;
-import java.io.ByteArrayOutputStream;
public class NotX86 extends MachConcreteNode implements MachNode {
- NotX86(NotNode not) {super(not);}
+ NotX86(NotNode not) { super(not); }
+ @Override public String op() { return "not"; }
@Override public RegMask regmap(int i) { return x86_64_v2.RMASK; }
@Override public RegMask outregmap() { return x86_64_v2.RMASK; }
+ @Override public RegMask killmap() { return x86_64_v2.FLAGS_MASK; }
- @Override public 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 encoding( Encoding enc ) {
+ assert !(in(1) instanceof NotNode); // Cleared out by peeps
+ assert !(in(1) instanceof BoolNode);
+ X86.not(enc,enc.reg(this),enc.reg(in(1)));
}
@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 949ac6c2..15f4824d 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,43 +1,11 @@
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.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 {
-
- final TypeInteger _ti;
- OrIX86(Node or, TypeInteger ti) {super(or); _inputs.pop(); _ti = ti;}
-
- // Register mask allowed on input i.
- // This is the normal calling convention
- @Override public RegMask regmap(int i) {
- //assert i==1;
- return x86_64_v2.WMASK; }
- // Register mask allowed as a result. 0 for no register.
- @Override public RegMask outregmap() { return x86_64_v2.WMASK; }
-
-
- // Output is same register as input#1
- @Override public int twoAddress() { return 1; }
-
- // Encoding is appended into the byte array; size is returned
- @Override public int encoding(ByteArrayOutputStream bytes) {
- throw Utils.TODO();
- }
-
- // General form
- // General form: "ori ori | #imm"
- @Override public void asm(CodeGen code, SB sb) {
- sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" | #");
- _ti.print(sb);
- }
+public class OrIX86 extends ImmX86 {
+ OrIX86( Node add, int imm ) { super(add,imm); }
@Override public String op() { return "ori"; }
+ @Override public String glabel() { return "|"; }
+ @Override int opcode() { return 0x81; }
+ @Override int mod() { return 1; }
}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/OrX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/OrX86.java
index 6d99fe3b..bc8f0ea8 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,35 +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.*;
-import java.io.ByteArrayOutputStream;
-
-public class OrX86 extends MachConcreteNode implements MachNode{
- OrX86(Node or) {
- super(or);
- }
-
- // Register mask allowed on input i.
- // This is the normal calling convention
- @Override public RegMask regmap(int i) { assert i==1; return x86_64_v2.WMASK; }
-
- @Override public RegMask outregmap() { return x86_64_v2.WMASK; }
-
- // Output is same register as input#1
- @Override public int twoAddress() { return 1; }
-
- // Encoding is appended into the byte array; size is returned
- @Override public int encoding(ByteArrayOutputStream bytes) {
- throw Utils.TODO();
- }
-
- // General form
- // General form: "or dst | src"
- @Override public void asm(CodeGen code, SB sb) {
- sb.p(code.reg(this)).p(" = ").p(code.reg(in(1)));
- }
+import com.seaofnodes.simple.node.Node;
+public class OrX86 extends RegX86 {
+ OrX86( Node add ) { super(add); }
@Override public String op() { return "or"; }
+ @Override public String glabel() { return "|"; }
+ @Override int opcode() { return 0x0B; }
+ @Override public boolean commutes() { return true; }
}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ParmX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ParmX86.java
index 69ebb74e..5cfdd804 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,27 +1,17 @@
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.Encoding;
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);
- _rmask = x86_64_v2.callInMask(fun().sig(),_idx);
+ _rmask = x86_64_v2.callInMask(fun().sig(),_idx,1/*RPC*/);
}
-
- // Register mask allowed on input i. 0 for no register.
- @Override public RegMask regmap(int i) { return _rmask; }
- // Register mask allowed as a result. Calling convention register
+ @Override public RegMask regmap(int i) { return null; }
@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 void encoding( Encoding enc ) { }
}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ProjX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ProjX86.java
index 7ac014e6..046c7b4c 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,23 +1,15 @@
package com.seaofnodes.simple.node.cpus.x86_64_v2;
-import com.seaofnodes.simple.Utils;
+import com.seaofnodes.simple.codegen.Encoding;
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.
@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) {
- return 0;
- }
+ @Override public void encoding( Encoding enc ) {}
}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/RegX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/RegX86.java
new file mode 100644
index 00000000..8dc749ee
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/RegX86.java
@@ -0,0 +1,21 @@
+package com.seaofnodes.simple.node.cpus.x86_64_v2;
+
+import com.seaofnodes.isa.X86;
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.codegen.*;
+import com.seaofnodes.simple.node.*;
+
+public abstract class RegX86 extends MachConcreteNode {
+ RegX86( Node add ) { super(add); }
+ @Override public RegMask regmap(int i) { return x86_64_v2.RMASK; }
+ @Override public RegMask outregmap() { return x86_64_v2.WMASK; }
+ @Override public int twoAddress() { return 1; }
+ abstract int opcode();
+ @Override public void encoding( Encoding enc ) {
+ X86.reg(enc,opcode(),enc.reg(in(1)),enc.reg(in(2)));
+ }
+ // General form: "add dst += src"
+ @Override public void asm(CodeGen code, SB sb) {
+ sb.p(code.reg(this)).p(" ").p(glabel()).p("= ").p(code.reg(in(2)));
+ }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/RetX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/RetX86.java
index 433f64cc..e4467bb4 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,49 +1,15 @@
package com.seaofnodes.simple.node.cpus.x86_64_v2;
+import com.seaofnodes.isa.X86;
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;
+import com.seaofnodes.simple.codegen.*;
+import com.seaofnodes.simple.node.*;
-// Return
public class RetX86 extends ReturnNode implements MachNode {
RetX86( ReturnNode ret, FunNode fun ) { super(ret, fun); fun.setRet(this); }
-
- // Correct Nodes outside the normal edges
- @Override public void postSelect() {
- FunNode fun = (FunNode)rpc().in(0);
- _fun = fun;
- fun.setRet(this);
- }
-
- // Register mask allowed on input i.
- // This is the normal calling convention
- @Override public RegMask regmap(int i) { return 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) {
- // Without imm form
- // Near return
- int beforeSize = bytes.size();
- bytes.write(0xC3); // opcode
-
- return bytes.size() - beforeSize;
- }
-
- @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 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 void encoding( Encoding enc ) {
+ int sz = fun()._frameAdjust;
+ if( sz!=0 ) X86.imm(enc,0x81,0,x86_64_v2.RSP,sz);
+ X86.ret(enc);
}
-
- @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 9f942cc6..43ad6bbd 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,39 +1,33 @@
package com.seaofnodes.simple.node.cpus.x86_64_v2;
-import com.seaofnodes.simple.*;
+import com.seaofnodes.isa.X86;
+import com.seaofnodes.simple.SB;
import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.Encoding;
import com.seaofnodes.simple.codegen.RegMask;
+import com.seaofnodes.simple.node.MachNode;
+import com.seaofnodes.simple.node.Node;
+
import com.seaofnodes.simple.node.*;
-import com.seaofnodes.simple.type.TypeInteger;
-import java.io.ByteArrayOutputStream;
// Arithmetic Right Shift
public class SarIX86 extends MachConcreteNode implements MachNode {
- final TypeInteger _ti;
- SarIX86(Node sar, TypeInteger ti) {super(sar); _inputs.pop(); _ti = ti;}
+ final int _imm;
+ SarIX86( Node sari, int imm ) { super(sari); assert x86_64_v2.imm8(imm); _inputs.pop(); _imm = imm; }
+ @Override public String op() { return "sari"; }
+ @Override public String glabel() { return ">>"; }
+ int opcode() { return 0xC1; }
+ int mod() { return 7; }
- // Register mask allowed on input i.
- // This is the normal calling convention
- @Override public RegMask regmap(int i) {
- // assert i==1;
- return x86_64_v2.RMASK; }
- // Register mask allowed as a result. 0 for no register.
+ @Override public RegMask regmap(int i) { return x86_64_v2.RMASK; }
@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();
+ @Override public void encoding(Encoding enc) {
+ X86.shift(enc,mod(),enc.reg(this),_imm);
}
- // General form
- // General form: "sari dst << #imm"
@Override public void asm(CodeGen code, SB sb) {
- sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" >> #");
- _ti.print(sb);
+ sb.p(code.reg(this)).p(" ").p(glabel()).p("= #").p(_imm);
}
-
- @Override public String op() { return "sari"; }
}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SarX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SarX86.java
index ee7a2f56..50693034 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,41 +1,21 @@
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;
-import java.util.BitSet;
-import java.lang.StringBuilder;
+import com.seaofnodes.isa.X86;
+import com.seaofnodes.simple.Utils;
+import com.seaofnodes.simple.codegen.*;
+import com.seaofnodes.simple.node.Node;
-
-public class SarX86 extends MachConcreteNode implements MachNode {
- SarX86(Node sar) { super(sar); }
-
- // Register mask allowed on input i.
- // This is the normal calling convention
+public class SarX86 extends RegX86 {
+ SarX86( Node add ) { super(add); }
+ @Override public String op() { return "sar"; }
+ @Override public String glabel() { return ">>"; }
@Override public RegMask regmap(int i) {
- if(i == 1) return x86_64_v2.WMASK;
- if(i == 2) return x86_64_v2.RCX_MASK;
- throw Utils.TODO();
- }
- // Register mask allowed as a result. 0 for no register.
- @Override public RegMask outregmap() { return x86_64_v2.WMASK; }
-
- // Output is same register as input#1
- @Override public int twoAddress() { return 1; }
-
- // Encoding is appended into the byte array; size is returned
- @Override public int encoding(ByteArrayOutputStream bytes) {
+ if (i == 1) return x86_64_v2.WMASK;
+ if (i == 2) return x86_64_v2.RCX_MASK;
throw Utils.TODO();
}
-
- // General form
- @Override public void asm(CodeGen code, SB sb) {
- sb.p(code.reg(this)).p(" >> ").p(code.reg(in(1)));
+ @Override int opcode() { return 0xD3; }
+ @Override public void encoding( Encoding enc ) {
+ X86.unary(enc,opcode(),7,enc.reg(this));
}
-
- @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 53237538..591bf8da 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,33 +1,37 @@
package com.seaofnodes.simple.node.cpus.x86_64_v2;
+import com.seaofnodes.isa.X86;
import com.seaofnodes.simple.*;
-import com.seaofnodes.simple.codegen.CodeGen;
-import com.seaofnodes.simple.codegen.RegMask;
+import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.*;
-import com.seaofnodes.simple.type.Type;
-import com.seaofnodes.simple.type.TypeInteger;
-import java.io.ByteArrayOutputStream;
-// Compare immediate. Sets flags.
// Corresponds to the x86 instruction "sete && setne".
// Use result of comparison without jump.
public class SetX86 extends MachConcreteNode implements MachNode {
final String _bop; // One of <,<=,==
+ final boolean _unsigned;
// Constructor expects input is an X86 and not an Ideal node.
- SetX86( Node cmp, String bop ) {
+ SetX86( Node cmp, String bop, boolean unsigned ) {
super(cmp);
_inputs.setLen(1); // Pop the cmp inputs
// Replace with the matched cmp
_inputs.push(cmp);
_bop = bop;
+ _unsigned = unsigned;
}
-
+ @Override public String op() { return "set"+_bop; }
@Override public RegMask regmap(int i) { assert i==1; return x86_64_v2.FLAGS_MASK; }
@Override public RegMask outregmap() { return x86_64_v2.WMASK; }
- // Encoding is appended into the byte array; size is returned
- @Override public int encoding(ByteArrayOutputStream bytes) {
- throw Utils.TODO();
+ @Override public void encoding( Encoding enc ) {
+ int op=switch (_bop) {
+ case "==" -> 0x94; // SETE
+ case "<" -> _unsigned ? 0x92 : 0x9C; // SETB /SETL
+ case "<=" -> _unsigned ? 0x96 : 0x9E; // SETBE/SETLE
+ case ">=" -> _unsigned ? 0x93 : 0x9D; // SETAE/SETGE
+ default -> throw Utils.TODO();
+ };
+ X86.set(enc,op&15,enc.reg(this));
}
@Override public void asm(CodeGen code, SB sb) {
@@ -35,7 +39,4 @@ public class SetX86 extends MachConcreteNode implements MachNode {
String src = code.reg(in(1));
if( src!="flags" ) sb.p(" = ").p(src);
}
-
- @Override public String op() { return "set"+_bop; }
-
}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShlIX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShlIX86.java
index c18007cd..f31832e6 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,40 +1,33 @@
package com.seaofnodes.simple.node.cpus.x86_64_v2;
+import com.seaofnodes.isa.X86;
+import com.seaofnodes.simple.node.Node;
+
import com.seaofnodes.simple.*;
-import com.seaofnodes.simple.codegen.CodeGen;
-import com.seaofnodes.simple.codegen.RegMask;
+import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.*;
-import com.seaofnodes.simple.type.Type;
-import com.seaofnodes.simple.type.TypeInteger;
-import java.io.ByteArrayOutputStream;
-import java.util.BitSet;
-public class ShlIX86 extends MachConcreteNode implements MachNode{
- final TypeInteger _ti;
- ShlIX86(Node shl, TypeInteger ti) {super(shl); _inputs.pop(); _ti = ti;}
+public class ShlIX86 extends MachConcreteNode implements MachNode {
+ final int _imm;
+ ShlIX86( Node shli, int imm ) {
+ super(shli);
+ assert x86_64_v2.imm8(imm);
+ _inputs.pop(); // Pop constant input off
+ _imm = imm;
+ }
+ public int opcode() { return 0xC1; }
+ public int mod() { return 4; }
- // Register mask allowed on input i.
- // This is the normal calling convention
- @Override public RegMask regmap(int i) {
- //assert i==1;
- return x86_64_v2.RMASK; }
- // Register mask allowed as a result. 0 for no register.
+ @Override public RegMask regmap(int i) { return x86_64_v2.RMASK; }
@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();
+ @Override public String op() { return "shli"; }
+ @Override public String glabel() { return "<<"; }
+ @Override public void encoding(Encoding enc) {
+ X86.shift(enc,mod(),enc.reg(this),_imm);
}
-
- // General form
- // General form: "shli dst << #imm"
@Override public void asm(CodeGen code, SB sb) {
- sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" << #");
- _ti.print(sb);
+ sb.p(code.reg(this)).p(" ").p(glabel()).p("= #").p(_imm);
}
-
- @Override public String op() { return "shli"; }
}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShlX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShlX86.java
index a3de5347..08c4db25 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,39 +1,21 @@
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;
+import com.seaofnodes.isa.X86;
+import com.seaofnodes.simple.Utils;
+import com.seaofnodes.simple.codegen.*;
+import com.seaofnodes.simple.node.Node;
-public class ShlX86 extends MachConcreteNode implements MachNode {
- ShlX86(Node shl) { super(shl); };
-
- // Register mask allowed on input i.
- // This is the normal calling convention
-
- // CL register must be used for input(2)
+public class ShlX86 extends RegX86 {
+ ShlX86( Node add ) { super(add); }
+ @Override public String op() { return "shl"; }
+ @Override public String glabel() { return "<<"; }
@Override public RegMask regmap(int i) {
- if(i == 1) return x86_64_v2.WMASK;
- if(i == 2) return x86_64_v2.RCX_MASK;
+ if (i == 1) return x86_64_v2.WMASK;
+ if (i == 2) return x86_64_v2.RCX_MASK;
throw Utils.TODO();
}
-
- @Override public RegMask outregmap() { return x86_64_v2.WMASK; }
-
- // Output is same register as input#1
- @Override public int twoAddress() { return 1; }
-
- // Encoding is appended into the byte array; size is returned
- @Override public int encoding(ByteArrayOutputStream bytes) {
- throw Utils.TODO();
- }
-
- // General form
- // General form: "shl dst << src"
- @Override public void asm(CodeGen code, SB sb) {
- sb.p(code.reg(this)).p(" << ").p(code.reg(in(1)));
+ @Override int opcode() { return 0xD3; }
+ @Override public final void encoding( Encoding enc ) {
+ X86.unary(enc,opcode(),4,enc.reg(this));
}
-
- @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 d17ee734..218542f3 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,41 +1,28 @@
package com.seaofnodes.simple.node.cpus.x86_64_v2;
+import com.seaofnodes.isa.X86;
+import com.seaofnodes.simple.node.Node;
+
import com.seaofnodes.simple.*;
-import com.seaofnodes.simple.codegen.CodeGen;
-import com.seaofnodes.simple.codegen.RegMask;
+import com.seaofnodes.simple.codegen.*;
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 ShrIX86 extends MachConcreteNode implements MachNode{
- final TypeInteger _ti;
- ShrIX86(Node shr, TypeInteger ti) {super(shr); _inputs.pop(); _ti = ti;}
+public class ShrIX86 extends MachConcreteNode implements MachNode {
+ final int _imm;
+ ShrIX86( Node shri, int imm ) { super(shri); assert x86_64_v2.imm8(imm); _inputs.pop(); _imm = imm; }
+ @Override public String op() { return "shri"; }
+ @Override public String glabel() { return ">>>"; }
+ int opcode() { return 0xC1; }
+ int mod() { return 5; }
- // Register mask allowed on input i.
- // This is the normal calling convention
- @Override public RegMask regmap(int i) {
- //assert i==1;
- return x86_64_v2.RMASK; }
- // Register mask allowed as a result. 0 for no register.
+ @Override public RegMask regmap(int i) { return x86_64_v2.RMASK; }
@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();
+ @Override public void encoding(Encoding enc) {
+ X86.shift(enc,mod(),enc.reg(this),_imm);
}
-
- // General form
- // General form: "shri dst >>> #imm"
@Override public void asm(CodeGen code, SB sb) {
- sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" >>> #");
- _ti.print(sb);
+ sb.p(code.reg(this)).p(" ").p(glabel()).p("= #").p(_imm);
}
-
- @Override public String op() { return "shri"; }
}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShrX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShrX86.java
index 757d9b8e..c72f21e2 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,40 +1,21 @@
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;
+import com.seaofnodes.isa.X86;
+import com.seaofnodes.simple.Utils;
+import com.seaofnodes.simple.codegen.*;
+import com.seaofnodes.simple.node.Node;
-public class ShrX86 extends MachConcreteNode implements MachNode {
- ShrX86(Node shr) {
- super(shr);
- };
- // Register mask allowed on input i.
- // This is the normal calling convention
-
- // CL register must be used for input(2)
+public class ShrX86 extends RegX86 {
+ ShrX86( Node add ) { super(add); }
+ @Override public String op() { return "shr"; }
+ @Override public String glabel() { return ">>>"; }
@Override public RegMask regmap(int i) {
- if(i == 1) return x86_64_v2.WMASK;
- if(i == 2) return x86_64_v2.RCX_MASK;
- throw Utils.TODO();
- }
-
- @Override 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) {
+ if (i == 1) return x86_64_v2.WMASK;
+ if (i == 2) return x86_64_v2.RCX_MASK;
throw Utils.TODO();
}
-
- // General form
- // General form: "shr dst >>> src"
- @Override public void asm(CodeGen code, SB sb) {
- sb.p(code.reg(this)).p(" >>> ").p(code.reg(in(1)));
+ @Override int opcode() { return 0xD3; }
+ @Override public final void encoding( Encoding enc ) {
+ X86.unary(enc,opcode(),5,enc.reg(this));
}
-
- @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
index ea283a87..5cf668d1 100644
--- 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
@@ -1,21 +1,54 @@
package com.seaofnodes.simple.node.cpus.x86_64_v2;
+import com.seaofnodes.isa.X86;
+import com.seaofnodes.simple.SB;
import com.seaofnodes.simple.Utils;
-import com.seaofnodes.simple.codegen.RegMask;
+import com.seaofnodes.simple.codegen.*;
+import com.seaofnodes.simple.node.FunNode;
import com.seaofnodes.simple.node.Node;
import com.seaofnodes.simple.node.SplitNode;
-import java.io.ByteArrayOutputStream;
+import com.seaofnodes.simple.type.Type;
+import com.seaofnodes.simple.type.TypeFloat;
+import com.seaofnodes.simple.type.TypeInteger;
public class SplitX86 extends SplitNode {
SplitX86( String kind, byte round ) { super(kind,round, new Node[2]); }
-
- // Register mask allowed on input i.
+ @Override public String op() { return "mov"; }
@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();
+ // Need to handle 8 cases: . reg->reg, reg->xmm, reg->flags, xmm->reg, xmm->xmm, xmm->flags, flags->reg, flags->xmm,
+ // flags->flags.
+ @Override public void encoding( Encoding enc ) {
+ short dst=enc.reg(this), src=enc.reg(in(1));
+ if( dst==x86_64_v2.FLAGS ) { X86.flags(enc,src,true); return; }
+ if( src==x86_64_v2.FLAGS ) { X86.flags(enc,dst,false); return; }
+ boolean dstX=dst>=x86_64_v2.XMM_OFFSET, srcX=src>=x86_64_v2.XMM_OFFSET;
+ // Stack spills
+ if( dst >= x86_64_v2.MAX_REG ) {
+ int off = enc._fun.computeStackOffset(enc._code,dst);
+ if( src >= x86_64_v2.MAX_REG ) {
+ // Rare stack-stack move. push [RSP+soff]; pop [RSP+doff]
+ int soff = enc._fun.computeStackOffset(enc._code,src);
+ X86.stackCopy(enc,soff,off);
+ return;
+ }
+ StoreX86.encVal(enc, srcX ? TypeFloat.F64 : TypeInteger.BOT, (short)x86_64_v2.RSP, (short)-1/*index*/, src, off, 0);
+ return;
+ }
+ if( src >= x86_64_v2.MAX_REG ) {
+ int off = enc._fun.computeStackOffset(enc._code,src);
+ LoadX86.enc(enc, dstX ? TypeFloat.F64 : TypeInteger.BOT, dst, (short)x86_64_v2.RSP, (short)-1, off, 0);
+ return;
+ }
+
+ X86.move(enc,dstX,srcX,dstX ? dst-x86_64_v2.XMM_OFFSET : dst,
+ srcX ? src-x86_64_v2.XMM_OFFSET : src);
+ }
+
+ // General form: "mov dst = src"
+ @Override public void asm(CodeGen code, SB sb) {
+ FunNode fun = code._encoding==null ? null : code._encoding._fun;
+ sb.p(code.reg(this,fun)).p(" = ").p(code.reg(in(1),fun));
}
}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/StoreX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/StoreX86.java
index 08d481fa..4796c0fe 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,20 +1,21 @@
package com.seaofnodes.simple.node.cpus.x86_64_v2;
+import com.seaofnodes.isa.X86;
import com.seaofnodes.print.ExprPrinter;
import com.seaofnodes.simple.SB;
-import com.seaofnodes.simple.codegen.CodeGen;
-import com.seaofnodes.simple.codegen.RegMask;
+import com.seaofnodes.simple.codegen.*;
+import com.seaofnodes.simple.node.ConstantNode;
import com.seaofnodes.simple.node.Node;
import com.seaofnodes.simple.node.StoreNode;
+import com.seaofnodes.simple.type.Type;
+import com.seaofnodes.simple.type.TypeFloat;
public class StoreX86 extends MemOpX86 {
StoreX86( StoreNode st, Node base, Node idx, int off, int scale, int imm, Node val ) {
super(st,st, base, idx, off, scale, imm, val);
}
-
@Override public String op() { return "st"+_sz; }
-
@Override protected ExprPrinter<Node> _printMach(ExprPrinter<Node> p) {
Node val = val();
p.p(".").p(_name).p("=");
@@ -22,9 +23,21 @@ public class StoreX86 extends MemOpX86 {
else p.n(val);
return p.p(";");
}
-
// Register mask allowed as a result. 0 for no register.
@Override public RegMask outregmap() { return null; }
+ @Override public void encoding( Encoding enc ) {
+ short ptr=enc.reg(ptr()), idx=enc.reg(idx()), src=enc.reg(val());
+ if( src==-1 ) X86.storeImm(enc,_declaredType.log_size(),_imm,ptr,idx,_off,_scale);
+ else encVal(enc,_declaredType,ptr,idx,src,_off,_scale);
+ }
+
+ // Non-immediate encoding
+ static void encVal( Encoding enc, Type decl, short ptr, short idx, short src, int off, int scale ) {
+ boolean xmm=src>=x86_64_v2.XMM_OFFSET;
+ X86.store(enc,decl.log_size(),xmm,
+ xmm ? src-x86_64_v2.XMM_OFFSET : src,ptr,idx,off,scale);
+ }
+
// General form: "stN [base + idx<<2 + 12],val"
@Override public void asm(CodeGen code, SB sb) {
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 ceaf0636..6a008551 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,31 +1,23 @@
package com.seaofnodes.simple.node.cpus.x86_64_v2;
+import com.seaofnodes.isa.X86;
import com.seaofnodes.simple.*;
-import com.seaofnodes.simple.codegen.CodeGen;
-import com.seaofnodes.simple.codegen.RegMask;
+import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.*;
-import java.io.ByteArrayOutputStream;
public class SubFX86 extends MachConcreteNode implements MachNode {
SubFX86( Node subf) { super(subf); }
-
-
- // Register mask allowed on input i.
+ @Override public String op() { return "subf"; }
@Override public RegMask regmap(int i) { assert i==1 || i==2; return x86_64_v2.XMASK; }
- // Register mask allowed as a result. 0 for no register.
@Override public RegMask outregmap() { return x86_64_v2.XMASK; }
- // Output is same register as input#1
@Override public int twoAddress() { return 1; }
- // Encoding is appended into the byte array; size is returned
- @Override public int encoding(ByteArrayOutputStream bytes) {
- throw Utils.TODO();
+ @Override public void encoding( Encoding enc ) {
+ X86.sse(enc,0xF2,0x0F5C,false,enc.reg(this)-x86_64_v2.XMM_OFFSET,enc.reg(in(2))-x86_64_v2.XMM_OFFSET);
}
// General form: "subf dst -= src"
@Override public void asm(CodeGen code, SB sb) {
- sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" - ").p(code.reg(in(2)));
+ sb.p(code.reg(this)).p(" -= ").p(code.reg(in(2)));
}
-
- @Override public String op() { return "subf"; }
}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SubX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SubX86.java
index da44a838..8da34bf5 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,31 +1,10 @@
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 SubX86 extends MachConcreteNode implements MachNode {
- SubX86( Node sub ) { super(sub); }
-
- // Register mask allowed on input i.
- @Override public RegMask regmap(int i) { assert i==1 || i==2; return x86_64_v2.RMASK; }
- // Register mask allowed as a result. 0 for no register.
- @Override public RegMask outregmap() { return x86_64_v2.WMASK; }
- // Output is same register as input#1
- @Override public int twoAddress() { return 1; }
-
- // Encoding is appended into the byte array; size is returned
- @Override public int encoding(ByteArrayOutputStream bytes) {
- throw Utils.TODO();
- }
-
- // General form: "sub dst -= src"
- @Override public void asm(CodeGen code, SB sb) {
- sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" - ").p(code.reg(in(2)));
- }
+import com.seaofnodes.simple.node.Node;
+public class SubX86 extends RegX86 {
+ SubX86( Node add ) { super(add); }
@Override public String op() { return "sub"; }
+ @Override public String glabel() { return "-"; }
+ @Override int opcode() { return 0x2B; }
}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/TFPX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/TFPX86.java
index 2b4de2ca..d0c6157c 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,29 +1,41 @@
package com.seaofnodes.simple.node.cpus.x86_64_v2;
+import com.seaofnodes.isa.X86;
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.codegen.*;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.node.cpus.riscv.riscv;
import com.seaofnodes.simple.type.Type;
import com.seaofnodes.simple.type.TypeFunPtr;
-import java.io.ByteArrayOutputStream;
-
// Function constants
-public class TFPX86 extends ConstantNode implements MachNode {
+public class TFPX86 extends ConstantNode implements MachNode, RIPRelSize {
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 String op() {
+ return _con == Type.NIL ? "xor" : "ldx";
+ }
+ @Override public boolean isClone() { return true; }
+ @Override public Node copy() { return new TFPX86(this); }
@Override public RegMask regmap(int i) { return null; }
- // 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); }
+ // Zero-set uses XOR kills flags
+ @Override public RegMask killmap() {
+ return _con == Type.NIL ? x86_64_v2.FLAGS_MASK : null;
+ }
+
+ @Override public void encoding( Encoding enc ) {
+ if( _con==Type.NIL ) { X86.constant(enc,enc.reg(this),0); return; }
+ enc.relo(this);
+ X86.rip(enc,0,0x8D,true,enc.reg(this));
+ }
+
+ @Override public byte encSize(int delta) {
+ return 7;
+ }
- // Encoding is appended into the byte array; size is returned
- @Override public int encoding(ByteArrayOutputStream bytes) {
- throw Utils.TODO();
+ // Delta is from opcode start
+ @Override public void patch( Encoding enc, int opStart, int opLen, int delta ) {
+ enc.patch4(opStart + 3, delta - 7);
}
// Human-readable form appended to the SB. Things like the encoding,
@@ -35,6 +47,6 @@ public class TFPX86 extends ConstantNode implements MachNode {
if( _con == Type.NIL )
sb.p(reg).p(",").p(reg);
else
- _con.print(sb.p(reg).p(" #"));
+ _con.print(sb.p(reg).p(" = #"));
}
}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/UJmpX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/UJmpX86.java
new file mode 100644
index 00000000..d991e40a
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/UJmpX86.java
@@ -0,0 +1,40 @@
+package com.seaofnodes.simple.node.cpus.x86_64_v2;
+
+import com.seaofnodes.isa.X86;
+
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.codegen.*;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.type.Type;
+
+// unconditional jump
+public class UJmpX86 extends CFGNode implements MachNode, RIPRelSize {
+ UJmpX86() { }
+ @Override public String op() { return "jmp"; }
+ @Override public String label() { return op(); }
+ @Override protected String format() { return "jmp "; }
+ @Override public RegMask regmap(int i) {return null; }
+ @Override public RegMask outregmap() { return null; }
+ @Override public Type compute() { throw Utils.TODO(); }
+ @Override public Node idealize() { throw Utils.TODO(); }
+ @Override public void encoding( Encoding enc ) {
+ enc.jump(this,uctrl());
+ X86.branch(enc,-1);
+ }
+
+ // Delta is from opcode start, but X86 measures from the end of the 2-byte encoding
+ @Override public byte encSize(int delta) {
+ return X86.branchSize(delta,false);
+ }
+
+ // Delta is from opcode start
+ @Override public void patch( Encoding enc, int opStart, int opLen, int delta ) {
+ X86.patchBranch(enc.bits(),opStart,opLen,delta,-1);
+ }
+
+ @Override public void asm(CodeGen code, SB sb) {
+ CFGNode target = uctrl();
+ //assert target.nOuts() > 1; // Should optimize jmp to empty targets
+ sb.p(label(target));
+ }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/XorIX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/XorIX86.java
index 47c6be73..2d989a50 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,41 +1,11 @@
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.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 {
-
- final TypeInteger _ti;
- XorIX86(Node xor, TypeInteger ti) {super(xor); _inputs.pop(); _ti = ti;}
-
- // Register mask allowed on input i.
- // This is the normal calling convention
- @Override public RegMask regmap(int i) { assert i==1; return x86_64_v2.WMASK; }
- // Register mask allowed as a result. 0 for no register.
- @Override public RegMask outregmap() { return x86_64_v2.WMASK; }
-
-
- // Output is same register as input#1
- @Override public int twoAddress() { return 1; }
-
- // Encoding is appended into the byte array; size is returned
- @Override public int encoding(ByteArrayOutputStream bytes) {
- throw Utils.TODO();
- }
-
- // General form
- // General form: "ori xori ^ #imm"
- @Override public void asm(CodeGen code, SB sb) {
- sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" ^ #");
- _ti.print(sb);
- }
+public class XorIX86 extends ImmX86 {
+ XorIX86( Node add, int imm ) { super(add,imm); }
@Override public String op() { return "xori"; }
+ @Override public String glabel() { return "^"; }
+ @Override int opcode() { return 0x81; }
+ @Override int mod() { return 6; }
}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/XorX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/XorX86.java
index 3322d951..86703903 100644
--- 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
@@ -1,36 +1,11 @@
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)));
- }
+public class XorX86 extends RegX86 {
+ XorX86( Node add ) { super(add); }
@Override public String op() { return "xor"; }
-
+ @Override public String glabel() { return "^"; }
+ @Override int opcode() { return 0x33; }
+ @Override public boolean commutes() { return true; }
}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/x86_64_v2.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/x86_64_v2.java
index c66aed9f..1b7cf775 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,26 +1,37 @@
package com.seaofnodes.simple.node.cpus.x86_64_v2;
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.codegen.*;
import com.seaofnodes.simple.node.*;
import com.seaofnodes.simple.type.*;
public class x86_64_v2 extends Machine {
+ public x86_64_v2( CodeGen code ) {}
// X86-64 V2. Includes e.g. SSE4.2 and POPCNT.
@Override public String name() { return "x86_64_v2"; }
+ @Override public int defaultOpSize() { return 5; }
- 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 final int RAX = 0, RCX = 1, RDX = 2, RBX = 3, RSP = 4, RBP = 5, RSI = 6, RDI = 7;
+ public static final int R08 = 8, R09 = 9, R10 = 10, R11 = 11, R12 = 12, R13 = 13, R14 = 14, R15 = 15;
- 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;
+ public static final int XMM0 = 16, XMM1 = 17, XMM2 = 18, XMM3 = 19, XMM4 = 20, XMM5 = 21, XMM6 = 22, XMM7 = 23;
+ public static final int XMM8 = 24, XMM9 = 25, XMM10 = 26, XMM11 = 27, XMM12 = 28, XMM13 = 29, XMM14 = 30, XMM15 = 31;
+ public static final int FLAGS = 32;
+ public static final int MAX_REG = 33;
+ public static final int RPC = 33;
+ // Human-readable name for a register number, e.g. "RAX".
+ public static final String[] REGS = new String[]{
+ "rax", "rcx", "rdx", "rbx", "rsp", "rbp", "rsi", "rdi",
+ "r8" , "r9" , "r10", "r11", "r12", "r13", "r14", "r15",
+ "xmm0", "xmm1", "xmm2" , "xmm3" , "xmm4" , "xmm5" , "xmm6" , "xmm7" ,
+ "xmm8", "xmm9", "xmm10", "xmm11", "xmm12", "xmm13", "xmm14", "xmm15",
+ "flags",
+ };
+ // Hard crash for bad register number, fix yer bugs!
+ @Override public String[] regs() { return REGS; }
+
+ public static int XMM_OFFSET = 16;
// General purpose register mask: pointers and ints, not floats
static final long RD_BITS = 0b1111111111111111L; // All the GPRs
static RegMask RMASK = new RegMask(RD_BITS);
@@ -29,49 +40,138 @@ public class x86_64_v2 extends Machine {
static RegMask WMASK = new RegMask(WR_BITS);
// Xmm register 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 RegMask XMASK = new RegMask(FP_BITS);
+ static RegMask FLAGS_MASK = new RegMask(FLAGS);
+ static 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 );
+ 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);
- 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 static RegMask RAX_MASK = new RegMask(RAX);
+ public static RegMask RCX_MASK = new RegMask(RCX);
+ public static RegMask RDX_MASK = new RegMask(RDX);
+ public static RegMask RDI_MASK = new RegMask(RDI);
+ public static RegMask R08_MASK = new RegMask(R08);
+ public static RegMask R09_MASK = new RegMask(R09);
+ public static RegMask RSI_MASK = new RegMask(RSI);
+
+ public static RegMask XMM0_MASK = new RegMask(XMM0);
+
+ // Encoding
+ public static int REX = 0x40;
+ public static int REX_W = 0x48;
+ public static int REX_WR = 0x4C;
+ public static int REX_WRB= 0x4D;
+ public static int REX_WB = 0x49;
+
+ // opcode included here
+ // 0F 84 cd JE rel32
+ // 0F 85 cd JNE rel32
+ // 0F 8F cd JG rel32
+ // 0F 8C cd JL rel32
+ // 0F 8E cd JLE rel32
+ // 0F 8D cd JGE rel32
+ static public int jumpop(String op) {
+ return switch (op) {
+ case "==" -> 0x84;
+ case "!=" -> 0x85;
+ case ">" -> 0x8F;
+ case "<" -> 0x8C;
+ case "<=" -> 0x8E;
+ case ">=" -> 0x8D;
+ default -> throw new IllegalArgumentException("Too many arguments");
+ };
+ }
- // Human-readable name for a register number, e.g. "RAX".
- // Hard crash for bad register number, fix yer bugs!
- 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","[rsp-4]",
+ // Limit of float args passed in registers
+ static RegMask[] XMMS8 = new RegMask[]{
+ new RegMask(XMM0), new RegMask(XMM1), new RegMask(XMM2), new RegMask(XMM3),
+ new RegMask(XMM4), new RegMask(XMM5), new RegMask(XMM6), new RegMask(XMM7),
};
- @Override public String reg( int reg ) {
- return reg < REGS.length ? REGS[reg] : "[rsp+"+(reg-REGS.length)*4+"]";
+
+ // Map from function signature and argument index to register.
+ // Used to set input registers to CallNodes, and ParmNode outputs.
+ @Override public RegMask callArgMask( TypeFunPtr tfp, int idx, int maxArgSlot ) { return callInMask(tfp,idx,maxArgSlot); }
+ static RegMask callInMask( TypeFunPtr tfp, int idx, int maxArgSlot ) {
+ if( idx==0 ) return RPC_MASK;
+ if( idx==1 ) return null;
+ return switch( CodeGen.CODE._callingConv ) {
+ case "SystemV" -> callSys5 (tfp,idx,maxArgSlot);
+ case "Win64" -> callWin64(tfp,idx,maxArgSlot);
+ default -> throw Utils.TODO();
+ };
}
- // WIN64(param passing)
- static RegMask[] CALLINMASK_WIN64 = new RegMask[] {
+ // Maximum stack args used by this signature
+ @Override public short maxArgSlot( TypeFunPtr tfp ) {
+ return switch( CodeGen.CODE._callingConv ) {
+ case "SystemV" -> maxArgSlotSys5 (tfp);
+ case "Win64" -> maxArgSlotWin64(tfp);
+ default -> throw Utils.TODO();
+ };
+ }
+
+ // Win64 - max *4* args in registers of all kinds; after that on stack.
+ // Stack args land in increasing memory order with empty/mirror slots for
+ // every register.
+ //
+ // foo( int i0, flt f1, int i2, flt f3, int i4, flt f5, int i6, ... )
+ //
+ // -- Prior Frame --
+ // i6
+ // f5
+ // i4
+ // empty mirror, XMM3= f3
+ // empty mirror, R08 = i2
+ // empty mirror, XMM1= f1
+ // empty mirror, RCX = i0
+ // -- Caller Frame; 16b align --
+ // RPC
+ // PAD/ALIGN
+ // -- Callee Frame; 16b align --
+ static RegMask[] WIN64_CALL = new RegMask[] {
RCX_MASK,
RDX_MASK,
R08_MASK,
R09_MASK,
};
+ static RegMask callWin64(TypeFunPtr tfp, int idx, int maxArgSlot ) {
+ // idx 2,3,4,5 passed in registers, with stack slot mirrors.
+ // idx >= 6 passed on stack, starting at slot#1 (#0 reserved for RPC).
+ if( idx >= 6 )
+ return new RegMask(MAX_REG+maxArgSlot+(idx-2));
+ return tfp.arg(idx-2) instanceof TypeFloat
+ ? XMMS8 [idx-2]
+ : WIN64_CALL[idx-2];
+ }
+ static short maxArgSlotWin64(TypeFunPtr tfp) {
+ return (short)tfp.nargs();
+ }
+
+ // Sys5: max 6 GPRs and 8 FPRS filled first. Extra args land in increasing
+ // memory order as needed - no mirror space.
+ //
+ // foo( int i0, flt f1, int i2, flt f3, int i4, flt f5, int i6, ... )
+ // RDI =i0, RSI =i2, RDX =i4, RCX =i6, R08 =i8, R09 =i10
+ // XMM0=f1, XMM1=f3, XMM2=f5, XMM3=f7, XMM4=f9, XMM5=f11, XMM6=f13, XMM7=f15
+ //
+ // -- Prior Frame --
+ // f18
+ // i18
+ // f17
+ // i16
+ // i14
+ // i12
+ // -- Caller Frame; 16b align --
+ // RPC
+ // PAD/ALIGN
+ // -- Callee Frame; 16b align --
// SystemV(param passing)
- static RegMask[] CALLINMASK_SYSTEMV = new RegMask[] {
+ static RegMask[] SYS5_CALL = new RegMask[] {
RDI_MASK,
RSI_MASK,
RDX_MASK,
@@ -80,289 +180,245 @@ public class x86_64_v2 extends Machine {
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];
+ static RegMask callSys5(TypeFunPtr tfp, int idx, int maxArgSlot ) {
+ // First 6 integers passed in registers: rdi,rsi,rdx,rcx,r08,r09
+ // First 8 floats passed in registers: xmm0-xmm7
+ int icnt=0, fcnt=0; // Count of ints, floats
+ for( int i=2; i<idx; i++ ) {
+ if( tfp.arg(i-2) instanceof TypeFloat ) fcnt++;
+ else icnt++;
+ }
+ int nstk = Math.max(icnt-6,0)+Math.max(fcnt-8,0);
+ return tfp.arg(idx-2) instanceof TypeFloat
+ ? fcnt<8 ? XMMS8 [fcnt] : new RegMask(MAX_REG+maxArgSlot+nstk)
+ : icnt<6 ? SYS5_CALL[icnt] : new RegMask(MAX_REG+maxArgSlot+nstk);
+ }
+ static short maxArgSlotSys5(TypeFunPtr tfp) {
+ int icnt=0, fcnt=0; // Count of ints, floats
+ for( int i=0; i<tfp.nargs(); i++ ) {
+ if( tfp.arg(i) instanceof TypeFloat ) fcnt++;
+ else icnt++;
}
- throw Utils.TODO(); // Pass on stack slot
+ int nstk = Math.max(icnt-6,0)+Math.max(fcnt-8,0);
+ return (short)nstk;
}
- // 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
+ (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)
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);
+ (1L<<XMM4) | (1L<<XMM5) ;
- 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 long callerSave() {
+ return switch (CodeGen.CODE._callingConv) {
+ case "SystemV" -> SYSTEM5_CALLER_SAVE;
+ case "Win64" -> WIN64_CALLER_SAVE;
+ default -> throw new IllegalArgumentException("Unknown calling convention: " + CodeGen.CODE._callingConv);
};
}
- @Override public 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 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;
+ @Override public long neverSave() { return 1L<<RSP; }
+ @Override public RegMask retMask( TypeFunPtr tfp ) {
+ return tfp.ret() instanceof TypeFloat ? XMM0_MASK : RAX_MASK;
}
+ @Override public int rpc() { return RPC; }
- // 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);
- };
- return 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 SplitX86(kind,round); }
+ @Override public SplitNode split(String kind, byte round, LRG lrg) {
+ return new SplitX86(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();
+ return new UJmpX86();
}
// Instruction selection
- @Override public Node instSelect( Node n ) {
- return switch( n ) {
- case AddFNode addf -> addf(addf);
- case AddNode add -> add(add);
- case AndNode and -> and(and);
- case BoolNode bool -> cmp(bool);
- case 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);
- case DivNode div -> new DivX86(div);
- case FunNode fun -> new FunX86(fun);
- case IfNode iff -> jmp(iff);
- case LoadNode ld -> ld(ld);
- case MemMergeNode mem -> new MemMergeNode(mem);
- case MulFNode mulf -> new MulFX86(mulf);
- case MulNode mul -> mul(mul);
- case NewNode nnn -> new NewX86(nnn);
- case OrNode or -> or(or);
- case ParmNode parm -> new ParmX86(parm);
+ @Override
+ public Node instSelect(Node n) {
+ return switch (n) {
+ case AddFNode addf -> addf(addf);
+ case AddNode add -> add(add);
+ case AndNode and -> and(and);
+ case BoolNode bool -> cmp(bool);
+ case CProjNode c -> new CProjNode(c);
+ case CallEndNode cend -> new CallEndX86(cend);
+ case CallNode call -> call(call);
+ case CastNode cast -> new CastX86(cast);
+ case ConstantNode con -> con(con);
+ case DivFNode divf -> new DivFX86(divf);
+ case DivNode div -> new DivX86(div);
+ case FunNode fun -> new FunX86(fun);
+ case IfNode iff -> jmp(iff);
+ case LoadNode ld -> ld(ld);
+ case MemMergeNode mem -> new MemMergeNode(mem);
+ case MinusNode neg -> new NegX86(neg);
+ case MulFNode mulf -> new MulFX86(mulf);
+ case MulNode mul -> mul(mul);
+ case NewNode nnn -> new NewX86(nnn);
+ case NotNode not -> new NotX86(not);
+ case OrNode or -> or(or);
+ case ParmNode parm -> new ParmX86(parm);
case BulkMemPhiNode phi -> new BulkMemPhiNode(phi);
case MemPhiNode phi -> new MemPhiNode(phi);
- case PhiNode phi -> new PhiNode(phi);
- case ProjNode prj -> prj(prj);
- case ReadOnlyNode read -> new ReadOnlyNode(read);
- case ReturnNode ret -> new RetX86(ret,ret.fun());
- case SarNode sar -> sar(sar);
- case ShlNode shl -> shl(shl);
- case ShrNode shr -> shr(shr);
- case StartNode start -> new StartNode(start);
- case StopNode stop -> new StopNode(stop);
- case StoreNode st -> st(st);
- case SubFNode subf -> new SubFX86(subf);
- case SubNode sub -> sub(sub);
- case ToFloatNode tfn -> i2f8(tfn);
+ case PhiNode phi -> new PhiNode(phi);
+ case ProjNode prj -> prj(prj);
+ case ReadOnlyNode read-> new ReadOnlyNode(read);
+ case ReturnNode ret -> new RetX86(ret, ret.fun());
+ case SarNode sar -> sar(sar);
+ case ShlNode shl -> shl(shl);
+ case ShrNode shr -> shr(shr);
+ case StartNode start -> new StartNode(start);
+ case StopNode stop -> new StopNode(stop);
+ case StoreNode st -> st(st);
+ case SubFNode subf -> new SubFX86(subf);
+ case SubNode sub -> sub(sub);
+ case ToFloatNode tfn -> i2f8(tfn);
case ToIntegerNode cvt -> new F8ToIX86(cvt);
- case XorNode xor -> xor(xor);
+ case XCtrlNode x -> new ConstantNode(Type.XCONTROL);
+ case XorNode xor -> xor(xor);
- case LoopNode loop -> new LoopNode(loop);
- case RegionNode region-> new RegionNode(region);
+ case LoopNode loop -> new LoopNode(loop);
+ case RegionNode region -> new RegionNode(region);
default -> throw Utils.TODO();
};
}
+ public static boolean imm8( long imm ) { return -128 <= imm && imm <= 127; }
+
+ public static boolean imm32( long imm ) { return (int)imm==imm; }
// Attempt a full LEA-style break down.
- private Node add( AddNode add ) {
+ private Node add(AddNode add) {
Node lhs = add.in(1);
Node rhs = add.in(2);
- // The other addend must occupy the result register, even if constant.
- if( lhs instanceof LoadNode ld && ld.nOuts()==1 )
- return new AddMemX86(add,address(ld),ld.ptr(),idx,off,scale, 0,rhs);
+ if( lhs instanceof LoadNode ld && ld.nOuts() == 1 && ld._declaredType.log_size() >= 3)
+ return new AddMemX86(add, address(ld), ld.ptr(), idx, off, scale, 0, rhs);
- if( rhs instanceof LoadNode ld && ld.nOuts()==1 )
+ if( rhs instanceof LoadNode ld && ld.nOuts()==1 && ld._declaredType.log_size()>=3 )
return new AddMemX86(add,address(ld),ld.ptr(),idx,off,scale, 0,lhs);
// Attempt a full LEA-style break down.
// Returns one of AddX86, AddIX86, LeaX86, or LHS
- if( rhs instanceof ConstantNode off && off._con instanceof TypeInteger toff ) {
+ if( rhs instanceof ConstantNode off2 && off2._con instanceof TypeInteger toff ) {
+ long imm = toff.value();
+ assert imm!=0; // Folded in peeps
+ if( (int)imm != imm ) // Full 64bit immediate
+ return new AddX86(add);
+ // Now imm <= 32bits
if( lhs instanceof AddNode ladd )
// ((base + (idx << scale)) + off)
- return _lea(add,ladd.in(1),ladd.in(2),toff.value());
+ return _lea(add, ladd.in(1), ladd.in(2), (int)imm);
if( lhs instanceof ShlNode shift )
// (idx << scale) + off; no base
- return _lea(add,null,shift,toff.value());
+ return _lea(add, null, shift, (int)imm);
// lhs + rhs1
- if( toff.value()==0 ) return add;
- return new AddIX86(add, toff);
+ return new AddIX86(add, (int)imm);
}
- return _lea(add,lhs,rhs,0);
+ return _lea(add, lhs, rhs, 0);
}
- private Node addf( AddFNode addf ) {
- if( addf.in(1) instanceof LoadNode ld && ld.nOuts()==1 )
- return new AddFMemX86(addf,address(ld),ld.ptr(),idx,off,scale, addf.in(2));
+ private Node addf(AddFNode addf) {
+ if(addf.in(1) instanceof LoadNode ld && ld.nOuts() == 1)
+ return new AddFMemX86(addf, address(ld), ld.ptr(), idx, off, scale, addf.in(2));
- if( addf.in(2) instanceof LoadNode ld && ld.nOuts()==1 )
- throw Utils.TODO(); // Swap load sides
+// if(addf.in(2) instanceof LoadNode ld && ld.nOuts() == 1)
+// throw Utils.TODO(); // Swap load sides
return new AddFX86(addf);
}
- private Node _lea( Node add, Node base, Node idx, long off ) {
- int scale = 1;
- if( base instanceof ShlNode && !(idx instanceof ShlNode) ) throw Utils.TODO(); // Bug in canonicalization, should on RHS
+ private Node _lea(Node add, Node base, Node idx, int off) {
+ int scale = 0;
+ if( base instanceof ShlNode && !(idx instanceof ShlNode) )
+ throw Utils.TODO(); // Bug in canonicalization, should on RHS
if( idx instanceof ShlNode shift && shift.in(2) instanceof ConstantNode shfcon &&
shfcon._con instanceof TypeInteger tscale && 0 <= tscale.value() && tscale.value() <= 3 ) {
idx = shift.in(1);
- scale = 1 << ((int)tscale.value());
+ scale = ((int) tscale.value());
}
// (base + idx) + off
- return off==0 && scale==1
+ return off == 0 && scale == 0
? new AddX86(add)
- : new LeaX86(add,base,idx,scale,off);
+ : new LeaX86(add, base, idx, scale, off);
}
private Node and(AndNode and) {
- if( and.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti )
- return new AndIX86(and, ti);
+ if( and.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti && imm32(ti.value()) )
+ return new AndIX86(and, (int)ti.value());
return new AndX86(and);
}
private Node call(CallNode call) {
- if( call.fptr() instanceof ConstantNode con && con._con instanceof TypeFunPtr tfp )
- return new CallX86(call, tfp);
- return new CallRX86( call );
+ return call.fptr() instanceof ConstantNode con && con._con instanceof TypeFunPtr tfp
+ ? new CallX86(call, tfp)
+ : new CallRX86(call);
}
// Because X86 flags, a normal ideal Bool is 2 X86 ops: a "cmp" and at "setz".
// Ideal If reading from a setz will skip it and use the "cmp" instead.
+ private static boolean swap, unsigned;
private Node cmp( BoolNode bool ) {
+ swap = unsigned = false;
Node cmp = _cmp(bool);
- return new SetX86(cmp,bool.op());
+ return new SetX86(cmp, swap ? IfNode.swap(bool.op()) : bool.op(), unsigned);
}
- private Node _cmp( BoolNode bool ) {
+
+ private Node _cmp(BoolNode bool) {
// Float variant
- if( bool.isFloat() )
+ if( bool.isFloat() ) {
+ unsigned = true;
return new CmpFX86(bool);
-
+ }
Node lhs = bool.in(1);
Node rhs = bool.in(2);
- if( lhs instanceof LoadNode ld && ld.nOuts()==1 )
- return new CmpMemX86(bool,address(ld),ld.ptr(),idx,off,scale, imm(rhs),val,false);
- if( rhs instanceof LoadNode ld && ld.nOuts()==1 )
- return new CmpMemX86(bool,address(ld),ld.ptr(),idx,off,scale, imm(lhs),val,true);
+ // Since cmp does not record a BOP, SetX/Jmp need to know if the CMP is swapped.
+
+ // Vs memory
+ if( lhs instanceof LoadNode ld && ld.nOuts() == 1 && rhs._type.isa(ld._declaredType) )
+ return new CmpMemX86(bool, address(ld), ld.ptr(), idx, off, scale, imm(rhs), val, false);
+
+ // Operands swap in the encoding directly, no need for Set/Jmp to swap `bop`
+ if( rhs instanceof LoadNode ld && ld.nOuts() == 1 && lhs._type.isa(ld._declaredType))
+ return new CmpMemX86(bool, address(ld), ld.ptr(), idx, off, scale, imm(lhs), val, true );
// Vs immediate
- if( rhs instanceof ConstantNode con && con._con instanceof TypeInteger ti )
- return new CmpIX86(bool, ti);
+ if( rhs instanceof ConstantNode con && con._con instanceof TypeInteger ti && imm32(ti.value()) )
+ return new CmpIX86(bool, (int)ti.value(), false);
+
+ // Operand swap compare; Set and Jmp need to swap `bop`
+ if( lhs instanceof ConstantNode con && con._con instanceof TypeInteger ti && imm32(ti.value()) )
+ return new CmpIX86(bool, (int)ti.value(), swap=true);
+
// x vs y
return new CmpX86(bool);
}
private Node con( ConstantNode con ) {
- if( !con._con.isConstant() ) return new ConstantNode( con ); // Default unknown caller inputs
- return switch( con._con ) {
- case TypeInteger ti -> new IntX86(con);
- case TypeFloat tf -> new FltX86(con);
- case TypeFunPtr tfp -> new TFPX86(con);
- case TypeMemPtr tmp -> 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 IntX86(con) : new 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 -> 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 IntX86(con) : new ConstantNode(con);
};
}
@@ -371,28 +427,30 @@ public class x86_64_v2 extends Machine {
return new I2f8X86(tfn);
}
- private Node jmp( IfNode iff ) {
+ private Node jmp(IfNode iff) {
// If/Bool combos will match to a Cmp/Set which sets flags.
// Most general arith ops will also set flags, which the Jmp needs directly.
// Loads do not set the flags, and will need an explicit TEST
- if( !(iff.in(1) instanceof BoolNode) )
- iff.setDef(1,new BoolNode.EQ(iff.in(1),new ConstantNode(TypeInteger.ZERO)));
- return new JmpX86(iff, ((BoolNode)iff.in(1)).op());
+ String op = "!=";
+ if( iff.in(1) instanceof BoolNode bool ) op = swap ? IfNode.swap(bool.op()) : bool.op();
+ else if( iff.in(1)==null ) op = "=="; // Never-node cutout
+ else iff.setDef(1, new BoolNode.NE(iff.in(1), new ConstantNode(TypeInteger.ZERO)));
+ return new JmpX86(iff, op);
}
- private Node ld( LoadNode ld ) {
- return new LoadX86(address(ld),ld.ptr(),idx,off,scale);
+ private Node ld(LoadNode ld) {
+ return new LoadX86(address(ld), ld.ptr(), idx, off, scale);
}
private Node mul(MulNode mul) {
- return mul.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti
- ? new MulIX86(mul, ti)
- : new MulX86(mul);
+ if( mul.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti && imm32(ti.value()) )
+ return new MulIX86(mul, (int)ti.value());
+ return new MulX86(mul);
}
private Node or(OrNode or) {
- if( or.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti)
- return new OrIX86(or, ti);
+ if( or.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti && imm32(ti.value()) )
+ return new OrIX86(or, (int)ti.value());
return new OrX86(or);
}
@@ -400,21 +458,21 @@ public class x86_64_v2 extends Machine {
return new ProjX86(prj);
}
- private Node sar( SarNode sar ) {
- if( sar.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti)
- return new SarIX86(sar, ti);
+ private Node sar(SarNode sar) {
+ if( sar.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti && imm8(ti.value()))
+ return new SarIX86(sar, (int)(ti.value() & 0xff) );
return new SarX86(sar);
}
- private Node shl( ShlNode shl ) {
- if( shl.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti )
- return new ShlIX86(shl, ti);
+ private Node shl(ShlNode shl) {
+ if( shl.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti && imm8(ti.value()))
+ return new ShlIX86(shl, (int)(ti.value() & 0xff) );
return new ShlX86(shl);
}
- private Node shr(ShrNode shr) {
- if( shr.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti )
- return new ShrIX86(shr, ti);
+ private Node shr( ShrNode shr ) {
+ if( shr.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti && imm8(ti.value()) )
+ return new ShrIX86(shr, (int)(ti.value() & 0xff) );
return new ShrX86(shr);
}
@@ -422,58 +480,58 @@ public class x86_64_v2 extends Machine {
// Look for "*ptr op= val"
Node op = st.val();
if( op instanceof AddNode ) {
- if( op.in(1) instanceof LoadNode ld &&
- ld.in(0)==st.in(0) &&
- ld.mem()==st.mem() &&
- ld.ptr()==st.ptr() &&
- ld.off()==st.off() ) {
- if( op instanceof AddNode ) {
- return new MemAddX86(address(st),st.ptr(),idx,off,scale,imm(op.in(2)),val);
- }
- throw Utils.TODO();
- }
+ if( op.in(1) instanceof LoadNode ld && stld_match(st,ld) )
+ return new MemAddX86(address(st), st.ptr(), idx, off, scale, imm(op.in(2)), val);
+ if( op.in(2) instanceof LoadNode ld && stld_match(st,ld) )
+ return new MemAddX86(address(st), st.ptr(), idx, off, scale, imm(op.in(1)), val);
}
-
- return new StoreX86(address(st),st.ptr(),idx,off,scale,imm(st.val()),val);
+ return new StoreX86(address(st), st.ptr(), idx, off, scale, imm(st.val()), val);
}
+ private static boolean stld_match(StoreNode st, LoadNode ld ) {
+ return
+ ld.in(0) == st.in(0) &&
+ ld.mem() == st.mem() &&
+ ld.ptr() == st.ptr() &&
+ ld.off() == st.off();
+ }
+
- private Node sub( SubNode sub ) {
+ private Node sub (SubNode sub ){
return sub.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti
- ? new AddIX86(sub, TypeInteger.constant(-ti.value()))
+ ? new AddIX86(sub, (int)-ti.value())
: new SubX86(sub);
}
- private Node xor(XorNode xor) {
- if(xor.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti)
- return new XorIX86(xor, ti);
- throw Utils.TODO();
+ private Node xor (XorNode xor){
+ if( xor.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti && imm32(ti.value()) )
+ return new XorIX86(xor, (int)ti.value());
+ return new XorX86(xor);
}
-
// Gather X86 addressing mode bits prior to constructing. This is a
// builder pattern, but saving the bits in a *local* *global* here to keep
// mess contained.
private static int off, scale, imm;
private static Node idx, val;
- private <N extends MemOpNode> N address( N mop ) {
+ private <N extends MemOpNode > N address(N mop) {
off = scale = imm = 0; // Reset
idx = val = null;
Node base = mop.ptr();
// Skip/throw-away a ReadOnly, only used to typecheck
- if( base instanceof ReadOnlyNode read ) base = read.in(1);
+ if(base instanceof ReadOnlyNode read) base = read.in(1);
assert !(base instanceof AddNode) && base._type instanceof TypeMemPtr; // Base ptr always, not some derived
- if( mop.off() instanceof AddNode add && add.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti ) {
- off = (int)ti.value();
+ if(mop.off() instanceof AddNode add && add.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti) {
+ off = (int) ti.value();
assert off == ti.value(); // In 32-bit range
idx = add.in(1);
- if( idx instanceof ShlNode shift && shift.in(2) instanceof ConstantNode shfcon &&
- shfcon._con instanceof TypeInteger tscale && 0 <= tscale.value() && tscale.value() <= 3 ) {
+ if(idx instanceof ShlNode shift && shift.in(2) instanceof ConstantNode shfcon &&
+ shfcon._con instanceof TypeInteger tscale && 0 <= tscale.value() && tscale.value() <= 3) {
idx = shift.in(1);
- scale = (int)tscale.value();
+ scale = (int) tscale.value();
}
} else {
- if( mop.off() instanceof ConstantNode con && con._con instanceof TypeInteger ti ) {
- off = (int)ti.value();
+ 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();
@@ -483,10 +541,10 @@ public class x86_64_v2 extends Machine {
}
private int imm( Node xval ) {
- assert val==null && imm==0;
- if( xval instanceof ConstantNode con && con._con instanceof TypeInteger ti ) {
+ assert val == null && imm == 0;
+ if( xval instanceof ConstantNode con && con._con instanceof TypeInteger ti) {
val = null;
- imm = (int)ti.value();
+ imm = (int) ti.value();
assert imm == ti.value(); // In 32-bit range
} else {
val = xval;
diff --git a/src/main/java/com/seaofnodes/simple/print/ASMPrinter.java b/src/main/java/com/seaofnodes/simple/print/ASMPrinter.java
index a4f768c5..09ebd51b 100644
--- a/src/main/java/com/seaofnodes/simple/print/ASMPrinter.java
+++ b/src/main/java/com/seaofnodes/simple/print/ASMPrinter.java
@@ -8,13 +8,16 @@ import com.seaofnodes.simple.SB;
import com.seaofnodes.simple.node.*;
import com.seaofnodes.simple.type.*;
import java.util.ArrayList;
+import com.seaofnodes.simple.codegen.Encoding;
/** Machine-specific facts; traversal, columns and byte display live in print/. */
public final class ASMPrinter extends AssemblyAdapter<Node> {
private final CodeGen _code;
+ private final Encoding _enc;
private ASMPrinter(CodeGen code) {
super(new IRPrinter());
_code=code;
+ _enc=code._encoding;
}
public static SB print(SB sb, CodeGen code) {
return sb.p(new com.seaofnodes.print.ASMPrinter<>(new ASMPrinter(code)).print());
@@ -50,4 +53,33 @@ public final class ASMPrinter extends AssemblyAdapter<Node> {
return n.getClass()==ConstantNode.class;
}
@Override public boolean postAlloc() { return _code._phase.ordinal()>CodeGen.Phase.RegAlloc.ordinal(); }
+ @Override public boolean encoded() { return _enc!=null && _code._phase.ordinal()>=CodeGen.Phase.Encoding.ordinal(); }
+ @Override public int defaultSize() { return _code._mach==null ? 2 : _code._mach.defaultOpSize(); }
+ @Override public boolean littleEndian() { return _code._asmLittle; }
+ @Override public boolean prologue(Node n) { return ((FunNode)n)._frameAdjust!=0; }
+ @Override public byte[] bytes() { return encoded() ? _enc._bits.buf() : null; }
+ @Override public int offset(Node n) {
+ return encoded() && _enc._opStart!=null && n._nid<_enc._opStart.length ? _enc._opStart[n._nid] : -1;
+ }
+ @Override public int size(Node n) {
+ return encoded() && _enc._opLen!=null && n._nid<_enc._opLen.length ? _enc._opLen[n._nid]&255 : 0;
+ }
+ private static ArrayList<Data> entries(Iterable<Encoding.Relo> relos) {
+ var data=new ArrayList<Data>();
+ for( Encoding.Relo r : relos ) {
+ int align=1<<r._align;
+ int size=align;
+ if( r._t instanceof TypeTuple t ) size=align*t._types.length;
+ data.add(new Data(r._t,r._t.str(),r._align,size));
+ }
+ return data;
+ }
+ @Override public ArrayList<Pool> pools(int codeEnd) {
+ var pools=new ArrayList<Pool>();
+ if( !encoded() ) return pools;
+ int base=(codeEnd+15)&-16;
+ if( !_enc._bigCons.isEmpty() && base<_enc._bits.size() )
+ pools.add(new Pool("Constant Pool",_enc._bits.buf(),base,base,entries(_enc._bigCons.values())));
+ return pools;
+ }
}
diff --git a/src/main/java/com/seaofnodes/simple/print/SimpleGraphAdapter.java b/src/main/java/com/seaofnodes/simple/print/SimpleGraphAdapter.java
index 0357ff61..b9aabba3 100644
--- a/src/main/java/com/seaofnodes/simple/print/SimpleGraphAdapter.java
+++ b/src/main/java/com/seaofnodes/simple/print/SimpleGraphAdapter.java
@@ -6,7 +6,7 @@ import com.seaofnodes.simple.node.*;
import com.seaofnodes.simple.node.Node;
import com.seaofnodes.simple.type.TypeMem;
-/** Chapter 20's view of the IR; browser, transport and layout are shared. */
+/** Chapter 21's view of the IR; browser, transport and layout are shared. */
public class SimpleGraphAdapter extends GraphAdapter<Node> {
@Override protected String edgeName(Node n, int i) {
diff --git a/src/main/java/com/seaofnodes/simple/print/SimpleGraphObserver.java b/src/main/java/com/seaofnodes/simple/print/SimpleGraphObserver.java
index 9dce0b75..d8d0e216 100644
--- a/src/main/java/com/seaofnodes/simple/print/SimpleGraphObserver.java
+++ b/src/main/java/com/seaofnodes/simple/print/SimpleGraphObserver.java
@@ -7,7 +7,7 @@ import com.seaofnodes.simple.node.Node;
import java.util.ArrayList;
import java.io.IOException;
-/** Chapter 20's compilation context; capture and event bookkeeping are shared. */
+/** Chapter 21's compilation context; capture and event bookkeeping are shared. */
public class SimpleGraphObserver extends GraphCapture<Node> {
private CodeGen _code;
public SimpleGraphObserver() { super(new SimpleGraphAdapter()); }
diff --git a/src/main/java/com/seaofnodes/simple/type/Type.java b/src/main/java/com/seaofnodes/simple/type/Type.java
index 1ce5dbd0..96b2c6ef 100644
--- a/src/main/java/com/seaofnodes/simple/type/Type.java
+++ b/src/main/java/com/seaofnodes/simple/type/Type.java
@@ -103,6 +103,7 @@ public class Type {
// checks with every type compare, only during interning.
// Factory method which interns "this"
+ @SuppressWarnings("unchecked")
public <T extends Type> T intern() {
T nnn = (T)INTERN.get(this);
if( nnn==null )
@@ -221,7 +222,7 @@ public class Type {
// Sizes are expected to be between 1 and 64 bits.
// Size 0 means this either takes no space (such as a known-zero field)
// or isn't a scalar to be stored in memory.
- public int log_size() { throw Utils.TODO(); }
+ public int log_size() { return 3; }
// ----------------------------------------------------------
// Useful in the debugger, which calls toString everywhere.
diff --git a/src/main/java/com/seaofnodes/simple/type/TypeFunPtr.java b/src/main/java/com/seaofnodes/simple/type/TypeFunPtr.java
index 609f2443..1bd73732 100644
--- a/src/main/java/com/seaofnodes/simple/type/TypeFunPtr.java
+++ b/src/main/java/com/seaofnodes/simple/type/TypeFunPtr.java
@@ -44,6 +44,7 @@ public class TypeFunPtr extends TypeNil {
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 TypeFunPtr MAIN = make((byte)3,TypeTuple.MAIN,TypeInteger.BOT,-1);
+ public static TypeFunPtr CALLOC= make((byte)3,TypeTuple.CALLOC,TypeMemPtr.BOT,-1);
public static void gather(ArrayList<Type> ts) { ts.add(TEST); ts.add(TEST0); ts.add(BOT); ts.add(MAIN); }
@Override
diff --git a/src/main/java/com/seaofnodes/simple/type/TypeMemPtr.java b/src/main/java/com/seaofnodes/simple/type/TypeMemPtr.java
index 61b020cb..98e92bca 100644
--- a/src/main/java/com/seaofnodes/simple/type/TypeMemPtr.java
+++ b/src/main/java/com/seaofnodes/simple/type/TypeMemPtr.java
@@ -98,7 +98,7 @@ public class TypeMemPtr extends TypeNil {
// Is forward-reference
@Override public boolean isFRef() { return _obj.isFRef(); }
- @Override public int log_size() { return 2; } // (1<<2)==4-byte pointers
+ @Override public int log_size() { return 3; } // (1<<3)==8-byte pointers
@Override int hash() { return (_ro ? 8192 : 0) ^ _obj.hashCode() ^ 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 dc92e086..990c5fcf 100644
--- a/src/main/java/com/seaofnodes/simple/type/TypeStruct.java
+++ b/src/main/java/com/seaofnodes/simple/type/TypeStruct.java
@@ -184,9 +184,12 @@ public class TypeStruct extends Type {
public SB print(SB sb) {
if( this==BOT ) return sb.p("StructBot");
if( this==TOP ) return sb.p("StructTop");
+ if( _fields == null ) return sb.p(_name); // Forward reference struct, just print the name
+ if( isAry() ) {
+ if( !isFinal() ) sb.p("!");
+ return sb.p(_name); // Skip printing generic array fields
+ }
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 95e35e99..d3b19b52 100644
--- a/src/main/java/com/seaofnodes/simple/type/TypeTuple.java
+++ b/src/main/java/com/seaofnodes/simple/type/TypeTuple.java
@@ -17,6 +17,7 @@ public class TypeTuple extends Type {
public static final TypeTuple START= make(Type.CONTROL,TypeMem.TOP,TypeInteger.BOT);
public static final TypeTuple MAIN = make(TypeInteger.BOT);
public static final TypeTuple RET = make(Type.CONTROL,TypeMem.BOT,Type.BOTTOM);
+ public static final TypeTuple CALLOC = make(TypeInteger.BOT,TypeInteger.BOT);
public static final TypeTuple IF_BOTH = make(new Type[]{Type. CONTROL,Type. CONTROL});
public static final TypeTuple IF_NEITHER = make(new Type[]{Type.XCONTROL,Type.XCONTROL});
@@ -55,6 +56,21 @@ public class TypeTuple extends Type {
return make(ts);
}
+ @Override public boolean isConstant() {
+ for( Type t : _types )
+ if( !t.isConstant() )
+ return false;
+ return true;
+ }
+
+ @Override public int log_size() {
+ assert isConstant();
+ int log_size = 0;
+ for( Type t : _types )
+ log_size = Math.max(log_size,t.log_size());
+ return log_size;
+ }
+
public Type ret() { assert _types.length==3; return _types[2]; }
@Override public String str() { return print(new SB()).toString(); }