summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-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.java9
-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.java381
-rw-r--r--src/main/java/com/seaofnodes/simple/codegen/Encoding.java473
-rw-r--r--src/main/java/com/seaofnodes/simple/codegen/GlobalCodeMotion.java94
-rw-r--r--src/main/java/com/seaofnodes/simple/codegen/IFG.java146
-rw-r--r--src/main/java/com/seaofnodes/simple/codegen/LRG.java30
-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.java283
-rw-r--r--src/main/java/com/seaofnodes/simple/codegen/RegMask.java38
-rw-r--r--src/main/java/com/seaofnodes/simple/codegen/RegMaskRW.java36
-rw-r--r--src/main/java/com/seaofnodes/simple/node/AddFNode.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/AddNode.java4
-rw-r--r--src/main/java/com/seaofnodes/simple/node/BoolNode.java14
-rw-r--r--src/main/java/com/seaofnodes/simple/node/CFGNode.java27
-rw-r--r--src/main/java/com/seaofnodes/simple/node/CProjNode.java16
-rw-r--r--src/main/java/com/seaofnodes/simple/node/CallEndNode.java46
-rw-r--r--src/main/java/com/seaofnodes/simple/node/CallNode.java17
-rw-r--r--src/main/java/com/seaofnodes/simple/node/CalleeSaveNode.java6
-rw-r--r--src/main/java/com/seaofnodes/simple/node/CastNode.java4
-rw-r--r--src/main/java/com/seaofnodes/simple/node/ConstantNode.java8
-rw-r--r--src/main/java/com/seaofnodes/simple/node/CtrlNode.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/DivFNode.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/DivNode.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/FRefNode.java4
-rw-r--r--src/main/java/com/seaofnodes/simple/node/FunNode.java61
-rw-r--r--src/main/java/com/seaofnodes/simple/node/IfNode.java38
-rw-r--r--src/main/java/com/seaofnodes/simple/node/LoadNode.java10
-rw-r--r--src/main/java/com/seaofnodes/simple/node/LogicalNode.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/LoopNode.java51
-rw-r--r--src/main/java/com/seaofnodes/simple/node/MachNode.java11
-rw-r--r--src/main/java/com/seaofnodes/simple/node/MemMergeNode.java13
-rw-r--r--src/main/java/com/seaofnodes/simple/node/MemOpNode.java48
-rw-r--r--src/main/java/com/seaofnodes/simple/node/MinusFNode.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/MinusNode.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/MulFNode.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/MulNode.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/NeverNode.java3
-rw-r--r--src/main/java/com/seaofnodes/simple/node/NewNode.java42
-rw-r--r--src/main/java/com/seaofnodes/simple/node/Node.java77
-rw-r--r--src/main/java/com/seaofnodes/simple/node/NotNode.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/ParmNode.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/PhiNode.java91
-rw-r--r--src/main/java/com/seaofnodes/simple/node/ProjNode.java4
-rw-r--r--src/main/java/com/seaofnodes/simple/node/ReadOnlyNode.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/RegionNode.java54
-rw-r--r--src/main/java/com/seaofnodes/simple/node/ReturnNode.java42
-rw-r--r--src/main/java/com/seaofnodes/simple/node/RoundF32Node.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/ScopeNode.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/ShlNode.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/SplitNode.java5
-rw-r--r--src/main/java/com/seaofnodes/simple/node/StartNode.java4
-rw-r--r--src/main/java/com/seaofnodes/simple/node/StopNode.java5
-rw-r--r--src/main/java/com/seaofnodes/simple/node/StoreNode.java13
-rw-r--r--src/main/java/com/seaofnodes/simple/node/StructNode.java4
-rw-r--r--src/main/java/com/seaofnodes/simple/node/SubFNode.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/SubNode.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/ToFloatNode.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/XCtrlNode.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/AddARM.java28
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/AddFARM.java23
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/AddIARM.java52
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/AndARM.java33
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/AndIARM.java56
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/AsrARM.java32
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/AsrIARM.java54
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/BranchARM.java62
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/CallARM.java34
-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.java18
-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.java18
-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.java34
-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.java26
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/DivFARM.java21
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/FloatARM.java33
-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.java26
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/IntARM.java47
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/LoadARM.java30
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/LslARM.java32
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/LslIARM.java57
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/LsrARM.java31
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/LsrIARM.java52
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/MemOpARM.java55
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/MulARM.java27
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/MulFARM.java20
-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.java43
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/NotARM.java26
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/OrARM.java38
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/OrIARM.java48
-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.java48
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/SetARM.java21
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/SplitARM.java62
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/StoreARM.java49
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/SubARM.java25
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/SubFARM.java26
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/SubIARM.java31
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/TFPARM.java46
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/UJmpARM.java48
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/XorARM.java40
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/XorIARM.java50
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/arm/arm.java809
-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.java41
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/AddFRISC.java25
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/AddIRISC.java16
-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.java7
-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.java77
-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.java44
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/CallRRISC.java24
-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.java21
-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/FltRISC.java50
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/FunRISC.java23
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/I2F8RISC.java23
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/ImmRISC.java46
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/IntRISC.java29
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/LUI.java32
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/LoadRISC.java33
-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.java25
-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.java20
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/NewRISC.java49
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/NotRISC.java27
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/OrIRISC.java5
-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.java47
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/SetFRISC.java24
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/SetIRISC.java20
-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.java5
-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.java61
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/SraIRISC.java5
-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.java5
-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.java43
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/SubFRISC.java25
-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.java58
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/UJmpRISC.java46
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/riscv/XorIRISC.java5
-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.java516
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddFMemX86.java23
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/AddFX86.java30
-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.java26
-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.java25
-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.java25
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CmpIX86.java49
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CmpMemX86.java71
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CmpX86.java27
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/DivFX86.java37
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/DivX86.java28
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/FltX86.java54
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/FunX86.java32
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/I2f8X86.java29
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ImmX86.java37
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/IntX86.java59
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/JmpX86.java76
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/LeaX86.java41
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/LoadX86.java45
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MemAddX86.java25
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MemOpX86.java23
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MulFX86.java30
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MulIX86.java40
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MulX86.java24
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/NegX86.java21
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/NewX86.java28
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/NotX86.java39
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/OrIX86.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.java26
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/RetX86.java52
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SarIX86.java47
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SarX86.java49
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SetX86.java40
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShlIX86.java54
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShlX86.java47
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShrIX86.java50
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ShrX86.java48
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SplitX86.java102
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/StoreX86.java61
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SubFX86.java28
-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.java58
-rw-r--r--src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/UJmpX86.java53
-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.java709
-rw-r--r--src/main/java/com/seaofnodes/simple/print/ASMPrinter.java229
-rw-r--r--src/main/java/com/seaofnodes/simple/print/IRPrinter.java7
-rw-r--r--src/main/java/com/seaofnodes/simple/print/JSViewer.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
-rw-r--r--src/test/java/com/seaofnodes/simple/BrainFuckTest.java81
-rw-r--r--src/test/java/com/seaofnodes/simple/Chapter05Test.java2
-rw-r--r--src/test/java/com/seaofnodes/simple/Chapter06Test.java4
-rw-r--r--src/test/java/com/seaofnodes/simple/Chapter08Test.java4
-rw-r--r--src/test/java/com/seaofnodes/simple/Chapter11Test.java4
-rw-r--r--src/test/java/com/seaofnodes/simple/Chapter13Test.java2
-rw-r--r--src/test/java/com/seaofnodes/simple/Chapter14Test.java2
-rw-r--r--src/test/java/com/seaofnodes/simple/Chapter15Test.java49
-rw-r--r--src/test/java/com/seaofnodes/simple/Chapter17Test.java2
-rw-r--r--src/test/java/com/seaofnodes/simple/Chapter18Test.java22
-rw-r--r--src/test/java/com/seaofnodes/simple/Chapter19Test.java189
-rw-r--r--src/test/java/com/seaofnodes/simple/Chapter20Test.java115
-rw-r--r--src/test/java/com/seaofnodes/simple/Chapter21Test.java276
-rw-r--r--src/test/java/com/seaofnodes/simple/Compile.java65
-rw-r--r--src/test/java/com/seaofnodes/simple/EvalArm64.java751
-rw-r--r--src/test/java/com/seaofnodes/simple/EvalRisc5.java353
-rw-r--r--src/test/java/com/seaofnodes/simple/Main.java1
-rw-r--r--src/test/java/com/seaofnodes/simple/MergeSortTest.java89
-rw-r--r--src/test/java/com/seaofnodes/simple/Simple.java313
-rw-r--r--src/test/java/com/seaofnodes/simple/TestArm64.java42
-rw-r--r--src/test/java/com/seaofnodes/simple/TestC.java89
-rw-r--r--src/test/java/com/seaofnodes/simple/TestRisc5.java50
-rw-r--r--src/test/java/com/seaofnodes/simple/evaluator/Scheduler.java16
-rw-r--r--src/test/java/com/seaofnodes/simple/fuzzer/FuzzerUtils.java2
-rw-r--r--src/test/java/com/seaofnodes/simple/progs/antiDep1.smp11
-rw-r--r--src/test/java/com/seaofnodes/simple/progs/arg_count.c10
-rw-r--r--src/test/java/com/seaofnodes/simple/progs/arg_count.smp6
-rw-r--r--src/test/java/com/seaofnodes/simple/progs/array1.smp9
-rw-r--r--src/test/java/com/seaofnodes/simple/progs/brain_fuck.c18
-rw-r--r--src/test/java/com/seaofnodes/simple/progs/brain_fuck.smp46
-rw-r--r--src/test/java/com/seaofnodes/simple/progs/fcn.smp15
-rw-r--r--src/test/java/com/seaofnodes/simple/progs/fcn2.smp5
-rw-r--r--src/test/java/com/seaofnodes/simple/progs/fcn3.smp42
-rw-r--r--src/test/java/com/seaofnodes/simple/progs/fib.c16
-rw-r--r--src/test/java/com/seaofnodes/simple/progs/fib.smp11
-rw-r--r--src/test/java/com/seaofnodes/simple/progs/jig.smp2
-rw-r--r--src/test/java/com/seaofnodes/simple/progs/loop2.c7
-rw-r--r--src/test/java/com/seaofnodes/simple/progs/loop2.smp10
-rw-r--r--src/test/java/com/seaofnodes/simple/progs/newtonFloat.c16
-rw-r--r--src/test/java/com/seaofnodes/simple/progs/newtonFloat.smp12
-rw-r--r--src/test/java/com/seaofnodes/simple/progs/no_stack_arg_count.smp6
-rw-r--r--src/test/java/com/seaofnodes/simple/progs/person.c34
-rw-r--r--src/test/java/com/seaofnodes/simple/progs/person.smp9
-rw-r--r--src/test/java/com/seaofnodes/simple/progs/sieve.c19
-rw-r--r--src/test/java/com/seaofnodes/simple/progs/sieve.smp61
-rw-r--r--src/test/java/com/seaofnodes/simple/progs/sort.c28
-rw-r--r--src/test/java/com/seaofnodes/simple/progs/sort.smp38
-rw-r--r--src/test/java/com/seaofnodes/simple/progs/stringHash.c30
-rw-r--r--src/test/java/com/seaofnodes/simple/progs/stringHash.smp31
279 files changed, 9015 insertions, 4360 deletions
diff --git a/src/main/java/com/seaofnodes/simple/IterPeeps.java b/src/main/java/com/seaofnodes/simple/IterPeeps.java
index 6208209f..66e8a4a8 100644
--- a/src/main/java/com/seaofnodes/simple/IterPeeps.java
+++ b/src/main/java/com/seaofnodes/simple/IterPeeps.java
@@ -49,6 +49,7 @@ public class IterPeeps {
public IterPeeps( long seed ) { _work = new WorkList<>(seed); }
+ @SuppressWarnings("unchecked")
public <N extends Node> N add( N n ) { return (N)_work.push(n); }
public void addAll( Ary<Node> ary ) { _work.addAll(ary); }
@@ -126,6 +127,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 07f82625..219c3c44 100644
--- a/src/main/java/com/seaofnodes/simple/Parser.java
+++ b/src/main/java/com/seaofnodes/simple/Parser.java
@@ -144,7 +144,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");
@@ -154,7 +172,7 @@ public class Parser {
for( StructNode init : INITS.values() )
init.unkeep().kill();
INITS.clear();
- _code._stop.peephole();
+ stop.peephole();
if( show ) showGraph();
}
@@ -1407,6 +1425,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());
// Memory from CallEnd
@@ -1669,6 +1688,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 88b49863..e9b5eb2e 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.TypeMem;
@@ -27,7 +26,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++ )
@@ -55,8 +54,8 @@ 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);
- if( !lrg2.machUse(mach,(short)i,use_mask.size1()).and(use_mask) )
+ 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
}
}
@@ -64,7 +63,7 @@ abstract public class BuildLRG {
}
// MultiNodes have projections which set registers
- if( n instanceof MultiNode )
+ if( n instanceof MultiNode && !(n instanceof CFGNode) )
for( Node proj : n.outs() )
if( proj instanceof MachNode )
defLRG(alloc,proj);
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 a1294890..9ec3bc7b 100644
--- a/src/main/java/com/seaofnodes/simple/codegen/CodeGen.java
+++ b/src/main/java/com/seaofnodes/simple/codegen/CodeGen.java
@@ -4,11 +4,14 @@ 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 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;
@@ -17,11 +20,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;
@@ -30,7 +35,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 ) {
@@ -46,6 +50,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
@@ -62,6 +87,24 @@ public class CodeGen {
public int getALIAS() { return _alias++; }
+ // idepths are cached and valid until *inserting* CFG edges (deleting is
+ // OK). This happens with inlining, which bumps the version to bulk
+ // invalidate the idepth caches.
+ private int _iDepthVersion = 0;
+ public void invalidateIDepthCaches() { _iDepthVersion++; }
+ public boolean validIDepth(int idepth) {
+ if( idepth==0 ) return false;
+ if( _iDepthVersion==0 ) return true;
+ return (idepth%100)==_iDepthVersion;
+ }
+ public int iDepthAt(int idepth) {
+ return 100*idepth+_iDepthVersion;
+ }
+ public int iDepthFrom(int idepth) {
+ assert idepth==0 || validIDepth(idepth);
+ return idepth+100;
+ }
+
// Popular visit bitset, declared here, so it gets reused all over
public final BitSet _visit = new BitSet();
public BitSet visit() { assert _visit.isEmpty(); return _visit; }
@@ -154,7 +197,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.
@@ -169,17 +212,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;
@@ -188,9 +256,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
@@ -226,7 +315,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();
@@ -246,8 +335,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
@@ -257,8 +346,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);
if( show )
@@ -268,7 +355,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;
@@ -281,7 +368,7 @@ public class CodeGen {
// ---------------------------
// Register Allocation
- int _tRegAlloc;
+ public int _tRegAlloc;
public RegAlloc _regAlloc;
public CodeGen regAlloc() {
assert _phase == Phase.LocalSched;
@@ -292,31 +379,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(); }
@@ -335,4 +437,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..746ecfed
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/codegen/ElfFile.java
@@ -0,0 +1,381 @@
+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];
+
+ 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..c35ea6a4
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/codegen/Encoding.java
@@ -0,0 +1,473 @@
+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 {
+
+ // 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 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;
+ }
+ }
+ 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 || 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 11c2b061..40f827ec 100644
--- a/src/main/java/com/seaofnodes/simple/codegen/GlobalCodeMotion.java
+++ b/src/main/java/com/seaofnodes/simple/codegen/GlobalCodeMotion.java
@@ -24,7 +24,7 @@ public abstract class GlobalCodeMotion {
schedEarly(code);
// Break up shared global constants by functions
- breakUpGlobalConstants(code);
+ breakUpGlobalConstants(code._start);
code._visit.clear();
schedLate (code);
@@ -45,23 +45,28 @@ public abstract class GlobalCodeMotion {
}
// Break up shared global constants by functions
- private static void breakUpGlobalConstants( CodeGen code ) {
+ private static void breakUpGlobalConstants( Node start ) {
// For all global constants
- for( int i=0; i< code._start.nOuts(); i++ ) {
- Node con = code._start.out(i);
+ for( int i=0; i< start.nOuts(); i++ ) {
+ Node con = start.out(i);
if( con instanceof MachNode mach && mach.isClone() ) {
+ breakUpGlobalConstants(con);
// While constant has users in different functions
while( true ) {
// Find a function user, and another function
FunNode fun = null;
boolean done=true;
for( Node use : con.outs() ) {
- FunNode fun2 = use.cfg0().fun();
+ FunNode fun2 = use instanceof ReturnNode ret ? ret.fun() : use.cfg0().fun();
if( fun==null || fun==fun2 ) fun=fun2;
else { done=false; break; }
}
// Single function user, so this constant is not shared
- if( done ) { i--; con.setDef(0,fun); break; }
+ if( done ) {
+ if( con.in(0)==start ) i--;
+ con.setDef(0,fun);
+ break;
+ }
// Move function users to a private constant
Node con2 = mach.copy(); // Private constant clone
con2._inputs.set(0,null); // Preserve edge invariants from clone
@@ -155,13 +160,15 @@ public abstract class GlobalCodeMotion {
continue outer; // Nope, await all uses done
// Loads need their memory inputs' uses also done
- if( n instanceof LoadNode ld )
+ if( n instanceof MemOpNode ld && ld._isLoad )
for( Node memuse : ld.mem()._outputs )
if( late[memuse._nid]==null &&
// New makes new memory, never crushes load memory
!(memuse instanceof NewNode) &&
// Load-use directly defines memory
(memuse._type instanceof TypeMem ||
+ // Load-use directly defines memory
+ memuse instanceof CallNode ||
// Load-use indirectly defines memory
(memuse._type instanceof TypeTuple tt && tt._types[ld._alias] instanceof TypeMem)) )
continue outer;
@@ -175,8 +182,8 @@ public abstract class GlobalCodeMotion {
if( def!=null && late[def._nid]==null ) {
work.push(def);
// if the def has a load use, maybe the load can fire
- for( Node ld : def._outputs )
- if( ld instanceof LoadNode && late[ld._nid]==null )
+ for( Node out : def._outputs )
+ if( out instanceof MemOpNode ld && ld._isLoad && late[ld._nid]==null )
work.push(ld);
}
}
@@ -192,9 +199,36 @@ public abstract class GlobalCodeMotion {
lca = use_block(n,use, late)._idom(lca,null);
// Loads may need anti-dependencies, raising their LCA
- if( n instanceof LoadNode load )
+ if( n instanceof MemOpNode load && load._isLoad )
lca = find_anti_dep(lca,load,early,late);
+
+ // Nodes setting a single register and getting killed will stay close
+ // to the uses, since they will be forced to spill anyway. The kill
+ // check is very weak, and some may be hoisted only to spill in the RA.
+ if( n instanceof MachNode mach ) {
+ RegMask out = mach.outregmap();
+ if( out!=null && out.size1() ) {
+ int reg = mach.outregmap().firstReg();
+ // Look for kills
+ outer:
+ for( CFGNode lca2=lca; lca2 != early; lca2 = lca2.idom() ) {
+ if( lca2 instanceof MachNode mach2 ) {
+ for( int i=1; i<lca2.nIns(); i++ ) {
+ RegMask mask = mach2.regmap(i);
+ if( mask!=null && mask.test(reg) ) {
+ early = lca2 instanceof IfNode ? lca2.idom() : lca2;
+ break outer;
+ }
+ }
+ RegMask kill = mach2.killmap();
+ if( kill != null )
+ throw Utils.TODO();
+ }
+ }
+ }
+ }
+
// Walk up from the LCA to the early, looking for best place. This is
// the lowest execution frequency, approximated by least loop depth and
// deepest control flow.
@@ -226,10 +260,12 @@ public abstract class GlobalCodeMotion {
// Least loop depth first, then largest idepth
private static boolean better( CFGNode lca, CFGNode best ) {
return lca.loopDepth() < best.loopDepth() ||
- (lca.idepth() > best.idepth() || best instanceof IfNode);
+ lca instanceof NeverNode ||
+ lca.idepth() > best.idepth() ||
+ best instanceof IfNode;
}
- private static CFGNode find_anti_dep(CFGNode lca, LoadNode load, CFGNode early, CFGNode[] late) {
+ private static CFGNode find_anti_dep(CFGNode lca, MemOpNode load, CFGNode early, CFGNode[] late) {
// We could skip final-field loads here.
// Walk LCA->early, flagging Load's block location choices
for( CFGNode cfg=lca; early!=null && cfg!=early.idom(); cfg = cfg.idom() )
@@ -237,13 +273,15 @@ public abstract class GlobalCodeMotion {
// Walk load->mem uses, looking for Stores causing an anti-dep
for( Node mem : load.mem()._outputs ) {
switch( mem ) {
- case StoreNode st:
- assert late[st._nid]!=null;
- lca = anti_dep(load,late[st._nid],st.cfg0(),lca,st);
- break;
- case CallNode st:
- assert late[st._nid]!=null;
- lca = anti_dep(load,late[st._nid],st.cfg0(),lca,st);
+ case MemOpNode st:
+ if( !st._isLoad ) {
+ assert late[mem._nid] != null;
+ lca = anti_dep( load, late[mem._nid], mem.cfg0(), lca, st );
+ }
+ break; // Loads do not cause anti-deps on other loads
+ case CallNode call:
+ assert late[call._nid]!=null;
+ lca = anti_dep(load,late[call._nid],call.cfg0(),lca,call);
break;
case PhiNode phi:
// Repeat anti-dep for matching Phi inputs.
@@ -253,7 +291,6 @@ public abstract class GlobalCodeMotion {
lca = anti_dep(load,phi.region().cfg(i),load.mem().cfg0(),lca,null);
break;
case NewNode st: break;
- case LoadNode ld: break; // Loads do not cause anti-deps on other loads
case ReturnNode ret: break; // Load must already be ahead of Return
case MemMergeNode ret: break; // Mem uses now on ScopeMin
case NeverNode never: break;
@@ -264,16 +301,13 @@ public abstract class GlobalCodeMotion {
}
//
- private static CFGNode anti_dep( LoadNode load, CFGNode stblk, CFGNode defblk, CFGNode lca, Node st ) {
- // Walk store blocks "reach" from its scheduled location to its earliest
- for( ; stblk != defblk.idom(); stblk = stblk.idom() ) {
- // Store and Load overlap, need anti-dependence
- if( stblk._anti==load._nid ) {
- lca = stblk._idom(lca,null); // Raise Loads LCA
- if( lca == stblk && st != null && st._inputs.find(load) == -1 ) // And if something moved,
- st.addDef(load); // Add anti-dep as well
- return lca; // Cap this stores' anti-dep to here
- }
+ private static CFGNode anti_dep( MemOpNode load, CFGNode stblk, CFGNode defblk, CFGNode lca, Node st ) {
+ // Store and Load overlap, need anti-dependence
+ if( stblk._anti==load._nid ) {
+ lca = stblk._idom(lca,null); // Raise Loads LCA
+ if( lca == stblk && st != null && st._inputs.find(load) == -1 ) // And if something moved,
+ st.addDef(load); // Add anti-dep as well
+ return lca; // Cap this stores' anti-dep to here
}
return lca;
}
diff --git a/src/main/java/com/seaofnodes/simple/codegen/IFG.java b/src/main/java/com/seaofnodes/simple/codegen/IFG.java
index 1f3186ff..fdd4e2f9 100644
--- a/src/main/java/com/seaofnodes/simple/codegen/IFG.java
+++ b/src/main/java/com/seaofnodes/simple/codegen/IFG.java
@@ -19,7 +19,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();
@@ -79,6 +79,8 @@ abstract public class IFG {
while( !WORK.isEmpty() )
do_block(round,alloc,WORK.pop());
+ if( alloc.success() )
+ convert2DAdjacency(alloc);
return alloc.success();
}
@@ -125,18 +127,18 @@ 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;
- // A copy does not define a new value, and the src and dst can use the
- // same register. Remove the input from TMP/liveout set before
- // interfering.
- //if( n instanceof MachNode m && m.isSplit() )
- // TMP.remove(alloc.lrg(n.in(1))); // Kill spill-use
+
+ // 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 ) {
- if( n instanceof MachNode m )
- kills(alloc,m);
// Interfere n with all live
for( LRG tlrg : TMP.keySet() ) {
assert tlrg.leader();
@@ -148,9 +150,10 @@ abstract public class IFG {
// last tlrg register at some point, either tlrg or lrg
// must fail. If *n* (a subset of lrg) needs the single
// last tlrg register then only tlrg must fail.
- if( lrg.size1() && !tlrg.clr(lrg._mask.firstReg()) )
- alloc.fail(tlrg);
- else addIFG(lrg,tlrg); // Add interference
+ if( ((MachNode)n).outregmap().size1() ) {
+ if( !tlrg.clr(lrg._mask.firstReg()) ) // Clear bit, no interference
+ alloc.fail(tlrg); // Clearing drives mask to empty
+ } else addIFG(lrg,tlrg); // Add interference
}
}
@@ -171,7 +174,7 @@ abstract public class IFG {
// Look for a must-use single register conflicting with some other must-def.
if( n instanceof MachNode m ) {
RegMask ni_mask = m.regmap(i);
- if( ni_mask.size1() ) { // Must-use single register
+ if( ni_mask!=null && ni_mask.size1() ) { // Must-use single register
// Search all current live
for( LRG tlrg : TMP.keySet() ) {
assert tlrg.leader();
@@ -207,12 +210,17 @@ abstract public class IFG {
// Disallow clone-ables from killing registers. Just fail
// them and re-clone closer to target... so no kill.
// Special case for Intel XOR used to zero.
- if( m.isClone() )
+ Node n = (Node)m;
+ CFGNode effUseBlk = n.out(0) instanceof PhiNode phi ? phi.region().cfg(phi._inputs.find(n)) : n.out(0).cfg0();
+ if( m.isClone() && // Must be clonable
+ (n.nOuts()>1 || // Has many users OR
+ // Only 1 user but effective use is remote block
+ effUseBlk != n.cfg0() ))
+ // Then fail the clonable; it should split or move
alloc.fail(alloc.lrg((Node)m));
- else if( !tlrg.sub(killMask) ) {
- tlrg._killed = true; // Failed by a kill-mask
+ // Else clonable cannot move
+ else if( !tlrg.sub(killMask) )
alloc.fail(tlrg);
- }
}
}
}
@@ -234,14 +242,12 @@ abstract public class IFG {
private static void mergeLiveOut( RegAlloc alloc, CFGNode priorbb, int i ) {
CFGNode bb = priorbb.cfg(i);
if( bb == null ) return; // Start has no prior
- if( !bb.blockHead() ) bb = bb.cfg0();
- //if( i==0 && !(bb instanceof StartNode) ) bb = bb.cfg0();
- assert bb.blockHead();
+ while( !bb.blockHead() ) bb = bb.cfg0();
// 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 ) {
@@ -260,6 +266,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.
@@ -276,22 +300,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
@@ -407,7 +419,7 @@ abstract public class IFG {
int best=sptr;
int bestScore = pickRiskyScore(color_stack[best]);
for( int i=sptr+1; i<color_stack.length; i++ ) {
- if( bestScore == 999999 ) return best; // Already max score
+ if( bestScore == 1000000 ) return best; // Already max score
int iScore = pickRiskyScore(color_stack[i]);
if( iScore > bestScore )
{ best = i; bestScore = iScore; }
@@ -424,13 +436,25 @@ abstract public class IFG {
// Picking a live range that is very close to coloring might allow it to
// color despite being risky.
private static int pickRiskyScore( LRG lrg ) {
+ // Pick single-def clonables that are not right next to their single-use.
+ // Failing to color these will clone them closer to their uses.
+ if( !lrg._multiDef && lrg._machDef.isClone() ) {
+ Node def = ((Node)lrg._machDef);
+ Node use = ((Node)lrg._machUse);
+ CFGNode cfg = def.cfg0();
+ if( cfg != use.cfg0() || // Different blocks OR
+ // Same block, but not close
+ cfg._outputs.find(def) < cfg._outputs.find(use)+1 )
+ return 1000000;
+ }
+
// Always pick callee-save registers as being very large area recovered
// and very cheap to spill.
if( lrg._machDef instanceof CalleeSaveNode )
- return 999998;
- if( lrg._splitDef != null && lrg._splitDef.in(1) instanceof CalleeSaveNode &&
+ return 1000000-2-lrg._mask.firstReg();
+ if( lrg._splitDef != null && lrg._splitDef. in(1) instanceof CalleeSaveNode &&
lrg._splitUse != null && lrg._splitUse.out(0) instanceof ReturnNode )
- return 999999;
+ return 1000000-1;
// TODO: cost/benefit model. Perhaps counting loop-depth (freq) of def/use for cost
// and "area" for benefit
@@ -441,35 +465,39 @@ abstract public class IFG {
if( mask.size1() ) return reg;
// Check chain of splits up the def-chain. Take first allocated
// register, and if it's available in the mask, take it.
- Node defSplit = lrg._splitDef, useSplit = lrg._splitUse;
- int tidx, cnt=0;
+ Node def = lrg._splitDef, use = lrg._splitUse;
+ int tidx=0, cnt=0;
- while( (tidx=biasable(defSplit)) != 0 || biasable(useSplit) != 0 ) {
+ while( def != null || use != null ) {
if( cnt++ > 10 ) break;
- if( tidx != 0 ) {
- short bias = biasColor( alloc, defSplit, mask );
+ if( def != null ) {
+ short bias = biasColor( alloc, def, mask );
if( bias >= 0 ) return bias; // Good bias
- if( bias == -2 ) defSplit = null; // Kill this side, no more searching
- } else defSplit = null;
+ if( bias == -2 ) def = null; // Kill this side, no more searching
+ else if( (tidx=biasable(def)) == 0 ) def = null;
+ }
- if( biasable(useSplit) != 0 ) {
- short bias = biasColor( alloc, useSplit, mask );
+ if( use != null ) {
+ short bias = biasColor( alloc, use, mask );
if( bias >= 0 ) return bias; // Good bias
- if( bias == -2 ) useSplit = null; // Kill this side, no more searching
- } else useSplit = null;
+ if( bias == -2 ) use = null; // Kill this side, no more searching
+ else if( biasable(use)==0 ) use = null;
+ }
- if( defSplit != null ) {
- short bias = biasColorNeighbors( alloc, defSplit, mask );
+ if( def != null ) {
+ short bias = biasColorNeighbors( alloc, def, mask );
if( bias >= 0 ) return bias;
// Advance def side
- defSplit = defSplit.in(tidx);
+ def = def.in(tidx);
+ if( alloc.lrg(def)==null ) def=null;
}
- if( useSplit != null ) {
- short bias = biasColorNeighbors( alloc, useSplit, mask );
+ if( use != null ) {
+ short bias = biasColorNeighbors( alloc, use, mask );
if( bias >= 0 ) return bias;
- useSplit = useSplit.out(0);
+ use = use.out(0);
+ if( biasable(use)==0 ) use=null;
}
}
@@ -478,7 +506,7 @@ abstract public class IFG {
private static int biasable(Node split) {
if( split instanceof SplitNode ) return 1; // Yes biasable, advance is slot 1
- if( split instanceof PhiNode ) return 1; // Yes biasable, advance is slot 1
+ if( split instanceof PhiNode phi ) return phi.region() instanceof LoopNode ? 2 : 1; // Yes biasable, advance is slot 1
if( !(split instanceof MachNode mach) ) return 0; // Not biasable
return mach.twoAddress(); // Only biasable if 2-addr
}
@@ -503,7 +531,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 394c4a01..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,16 +38,13 @@ 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;
short _uidx; // _machUse input
- // Mask set to empty via a kill-mask. This is usually a capacity kill,
- // which means we need to split and spill into memory - which means even if
- // the def-side has many registers, it MUST spill.
- boolean _killed;
-
// Some splits used in biased coloring
MachConcreteNode _splitDef, _splitUse;
@@ -59,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);
@@ -77,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; }
@@ -121,29 +121,39 @@ 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( _1regUseCnt==0 )
+ if( _machUse != lrg._machUse ) _multiUse=true;
+ if( _1regUseCnt==0 ) {
_machUse = lrg._machUse;
+ _uidx = lrg._uidx;
+ }
else if( _machUse==lrg._machUse )
_1regUseCnt--;
}
_1regUseCnt += lrg._1regUseCnt;
+ _multiUse |= lrg._multiUse;
// Fold deepest Split
_splitDef = deepSplit(_splitDef,lrg._splitDef);
_splitUse = deepSplit(_splitUse,lrg._splitUse);
// Fold together masks
- _mask = _mask.and(lrg._mask);
+ RegMask mask = _mask.and(lrg._mask);
+ if( mask==null )
+ mask = _mask.copy().and(lrg._mask);
+ _mask = mask;
return this;
}
@@ -153,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 )
@@ -164,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 badec740..bd4cbf35 100644
--- a/src/main/java/com/seaofnodes/simple/codegen/RegAlloc.java
+++ b/src/main/java/com/seaofnodes/simple/codegen/RegAlloc.java
@@ -4,6 +4,7 @@ import com.seaofnodes.simple.Ary;
import com.seaofnodes.simple.Utils;
import com.seaofnodes.simple.node.*;
import java.util.Arrays;
+import java.util.BitSet;
import java.util.IdentityHashMap;
/**
@@ -129,11 +130,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)+"]";
}
// -----------------------
@@ -141,15 +149,31 @@ 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);
+ }
// Top driver: repeated rounds of coloring and splitting.
byte round=0;
while( !graphColor(round) ) {
split(round);
- assert round < 7; // Really expect to be done soon
+ if( round >= 7 ) // Really expect to be done soon
+ throw Utils.TODO("Allocator taking too long");
round++;
}
postColor(); // Remove no-op spills
@@ -166,24 +190,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) {
@@ -193,11 +205,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.
- for( LRG lrg : _failed.keySet() )
+
+ // Sort, to avoid non-deterministic HashMap ordering
+ LRG[] splits = _failed.keySet().toArray(new LRG[0]);
+ Arrays.sort(splits, (x,y) -> x._lrg - y._lrg );
+ for( LRG lrg : splits )
split(round,lrg);
}
- // Split this live range
+ // Split this live range, top level heuristic
boolean split( byte round, LRG lrg ) {
assert lrg.leader(); // Already rolled up
@@ -206,13 +222,16 @@ 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 )
return splitEmptyMaskSimple(round,lrg);
- // Default to splitByLoop
- //return splitEmptyMask(round,lrg);
+ // Repeated single-reg uses from a single def. Special for archs
+ // with more fixed regs.
+ if( !lrg._multiDef && lrg._1regDefCnt <= 1 && lrg._1regUseCnt > 2 )
+ if( splitEmptyMaskByUse(round,lrg) )
+ return true;
}
// Generic split-by-loop depth.
@@ -235,39 +254,80 @@ public class RegAlloc {
// alloc
// V2/rax - kills prior RAX
// st4 [V1],len - No good, must split around
- makeSplit("def/empty1",round,lrg).insertAfter((Node)lrg._machDef, false/*true*/);
+ insertAfterAndReplace( makeSplit("def/empty1",round,lrg), (Node)lrg._machDef, false/*true*/);
// Split just before use
- if( lrg._1regUseCnt==1 )
+ if( lrg._1regUseCnt==1 || (lrg._1regDefCnt==1 && ((Node)lrg._machDef).nOuts()==1) )
insertBefore((Node)lrg._machUse,lrg._uidx,"use/empty1",round,lrg);
return true;
}
- // Split live range with an empty mask. Specifically forces splits at
- // single-register defs or uses everywhere.
- boolean splitEmptyMask( byte round, LRG lrg ) {
- findAllLRG(lrg);
- // If no single-use or single-def, assume this is a complete register
- // kill and force spilling everywhere.
- boolean all = lrg._killed || (lrg._1regDefCnt + lrg._1regUseCnt)==0;
- for( Node n : _ns ) {
- if( !(n instanceof MachNode mach) ) continue;
- // Find def of spilling live range; spilling everywhere, OR
- // single-register DEF and not cloneable (since these will clone
- // before every use)
- if( lrg(n)==lrg && (all || (!mach.isClone() && mach.outregmap().size1() )) )
- makeSplit(n,"def/empty2",round,lrg).insertAfter(n,true);
- // Find all uses
- for( int i=1; i<n.nIns(); i++ ) {
- Node def = n.in(i);
- // Skip any new splits inserted just this pass
- while( def instanceof SplitNode && lrg(def)==null )
- def = def.in(1);
- // Main def (past splits) is of the spilling lrg, and spilling
- // single-register USE (or everywhere)
- if( lrg(def)==lrg && (all || mach.regmap(i).size1()) )
- insertBefore(n,i,"use/empty2",round,lrg);
+ // Single-def live range with an empty mask. There are many single-reg
+ // uses. Theory is there's many repeats of the same reg amongst the uses.
+ // In of splitting once per use, start by splitting into groups based on
+ // required input register.
+ boolean splitEmptyMaskByUse( byte round, LRG lrg ) {
+ Node def = (Node)lrg._machDef;
+
+ // Look at each use, and break into non-overlapping register classes.
+ Ary<RegMask> rclass = new Ary<>(RegMask.class);
+ boolean done=false;
+ int ncalls=0;
+ while( !done ) {
+ done = true;
+ for( Node use : def._outputs )
+ if( use instanceof MachNode mach ) {
+ if( mach instanceof CallNode ) ncalls++;
+ for( int i=1; i<use.nIns(); i++ )
+ if( use.in(i)==def )
+ done = putIntoRegClass( rclass, mach.regmap(i) );
+ }
+ }
+
+ // See how many register classes we split into. Generally not
+ // productive to split like this across calls, which are going to kill
+ // all registers anyways.
+ if( rclass._len <= 1 || ncalls > 1 ) return false;
+
+ // Split by class
+ Ary<Node> ns = new Ary<>(Node.class);
+ for( RegMask rmask : rclass ) {
+ ns.addAll(def._outputs);
+ Node split = makeSplit(def,"popular",round,lrg);
+ split.insertAfter( def );
+ if( split.nIns()>1 ) split.setDef(1,def);
+ // all uses by class to split
+ for( Node use : ns ) {
+ if( use instanceof MachNode mach && use!=split ) {
+ // Check all use inputs for n, in case there's several
+ for( int i = 1; i < use.nIns(); i++ )
+ // Find a def input, and check register class
+ if( use.in( i ) == def ) {
+ RegMask m = mach.regmap( i );
+ if( m!=null && mach.regmap( i ).overlap( rmask ) )
+ // Modify use to use the split version specialized to this rclass
+ use.setDefOrdered( i, split );
+ }
+ }
+ }
+ ns.clear();
+ }
+ return true;
+ }
+
+
+ // Put use into a register class, perhaps adding a class or perhaps
+ // narrowing a class (and causing a repeat)
+ private static boolean putIntoRegClass( Ary<RegMask> rclass, RegMask rmask ) {
+ for( int i=0; i<rclass._len; i++ ) {
+ RegMask omask = rclass.at(i);
+ if( omask.and(rmask) == omask ) return true; // Within the same register class
+ if( omask.overlap(rmask) ) {
+ rclass.set(i,new RegMask(omask.copy().and(rmask)));
+ return false; // Need go again
}
}
+ // Add a new class, no need to go again
+ rclass.push(rmask);
return true;
}
@@ -281,27 +341,28 @@ public class RegAlloc {
// For all conflicts
for( Node def : conflicts ) {
assert lrg(def)==lrg; // Might be conflict use-side
+ // Split before each use that extends the live range; i.e. is a
+ // Phi or two-address
+ for( int i=0; i<def._outputs._len; i++ ) {
+ Node use = def.out(i);
+ if( (use instanceof PhiNode phi &&
+ !(phi.region() instanceof LoopNode loop && phi.in(2)==def && def.cfg0().idepth() > loop.idepth() ) ) ||
+ (use instanceof MachNode mach && mach.twoAddress()!=0 && use.in(mach.twoAddress())==def) )
+ insertBefore( use, use._inputs.find(def), "use/self/use",round,lrg );
+ }
// Split after the Phi which extends the LRG. Split also before
// Phi slot 1 (and not all inputs), because Phis extend the live range.
// TODO: split before all inputs (except the last; at least 1 split here must be extra)
if( def instanceof PhiNode phi && !(def instanceof ParmNode) ) {
SplitNode split = makeSplit("def/self",round,lrg);
- split.insertAfter(def,false);
+ insertAfterAndReplace(split,def,false);
if( split.nOuts()==0 )
- split.kill();
+ split.killOrdered();
insertBefore(phi,1,"use/self/phi",round,lrg);
}
// Split before two-address ops which extend the live range
if( def instanceof MachNode mach && mach.twoAddress()!= 0 )
insertBefore(def,mach.twoAddress(),"use/self/two",round,lrg);
- // Split before each use that extends the live range; i.e. is a
- // Phi or two-address
- for( int i=0; i<def._outputs._len; i++ ) {
- Node use = def.out(i);
- if( (use instanceof PhiNode phi && !(phi.region() instanceof LoopNode && phi.in(2)==def ) ) ||
- (use instanceof MachNode mach && mach.twoAddress()!=0 && use.in(mach.twoAddress())==def) )
- insertBefore( use, use._inputs.find(def), "use/self/use",round,lrg );
- }
}
return true;
}
@@ -348,10 +409,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
- !(n.nOuts()==1 && n.out(0) instanceof SplitNode) )
+ // 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
- makeSplit("def/loop",round,lrg).insertAfter(n,false);
+ insertAfterAndReplace( makeSplit("def/loop",round,lrg), n,true);
}
// PhiNodes check all CFG inputs
@@ -362,20 +423,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.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) && // This is a LRG use
- // splitting in inner loop or at loop border
- (min==max || n.cfg0().loopDepth() <= min) &&
- // Not a single-use split same block already
- !(n.in(i) instanceof SplitNode && n.in(i).nOuts()==1 && n.cfg0()==n.in(i).cfg0()) )
+ // 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 );
+ insertBefore( n, i, "use/loop/use", round,lrg, false );
}
}
}
@@ -396,6 +456,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;
@@ -421,12 +497,12 @@ public class RegAlloc {
for( Node n : _ns ) assert !n.isDead();
}
- void insertBefore(Node n, int i, String kind, byte round, LRG lrg) {
+ void insertBefore(Node n, int i, String kind, byte round, LRG lrg, boolean skip) {
Node def = n.in(i);
// Effective block for use
CFGNode cfg = n instanceof PhiNode phi ? phi.region().cfg(i) : n.cfg0();
// Def is a split ?
- if( def instanceof SplitNode ) {
+ if( skip && def instanceof SplitNode ) {
boolean singleReg = n instanceof MachNode mach && mach.regmap(i).size1();
// Same block, multiple registers, split is only used by n,
// assume this is good enough and do not split again.
@@ -435,9 +511,30 @@ public class RegAlloc {
}
makeSplit(def,kind,round,lrg).insertBefore(n, i);
// Skip split-of-split same block
- if( def instanceof SplitNode && cfg==def.cfg0() )
+ if( skip && def instanceof SplitNode && cfg==def.cfg0() )
n.in(i).setDefOrdered(1,def.in(1));
}
+ void insertBefore(Node n, int i, String kind, byte round, LRG lrg) {
+ insertBefore(n,i,kind,round,lrg,true);
+ }
+
+ // Replace uses of `def` with `split`, and insert `split` immediately after
+ // `def` in the basic block.
+ public void insertAfterAndReplace( Node split, Node def, boolean must ) {
+ split.insertAfter(def);
+ if( split.nIns()>1 ) split.setDef(1,def);
+ for( int j=def.nOuts()-1; j>=0; j-- ) {
+ Node use = def.out(j);
+ if( use==split ) continue; // Skip self
+ // Can we avoid a split of a split? 'this' split is used by
+ // another split in the same block.
+ if( !must && use instanceof SplitNode split2 && sameBlockNoClobber(split2) )
+ continue;
+ int idx = use._inputs.find(def);
+ use.setDefOrdered(idx,split);
+ if( j < def.nOuts() ) j++;
+ }
+ }
private Node makeSplit( Node def, String kind, byte round, LRG lrg ) {
Node split = def instanceof MachNode mach && mach.isClone()
@@ -456,11 +553,27 @@ 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) )
@@ -499,4 +612,24 @@ public class RegAlloc {
lo.setDefOrdered(1,hi.in(1));
return true;
}
+
+
+ public boolean sameBlockNoClobber( SplitNode split ) {
+ Node def = split.in(1);
+ CFGNode cfg = def.cfg0();
+ if( cfg != split.cfg0() ) return false; // Not same block
+ // Get multinode head
+ Node def0 = def instanceof ProjNode ? def.in(0) : def;
+ int defreg = lrg(def)._reg;
+ if( defreg == -1 ) defreg = lrg(def)._mask.firstReg();
+ if( defreg == -1 ) return false; // no allowed registers -> clobbered
+ for( int idx = cfg._outputs.find(split) -1; idx >= 0; idx-- ) {
+ Node n = cfg.out(idx);
+ if( n==def0 ) return true; // No clobbers
+ if( lrg(n) == lrg(def) ) return false; // Self conflict
+ if( lrg(n)!=null && lrg(n)._reg == defreg )
+ return false; // Clobbered
+ }
+ throw Utils.TODO();
+ }
}
diff --git a/src/main/java/com/seaofnodes/simple/codegen/RegMask.java b/src/main/java/com/seaofnodes/simple/codegen/RegMask.java
index f28805b5..ec0c4e30 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
@@ -28,10 +28,11 @@ public class RegMask {
public RegMask(int bit) {
if( bit < 64 ) _bits0 = 1L<<bit;
- else _bits1 = 1L<<(bit=64);
+ else _bits1 = 1L<<(bit-64);
}
public RegMask(long bits ) { _bits0 = bits; }
public RegMask(long bits0, long bits1 ) { _bits0 = bits0; _bits1 = bits1; }
+ public RegMask(RegMask r) { _bits0 = r._bits0; _bits1 = r._bits1; }
private RegMask() { _bits0 = _bits1 = 0; }
// AND, with copy-on-write if changing
@@ -65,6 +66,7 @@ public class RegMask {
public short firstReg() {
+ if( isEmpty() ) return -1;
return (short)(_bits0 != 0
? Long.numberOfTrailingZeros(_bits0)
: Long.numberOfTrailingZeros(_bits1)+64);
@@ -84,7 +86,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;
}
@@ -108,35 +110,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/AddFNode.java b/src/main/java/com/seaofnodes/simple/node/AddFNode.java
index 7d4e2910..792da62e 100644
--- a/src/main/java/com/seaofnodes/simple/node/AddFNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/AddFNode.java
@@ -13,7 +13,7 @@ public class AddFNode extends Node {
@Override public String glabel() { return "+"; }
@Override
- StringBuilder _print1(StringBuilder sb, BitSet visited) {
+ public StringBuilder _print1(StringBuilder sb, BitSet visited) {
in(1)._print0(sb.append("("), visited);
in(2)._print0(sb.append("+"), visited);
return sb.append(")");
diff --git a/src/main/java/com/seaofnodes/simple/node/AddNode.java b/src/main/java/com/seaofnodes/simple/node/AddNode.java
index bc65e79a..dd128e76 100644
--- a/src/main/java/com/seaofnodes/simple/node/AddNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/AddNode.java
@@ -15,7 +15,7 @@ public class AddNode extends Node {
@Override public String glabel() { return "+"; }
@Override
- StringBuilder _print1(StringBuilder sb, BitSet visited) {
+ public StringBuilder _print1(StringBuilder sb, BitSet visited) {
in(1)._print0(sb.append("("), visited);
in(2)._print0(sb.append("+"), visited);
return sb.append(")");
@@ -96,7 +96,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 e6b87ce8..fd6448a2 100644
--- a/src/main/java/com/seaofnodes/simple/node/BoolNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/BoolNode.java
@@ -21,7 +21,7 @@ abstract public class BoolNode extends Node {
public String glabel() { return op(); }
@Override
- StringBuilder _print1(StringBuilder sb, BitSet visited) {
+ public StringBuilder _print1(StringBuilder sb, BitSet visited) {
in(1)._print0(sb.append("("), visited);
in(2)._print0(sb.append(op()), visited);
return sb.append(")");
@@ -92,6 +92,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/CFGNode.java b/src/main/java/com/seaofnodes/simple/node/CFGNode.java
index 37d08fb0..7877f93a 100644
--- a/src/main/java/com/seaofnodes/simple/node/CFGNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/CFGNode.java
@@ -3,6 +3,7 @@ package com.seaofnodes.simple.node;
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;
@@ -55,6 +56,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;
+ }
// ------------------------------------------------------------------------
/**
@@ -65,7 +73,9 @@ public abstract class CFGNode extends Node {
* See {@link <a href="https://en.wikipedia.org/wiki/Dominator_(graph_theory)">...</a>}
*/
public int _idepth;
- public int idepth() { return _idepth==0 ? (_idepth=idom().idepth()+1) : _idepth; }
+ public int idepth() {
+ return CodeGen.CODE.validIDepth(_idepth) ? _idepth : (_idepth=CodeGen.CODE.iDepthFrom(idom().idepth()));
+ }
// Return the immediate dominator of this Node and compute dom tree depth.
public CFGNode idom(Node dep) { return cfg(0); }
@@ -77,8 +87,8 @@ public abstract class CFGNode extends Node {
CFGNode lhs = this;
while( lhs != rhs ) {
var comp = lhs.idepth() - rhs.idepth();
- if( comp >= 0 ) lhs = ((CFGNode)lhs.addDep(dep)).idom();
- if( comp <= 0 ) 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;
}
@@ -99,9 +109,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;
@@ -177,4 +187,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 adc4e66f..94770f16 100644
--- a/src/main/java/com/seaofnodes/simple/node/CProjNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/CProjNode.java
@@ -10,10 +10,10 @@ import java.util.BitSet;
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);
@@ -24,7 +24,7 @@ public class CProjNode extends CFGNode {
@Override public String label() { return _label; }
- @Override StringBuilder _print1(StringBuilder sb, BitSet visited) { return sb.append(_label); }
+ @Override public StringBuilder _print1(StringBuilder sb, BitSet visited) { return sb.append(_label); }
@Override public boolean blockHead() { return true; }
@@ -46,15 +46,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 03a4462d..f99a0b89 100644
--- a/src/main/java/com/seaofnodes/simple/node/CallEndNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/CallEndNode.java
@@ -1,7 +1,7 @@
package com.seaofnodes.simple.node;
import com.seaofnodes.simple.*;
-import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.type.*;
import java.util.BitSet;
@@ -23,7 +23,7 @@ public class CallEndNode extends CFGNode implements MultiNode {
public CallNode call() { return (CallNode)in(0); }
@Override
- StringBuilder _print1(StringBuilder sb, BitSet visited) {
+ public StringBuilder _print1(StringBuilder sb, BitSet visited) {
sb.append("cend( ");
sb.append( in(0) instanceof CallNode ? "Call, " : "----, ");
for( int i=1; i<nIns()-1; i++ )
@@ -38,7 +38,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.
@@ -70,8 +70,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;
@@ -81,13 +82,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);
}
}
@@ -97,4 +101,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 c9bbcc52..78a0b153 100644
--- a/src/main/java/com/seaofnodes/simple/node/CallNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/CallNode.java
@@ -21,7 +21,7 @@ public class CallNode extends CFGNode {
@Override public String label() { return "Call"; }
- @Override StringBuilder _print1(StringBuilder sb, BitSet visited) {
+ @Override public StringBuilder _print1(StringBuilder sb, BitSet visited) {
String fname = name();
if( fname == null ) fptr()._print0(sb,visited);
else sb.append(fname);
@@ -50,6 +50,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
@@ -87,6 +91,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();
@@ -98,12 +103,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
@@ -116,7 +121,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 )
@@ -124,7 +129,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
@@ -134,7 +139,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 5a92802c..0947bcf0 100644
--- a/src/main/java/com/seaofnodes/simple/node/CastNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/CastNode.java
@@ -31,7 +31,7 @@ public class CastNode extends Node {
@Override public boolean isConst() { return true; }
@Override
- StringBuilder _print1(StringBuilder sb, BitSet visited) {
+ public StringBuilder _print1(StringBuilder sb, BitSet visited) {
return in(1)._print0(sb.append(label()), visited);
}
@@ -46,7 +46,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 f7ebc006..79f301c1 100644
--- a/src/main/java/com/seaofnodes/simple/node/ConstantNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/ConstantNode.java
@@ -42,13 +42,13 @@ public class ConstantNode extends Node {
public String uniqueName() { return "Con_" + _nid; }
@Override
- StringBuilder _print1(StringBuilder sb, BitSet visited) {
+ public StringBuilder _print1(StringBuilder sb, BitSet visited) {
if( _con instanceof TypeFunPtr tfp && tfp.isConstant() ) {
FunNode fun = CodeGen.CODE.link(tfp);
- if( fun._name != null )
+ if( fun!=null && fun._name != null )
return sb.append("{ ").append(fun._name).append("}");
}
- return sb.append(_con.print(new SB()));
+ return sb.append(_con==null ? "---" : _con.print(new SB()));
}
@Override public boolean isConst() { return true; }
@@ -60,7 +60,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/CtrlNode.java b/src/main/java/com/seaofnodes/simple/node/CtrlNode.java
index 61d9c310..eda7d684 100644
--- a/src/main/java/com/seaofnodes/simple/node/CtrlNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/CtrlNode.java
@@ -8,7 +8,7 @@ import java.util.BitSet;
public class CtrlNode extends CFGNode {
public CtrlNode() { super(CodeGen.CODE._start); }
@Override public String label() { return "Ctrl"; }
- @Override StringBuilder _print1(StringBuilder sb, BitSet visited) { return sb.append("Cctrl"); }
+ @Override public StringBuilder _print1(StringBuilder sb, BitSet visited) { return sb.append("Cctrl"); }
@Override public boolean isConst() { return true; }
@Override public Type compute() { return Type.CONTROL; }
@Override public Node idealize() { return null; }
diff --git a/src/main/java/com/seaofnodes/simple/node/DivFNode.java b/src/main/java/com/seaofnodes/simple/node/DivFNode.java
index a6e459c9..633f3224 100644
--- a/src/main/java/com/seaofnodes/simple/node/DivFNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/DivFNode.java
@@ -13,7 +13,7 @@ public class DivFNode extends Node {
@Override public String glabel() { return "/"; }
@Override
- StringBuilder _print1(StringBuilder sb, BitSet visited) {
+ public StringBuilder _print1(StringBuilder sb, BitSet visited) {
in(1)._print0(sb.append("("), visited);
in(2)._print0(sb.append("/"), visited);
return sb.append(")");
diff --git a/src/main/java/com/seaofnodes/simple/node/DivNode.java b/src/main/java/com/seaofnodes/simple/node/DivNode.java
index 0d5ed297..41518311 100644
--- a/src/main/java/com/seaofnodes/simple/node/DivNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/DivNode.java
@@ -12,7 +12,7 @@ public class DivNode extends Node {
@Override public String glabel() { return "//"; }
@Override
- StringBuilder _print1(StringBuilder sb, BitSet visited) {
+ public StringBuilder _print1(StringBuilder sb, BitSet visited) {
in(1)._print0(sb.append("("), visited);
in(2)._print0(sb.append("/"), visited);
return sb.append(")");
diff --git a/src/main/java/com/seaofnodes/simple/node/FRefNode.java b/src/main/java/com/seaofnodes/simple/node/FRefNode.java
index f0c4045d..6093b8a6 100644
--- a/src/main/java/com/seaofnodes/simple/node/FRefNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/FRefNode.java
@@ -19,7 +19,7 @@ public class FRefNode extends ConstantNode {
@Override public String uniqueName() { return "FRef_" + _nid; }
- @Override StringBuilder _print1(StringBuilder sb, BitSet visited) {
+ @Override public StringBuilder _print1(StringBuilder sb, BitSet visited) {
return sb.append("FRef_").append(_n);
}
@@ -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 38aa83a0..b41a7e54 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;
@@ -109,8 +112,11 @@ public class FunNode extends RegionNode {
return null;
}
- // Bypass Region idom, always assume depth == 1, one more than Start
- @Override public int idepth() { return (_idepth=1); }
+ // Bypass Region idom, always assume depth == 1, one more than Start,
+ // unless folding then just a ID on input#1
+ @Override public int idepth() {
+ return _folding ? super.idepth() : CodeGen.CODE.iDepthAt(1);
+ }
// Bypass Region idom, always assume idom is Start
@Override public CFGNode idom(Node dep) { return cfg(1); }
@@ -161,4 +167,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 e04522b0..5d90449e 100644
--- a/src/main/java/com/seaofnodes/simple/node/IfNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/IfNode.java
@@ -1,10 +1,9 @@
package com.seaofnodes.simple.node;
+import com.seaofnodes.simple.Utils;
import com.seaofnodes.simple.codegen.CodeGen;
-import com.seaofnodes.simple.IterPeeps;
import com.seaofnodes.simple.type.*;
import java.util.BitSet;
-import java.util.HashSet;
public class IfNode extends CFGNode implements MultiNode {
@@ -20,7 +19,9 @@ public class IfNode extends CFGNode implements MultiNode {
@Override
public StringBuilder _print1(StringBuilder sb, BitSet visited) {
sb.append("if( ");
- return in(1)._print0(sb, visited).append(" )");
+ if( in(1)==null ) sb.append("never");
+ else in(1)._print0(sb, visited);
+ return sb.append(" )");
}
public Node ctrl() { return in(0); }
@@ -57,11 +58,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 b70a938d..6147de81 100644
--- a/src/main/java/com/seaofnodes/simple/node/LoadNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/LoadNode.java
@@ -20,7 +20,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);
}
// GraphVis DOT code (must be valid Java identifiers) and debugger labels
@@ -29,7 +29,7 @@ public class LoadNode extends MemOpNode {
@Override public String glabel() { return "." +_name; }
@Override
- StringBuilder _print1(StringBuilder sb, BitSet visited) { return sb.append(".").append(_name); }
+ public StringBuilder _print1(StringBuilder sb, BitSet visited) { return sb.append(".").append(_name); }
@Override
public Type compute() {
@@ -77,7 +77,7 @@ public class LoadNode extends MemOpNode {
while( true ) {
switch( mem ) {
case StoreNode st:
- if( ptr == st.ptr().addDep(this) && off() == st.off() )
+ if( ptr == addDep(st.ptr()) && off() == st.off() )
return extend(castRO(st.val())); // Proved equal
// Can we prove unequal? Offsets do not overlap?
if( !off()._type.join(st.off()._type).isHigh() && // Offsets overlap
@@ -88,7 +88,7 @@ public class LoadNode extends MemOpNode {
break;
case PhiNode phi:
// Assume related
- phi.addDep(this);
+ addDep(phi);
break outer;
case ConstantNode top: break outer; // Assume shortly dead
case ProjNode mproj: // Memory projection
@@ -182,7 +182,7 @@ public class LoadNode extends MemOpNode {
if( px==null ) return false;
if( px._type instanceof TypeMem mem && mem._t.isHighOrConst() ) return true;
if( px instanceof StoreNode st1 && ptr()==st1.ptr() && off()==st1.off() ) return true;
- px.addDep(this);
+ addDep(px);
return false;
}
diff --git a/src/main/java/com/seaofnodes/simple/node/LogicalNode.java b/src/main/java/com/seaofnodes/simple/node/LogicalNode.java
index 8c3452bf..e8552789 100644
--- a/src/main/java/com/seaofnodes/simple/node/LogicalNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/LogicalNode.java
@@ -13,7 +13,7 @@ public abstract class LogicalNode extends Node {
abstract String op();
@Override
- StringBuilder _print1(StringBuilder sb, BitSet visited) {
+ public StringBuilder _print1(StringBuilder sb, BitSet visited) {
in(1)._print0(sb.append("("), visited);
in(2)._print0(sb.append(op()), visited);
return sb.append(")");
diff --git a/src/main/java/com/seaofnodes/simple/node/LoopNode.java b/src/main/java/com/seaofnodes/simple/node/LoopNode.java
index d2b0e36c..2fa070bc 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,9 @@ 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 _idepth==0 ? (_idepth=idom().idepth()+1) : _idepth; }
+ public int idepth() {
+ return CodeGen.CODE.validIDepth(_idepth) ? _idepth : (_idepth=CodeGen.CODE.iDepthFrom(idom().idepth()));
+ }
// Bypass Region idom, same as the default idom() using use in(1) instead of in(0)
@Override public CFGNode idom(Node dep) { return entry(); }
@@ -39,42 +42,48 @@ 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 stop; // 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 = stop._ltree;
// Now fold control into the exit. Might have 1 valid exit, or an
// XCtrl or a bunch of prior NeverNode exits.
Node top = new ConstantNode(Type.TOP).peephole();
- ReturnNode ret = fun.ret();
+ Node memout = new MemMergeNode(false);
+ memout.addDef(f); // placeholder for control
+ for( Node u : _outputs )
+ if( u instanceof PhiNode phi && phi._type.isa(TypeMem.BOT) )
+ memout.addDef(phi);
+
Node ctrl = ret.ctrl(), mem = ret.mem(), expr = ret.expr();
- if( ctrl._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 = stop._ltree;
+ mem = new PhiNode(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;
}
ret.setDef(0,ctrl);
@@ -83,4 +92,12 @@ public class LoopNode extends RegionNode {
return stop;
}
+
+ private static boolean nested(LoopTree inner, LoopTree outer) {
+ for( ; inner!=null; inner = inner._par )
+ if( inner == outer )
+ return true;
+ return false;
+ }
+
}
diff --git a/src/main/java/com/seaofnodes/simple/node/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 1495a703..32c25a8b 100644
--- a/src/main/java/com/seaofnodes/simple/node/MemMergeNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/MemMergeNode.java
@@ -30,7 +30,7 @@ public class MemMergeNode extends Node {
@Override public boolean isMem() { return true; }
@Override
- StringBuilder _print1(StringBuilder sb, BitSet visited) {
+ public StringBuilder _print1(StringBuilder sb, BitSet visited) {
sb.append(_inProgress ? "Merge[" : "MEM[ ");
for( int j=2; j<nIns(); j++ ) {
sb.append(j);
@@ -65,11 +65,18 @@ public class MemMergeNode extends Node {
if( inProgress() ) return null;
// If not merging any memory (all memory is just the default)
- if( nIns()==2 )
+ if( allDefault() )
return in(1); // Become default memory
return null;
}
+ private boolean allDefault() {
+ for( int i=2; i<nIns(); i++ )
+ if( in(1) != in(i) )
+ return false;
+ return true;
+ }
+
public Node in( Var v ) { return in(v._idx); }
@@ -149,7 +156,7 @@ public class MemMergeNode extends Node {
}
}
- @Override boolean eq( Node n ) {
+ @Override public boolean eq( Node n ) {
return this==n || !_inProgress;
}
}
diff --git a/src/main/java/com/seaofnodes/simple/node/MemOpNode.java b/src/main/java/com/seaofnodes/simple/node/MemOpNode.java
index cba4dc1b..058c86fa 100644
--- a/src/main/java/com/seaofnodes/simple/node/MemOpNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/MemOpNode.java
@@ -9,26 +9,49 @@ import java.util.BitSet;
/**
* Convenience common base for Load and Store.
+ *
+ * 0 - Control
+ * 1 - Memory
+ * 2 - Base of object, OOP
+ * 3 - Offset, integer types
+ * 4 - Value, for Stores only
*/
public abstract class MemOpNode extends Node {
- 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 ) {
@@ -36,7 +59,20 @@ 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;
}
//
@@ -47,12 +83,12 @@ public abstract class MemOpNode extends Node {
public Node ptr() { return in(2); }
public Node off() { return in(3); }
- @Override StringBuilder _print1( StringBuilder sb, BitSet visited ) { return _printMach(sb,visited); }
+ @Override public StringBuilder _print1( StringBuilder sb, BitSet visited ) { return _printMach(sb,visited); }
public StringBuilder _printMach( StringBuilder sb, BitSet visited ) { throw Utils.TODO(); }
@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 "=="
}
diff --git a/src/main/java/com/seaofnodes/simple/node/MinusFNode.java b/src/main/java/com/seaofnodes/simple/node/MinusFNode.java
index 2b749ba3..99fc3144 100644
--- a/src/main/java/com/seaofnodes/simple/node/MinusFNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/MinusFNode.java
@@ -13,7 +13,7 @@ public class MinusFNode extends Node {
@Override public String glabel() { return "-"; }
@Override
- StringBuilder _print1(StringBuilder sb, BitSet visited) {
+ public StringBuilder _print1(StringBuilder sb, BitSet visited) {
in(1)._print0(sb.append("(-"), visited);
return sb.append(")");
}
diff --git a/src/main/java/com/seaofnodes/simple/node/MinusNode.java b/src/main/java/com/seaofnodes/simple/node/MinusNode.java
index dd2cbd26..e06e0c69 100644
--- a/src/main/java/com/seaofnodes/simple/node/MinusNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/MinusNode.java
@@ -14,7 +14,7 @@ public class MinusNode extends Node {
@Override public String glabel() { return "-"; }
@Override
- StringBuilder _print1(StringBuilder sb, BitSet visited) {
+ public StringBuilder _print1(StringBuilder sb, BitSet visited) {
in(1)._print0(sb.append("(-"), visited);
return sb.append(")");
}
diff --git a/src/main/java/com/seaofnodes/simple/node/MulFNode.java b/src/main/java/com/seaofnodes/simple/node/MulFNode.java
index 2d44a9fb..6fbf4bbb 100644
--- a/src/main/java/com/seaofnodes/simple/node/MulFNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/MulFNode.java
@@ -13,7 +13,7 @@ public class MulFNode extends Node {
@Override public String glabel() { return "*"; }
@Override
- StringBuilder _print1(StringBuilder sb, BitSet visited) {
+ public StringBuilder _print1(StringBuilder sb, BitSet visited) {
in(1)._print0(sb.append("("), visited);
in(2)._print0(sb.append("*"), visited);
return sb.append(")");
diff --git a/src/main/java/com/seaofnodes/simple/node/MulNode.java b/src/main/java/com/seaofnodes/simple/node/MulNode.java
index 80f8c319..384dbace 100644
--- a/src/main/java/com/seaofnodes/simple/node/MulNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/MulNode.java
@@ -13,7 +13,7 @@ public class MulNode extends Node {
@Override public String glabel() { return "*"; }
@Override
- StringBuilder _print1(StringBuilder sb, BitSet visited) {
+ public StringBuilder _print1(StringBuilder sb, BitSet visited) {
in(1)._print0(sb.append("("), visited);
in(2)._print0(sb.append("*"), visited);
return sb.append(")");
diff --git a/src/main/java/com/seaofnodes/simple/node/NeverNode.java b/src/main/java/com/seaofnodes/simple/node/NeverNode.java
index 26198500..516f218b 100644
--- a/src/main/java/com/seaofnodes/simple/node/NeverNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/NeverNode.java
@@ -9,7 +9,8 @@ import java.util.BitSet;
// "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 80545d66..5a8c1b6b 100644
--- a/src/main/java/com/seaofnodes/simple/node/NewNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/NewNode.java
@@ -1,7 +1,9 @@
package com.seaofnodes.simple.node;
-import com.seaofnodes.simple.type.*;
+import com.seaofnodes.simple.SB;
import com.seaofnodes.simple.Utils;
+import com.seaofnodes.simple.codegen.*;
+import com.seaofnodes.simple.type.*;
import java.util.BitSet;
/**
@@ -39,7 +41,7 @@ public class NewNode extends Node implements MultiNode {
}
@Override
- StringBuilder _print1(StringBuilder sb, BitSet visited) {
+ public StringBuilder _print1(StringBuilder sb, BitSet visited) {
sb.append("new ");
return sb.append(_ptr._obj.str());
}
@@ -76,8 +78,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==1 ? _arg3Mask : null; }
+ public RegMask outregmap() { return null; }
+ public RegMask outregmap(int idx) { return idx==1 ? _retMask : null; }
+ public RegMask killmap() { return _kills; }
+ public void asm(CodeGen code, SB sb) {
+ sb.p("#calloc, ").p(code.reg(size()));
+ }
}
diff --git a/src/main/java/com/seaofnodes/simple/node/Node.java b/src/main/java/com/seaofnodes/simple/node/Node.java
index b3d6c94e..c0c2e606 100644
--- a/src/main/java/com/seaofnodes/simple/node/Node.java
+++ b/src/main/java/com/seaofnodes/simple/node/Node.java
@@ -16,14 +16,14 @@ import static com.seaofnodes.simple.codegen.CodeGen.CODE;
* The Node class provides common functionality used by all subtypes.
* Subtypes of Node specialize by overriding methods.
*/
-public abstract class Node {
+public abstract class Node implements Cloneable {
/**
* Each node has a unique dense Node ID within a compilation context
* The ID is useful for debugging, for using as an offset in a bitvector,
* as well as for computing equality of nodes (to be implemented later).
*/
- public final int _nid;
+ public int _nid;
/**
* Inputs to the node. These are use-def references to Nodes.
@@ -32,7 +32,7 @@ public abstract class Node {
* Ordering is required because e.g. "a/b" is different from "b/a".
* The first input (offset 0) is often a {@link CFGNode} node.
*/
- public final Ary<Node> _inputs;
+ public Ary<Node> _inputs;
/**
* Outputs reference Nodes that are not null and have this Node as an
@@ -43,7 +43,7 @@ public abstract class Node {
* walked in either direction. These outputs are typically used for
* efficient optimizations but otherwise have no semantics meaning.
*/
- public final Ary<Node> _outputs;
+ public Ary<Node> _outputs;
/**
@@ -120,7 +120,7 @@ public abstract class Node {
: _print1(sb, visited);
}
// Every Node implements this; a partial-line recursive print
- abstract StringBuilder _print1(StringBuilder sb, BitSet visited);
+ abstract public StringBuilder _print1(StringBuilder sb, BitSet visited);
public String p(int depth) { return IRPrinter.prettyPrint(this,depth); }
@@ -226,6 +226,7 @@ public abstract class Node {
}
// Breaks the edge invariants, used temporarily
+ @SuppressWarnings("unchecked")
protected <N extends Node> N addUse(Node n) { _outputs.add(n); return (N)this; }
// Remove node 'use' from 'def's (i.e. our) output list, by compressing the list in-place.
@@ -267,6 +268,15 @@ public abstract class Node {
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; }
@@ -274,6 +284,7 @@ public abstract class Node {
// Add bogus null use to keep node alive
public <N extends Node> N keep() { return addUse(null); }
// Remove bogus null.
+ @SuppressWarnings("unchecked")
public <N extends Node> N unkeep() {
delUse(null);
return (N)this;
@@ -300,9 +311,8 @@ public abstract class Node {
kill();
}
- // Replace uses of `def` with `this`, and insert `this` immediately after
- // `def` in the basic block.
- public void insertAfter( Node def, boolean must ) {
+ // insert `this` immediately after `def` in the same basic block.
+ public void insertAfter( Node def ) {
CFGNode cfg = def.cfg0();
int i = cfg._outputs.find(def)+1;
if( cfg instanceof CallEndNode ) {
@@ -315,19 +325,6 @@ public abstract class Node {
while( cfg.out(i) instanceof PhiNode || cfg.out(i) instanceof CalleeSaveNode ) i++;
cfg._outputs.insert(this,i);
_inputs.set(0,cfg);
- for( int j=def.nOuts()-1; j>=0; j-- ) {
- // Can we avoid a split of a split? 'this' split is used by
- // another split in the same block.
- if( !must && def.out(j) instanceof SplitNode split && def.out(j).cfg0()==cfg &&
- !split._kind.contains("self") )
- continue;
- Node use = def._outputs.del(j);
- use.unlock();
- int idx = use._inputs.find(def);
- use._inputs.set(idx,this);
- addUse(use);
- }
- if( nIns()>1 ) setDef(1,def);
}
// Insert this in front of use.in(uidx) with this, and insert this
@@ -337,6 +334,8 @@ public abstract class Node {
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);
@@ -348,7 +347,7 @@ public abstract class Node {
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 ) {
@@ -487,6 +486,7 @@ public abstract class Node {
return old;
}
+ @SuppressWarnings("unchecked")
public <N extends Node> N init() { _type = compute(); return (N)this; }
/**
@@ -549,17 +549,16 @@ public abstract class Node {
* 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 ) {
+ <N extends Node> N addDep( N dep ) {
// Running peepholes during the big assert cannot have side effects
// like adding dependencies.
- if( CODE._midAssert ) return this;
- if( dep == null ) return this;
- if( _deps==null ) _deps = new Ary<>(Node.class);
- if( _deps .find(dep) != -1 ) return this; // Already on list
- if( _inputs .find(dep) != -1 ) return this; // No need for deps on immediate neighbors
- if( _outputs.find(dep) != -1 ) return this;
- _deps.add(dep);
- return this;
+ if( CODE._midAssert ) return dep;
+ if( dep._deps==null ) dep._deps = new Ary<>(Node.class);
+ if( dep._deps .find(this) != -1 ) return dep; // Already on list
+ if( dep._inputs .find(this) != -1 ) return dep; // No need for deps on immediate neighbors
+ if( dep._outputs.find(this) != -1 ) return dep;
+ dep._deps.add(this);
+ return dep;
}
// Move the dependents onto a worklist, and clear for future dependents.
@@ -585,7 +584,7 @@ public abstract class Node {
}
// 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
@@ -667,7 +666,7 @@ public abstract class Node {
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;
@@ -679,6 +678,18 @@ public abstract class Node {
// Does not need to be implemented in isCFG() nodes.
Node copy(Node lhs, Node rhs) { throw Utils.TODO("Binary ops need to implement copy"); }
+ public Node copy() {
+ Node n;
+ try { n = (Node)clone(); }
+ catch( Exception e ) { throw new RuntimeException(e); }
+ n._nid = CODE.getUID(); // allocate unique dense ID
+ n._inputs = new Ary<>(Node.class);
+ n._outputs = new Ary<>(Node.class);
+ n._deps = null;
+ n._hash = 0;
+ return n;
+ }
+
// Report any post-optimize errors
public Parser.ParseException err() { return null; }
diff --git a/src/main/java/com/seaofnodes/simple/node/NotNode.java b/src/main/java/com/seaofnodes/simple/node/NotNode.java
index b454de66..0314841d 100644
--- a/src/main/java/com/seaofnodes/simple/node/NotNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/NotNode.java
@@ -12,7 +12,7 @@ public class NotNode extends Node {
@Override public String glabel() { return "!"; }
@Override
- StringBuilder _print1(StringBuilder sb, BitSet visited) {
+ public StringBuilder _print1(StringBuilder sb, BitSet visited) {
in(1)._print0(sb.append("(!"), visited);
return sb.append(")");
}
diff --git a/src/main/java/com/seaofnodes/simple/node/ParmNode.java b/src/main/java/com/seaofnodes/simple/node/ParmNode.java
index 8d7c4205..9e11a129 100644
--- a/src/main/java/com/seaofnodes/simple/node/ParmNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/ParmNode.java
@@ -22,7 +22,7 @@ public class ParmNode extends PhiNode {
public FunNode fun() { return (FunNode)in(0); }
@Override
- StringBuilder _print1(StringBuilder sb, BitSet visited) {
+ public StringBuilder _print1(StringBuilder sb, BitSet visited) {
if( "main".equals(fun()._name) && _label.equals("arg") )
return sb.append("arg");
sb.append("Parm_").append(_label).append("(");
diff --git a/src/main/java/com/seaofnodes/simple/node/PhiNode.java b/src/main/java/com/seaofnodes/simple/node/PhiNode.java
index 16f38d45..0579ecd0 100644
--- a/src/main/java/com/seaofnodes/simple/node/PhiNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/PhiNode.java
@@ -13,7 +13,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) {
@@ -29,7 +29,7 @@ public class PhiNode extends Node {
@Override public String glabel() { return "&phi;_"+_label; }
@Override
- StringBuilder _print1(StringBuilder sb, BitSet visited) {
+ public StringBuilder _print1(StringBuilder sb, BitSet visited) {
if( !(region() instanceof RegionNode r) || r.inProgress() )
sb.append("Z");
sb.append("Phi(");
@@ -58,7 +58,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;
}
@@ -80,37 +80,15 @@ public class PhiNode extends Node {
if( r.in(i)._type == Type.XCONTROL )
return null;
- // Pull "down" a common data op. One less op in the world. One more
- // Phi, but Phis do not make code.
- // Phi(op(A,B),op(Q,R),op(X,Y)) becomes
- // op(Phi(A,Q,X), Phi(B,R,Y)).
- Node op = in(1);
- if( !isMem() && op.nIns()==3 && op.in(0)==null && same_op() ) {
- assert !(op instanceof CFGNode);
- Node[] lhss = new Node[nIns()];
- Node[] rhss = new Node[nIns()];
- lhss[0] = rhss[0] = in(0); // Set Region
- for( int i=1; i<nIns(); i++ ) {
- lhss[i] = in(i).in(1);
- rhss[i] = in(i).in(2);
- }
- Node phi_lhs = new PhiNode(_label, _declaredType,lhss).peephole();
- Node phi_rhs = new PhiNode(_label, _declaredType,rhss).peephole();
- Node down = op.copy(phi_lhs,phi_rhs);
- // Test not running backwards, which can happen for e.g. And's
- if( down.compute().isa(compute()) )
- return down;
- in(1).in(1).addDep(this);
- in(1).in(2).addDep(this);
- in(2).in(1).addDep(this);
- in(2).in(2).addDep(this);
- down.kill();
- }
+ // Generic "pull down op"
+ Node progress;
+ if( same_op() && (progress = drop_same_op()) != null )
+ return progress;
// If merging Phi(N, cast(N)) - we are losing the cast JOIN effects, so just remove.
if( nIns()==3 ) {
- if( in(1) instanceof CastNode cast && 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);
@@ -123,7 +101,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();
@@ -136,13 +114,52 @@ 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() {
- for( int i=2; i<nIns(); i++ )
- if( in(1).getClass() != in(i).getClass() )
+ for( int i=1; i<nIns(); i++ ) {
+ Node op = in(i);
+ if( in(1).getClass() != op.getClass() || op.in(0)!=null || in(1).nIns() != op.nIns() )
+ return false; // Wrong class or CFG bound or mismatched inputs
+ if( op.nOuts() > 1 ) { // Too many users, but addDep in case lose users
+ for( Node out : op._outputs )
+ if( out!=null && out!=this )
+ addDep(out);
return false;
+ }
+ }
return true;
}
+ private Node drop_same_op() {
+ assert !(in(1) instanceof CFGNode);
+ Node op = in(1);
+ Node cp = op.copy();
+ cp._type = null; // Fresh type
+ cp.addDef(null); // No control
+
+ for( int j=1; j<op.nIns(); j++ ) {
+ boolean needsPhi = false;
+ Node x = op.in(j); // Jth input from sample #1
+ for( int i=2; i<nIns(); i++ )
+ if( in(i).in(j) != x )
+ { needsPhi=true; break; }
+ if( needsPhi ) {
+ x = new PhiNode(_label,op.in(j)._type.glb());
+ x.addDef(region());
+ for( int i=1; i<nIns(); i++ )
+ x.addDef(in(i).in(j));
+ x = x.peephole();
+ }
+ cp.addDef(x);
+ }
+ // Test not running backwards, which can happen for e.g. And's
+ if( cp.compute().isa(compute()) )
+ return cp;
+ cp.kill();
+ return null;
+ }
+
/**
* If only single unique input, return it
*/
@@ -153,7 +170,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;
}
@@ -165,7 +182,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);
}
@@ -176,7 +193,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 3d9db4ed..a0394e5b 100644
--- a/src/main/java/com/seaofnodes/simple/node/ProjNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/ProjNode.java
@@ -24,7 +24,7 @@ public class ProjNode extends Node {
@Override public String label() { return _label; }
@Override
- StringBuilder _print1(StringBuilder sb, BitSet visited) {
+ public StringBuilder _print1(StringBuilder sb, BitSet visited) {
if( _label != null ) return sb.append(_label);
if( in(0) instanceof CallEndNode cend && cend.call()!=null )
return cend.call()._print0(sb,visited);
@@ -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/ReadOnlyNode.java b/src/main/java/com/seaofnodes/simple/node/ReadOnlyNode.java
index b755af3f..e9c25f47 100644
--- a/src/main/java/com/seaofnodes/simple/node/ReadOnlyNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/ReadOnlyNode.java
@@ -14,7 +14,7 @@ public class ReadOnlyNode extends Node {
@Override public String label() { return "ReadOnly"; }
@Override
- StringBuilder _print1(StringBuilder sb, BitSet visited) {
+ public StringBuilder _print1(StringBuilder sb, BitSet visited) {
return in(1)._print0(sb.append("(const)"),visited);
}
@Override public Type compute() {
diff --git a/src/main/java/com/seaofnodes/simple/node/RegionNode.java b/src/main/java/com/seaofnodes/simple/node/RegionNode.java
index fb6539ae..8f65d0e5 100644
--- a/src/main/java/com/seaofnodes/simple/node/RegionNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/RegionNode.java
@@ -1,6 +1,7 @@
package com.seaofnodes.simple.node;
import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.Parser;
import com.seaofnodes.simple.codegen.CodeGen;
import com.seaofnodes.simple.type.Type;
import java.util.*;
@@ -17,7 +18,7 @@ public class RegionNode extends CFGNode {
public String label() { return "Region"; }
@Override
- StringBuilder _print1(StringBuilder sb, BitSet visited) {
+ public StringBuilder _print1(StringBuilder sb, BitSet visited) {
return sb.append(label());
}
@@ -47,7 +48,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.in(0).addDep(this)==p2.in(0).addDep(this) &&
+ addDep(p1.in(0))==addDep(p2.in(0)) &&
p1.in(0) instanceof IfNode iff ) {
// Replace with the iff.ctrl directly
if( nIns()==3 ) return iff.ctrl();
@@ -56,6 +57,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;
}
@@ -98,14 +122,32 @@ 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( _idepth!=0 ) return _idepth;
+ if( CodeGen.CODE.validIDepth(_idepth) )
+ return _idepth;
int d=0;
for( Node n : _inputs )
if( n!=null )
- d = Math.max(d,((CFGNode)n).idepth()+1);
- return _idepth=d;
+ d = Math.max(d,CodeGen.CODE.iDepthFrom(((CFGNode)n).idepth()));
+ return _idepth = d;
}
@Override public CFGNode idom(Node dep) {
@@ -121,6 +163,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 809dd1e1..317f4ce8 100644
--- a/src/main/java/com/seaofnodes/simple/node/ReturnNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/ReturnNode.java
@@ -3,6 +3,9 @@ package com.seaofnodes.simple.node;
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.*;
import java.util.BitSet;
@@ -29,13 +32,13 @@ 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"; }
@Override
- StringBuilder _print1(StringBuilder sb, BitSet visited) {
+ public StringBuilder _print1(StringBuilder sb, BitSet visited) {
sb.append("return ");
if( expr()==null ) sb.append("----");
else expr()._print0(sb, visited);
@@ -127,4 +130,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/RoundF32Node.java b/src/main/java/com/seaofnodes/simple/node/RoundF32Node.java
index 44d3a587..7936086d 100644
--- a/src/main/java/com/seaofnodes/simple/node/RoundF32Node.java
+++ b/src/main/java/com/seaofnodes/simple/node/RoundF32Node.java
@@ -13,7 +13,7 @@ public class RoundF32Node extends Node {
@Override public String glabel() { return "(f32)"; }
@Override
- StringBuilder _print1(StringBuilder sb, BitSet visited) {
+ public StringBuilder _print1(StringBuilder sb, BitSet visited) {
return in(1)._print0(sb.append("((f32)"), visited).append(")");
}
diff --git a/src/main/java/com/seaofnodes/simple/node/ScopeNode.java b/src/main/java/com/seaofnodes/simple/node/ScopeNode.java
index f193e55a..38a5d968 100644
--- a/src/main/java/com/seaofnodes/simple/node/ScopeNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/ScopeNode.java
@@ -44,7 +44,7 @@ public class ScopeNode extends MemMergeNode {
@Override public String label() { return "Scope"; }
@Override
- StringBuilder _print1(StringBuilder sb, BitSet visited) {
+ public StringBuilder _print1(StringBuilder sb, BitSet visited) {
sb.append("Scope[ ");
int j=1;
for( int i=0; i<nIns(); i++ ) {
diff --git a/src/main/java/com/seaofnodes/simple/node/ShlNode.java b/src/main/java/com/seaofnodes/simple/node/ShlNode.java
index 21dbd503..82e01841 100644
--- a/src/main/java/com/seaofnodes/simple/node/ShlNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/ShlNode.java
@@ -38,7 +38,7 @@ public class ShlNode extends LogicalNode {
if( (shl.value()&63)==0 )
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 22cb9bb9..3a1ed011 100644
--- a/src/main/java/com/seaofnodes/simple/node/SplitNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/SplitNode.java
@@ -11,7 +11,10 @@ public abstract class SplitNode extends MachConcreteNode {
public SplitNode(String kind, byte round, Node[] nodes) { super(nodes); _kind = kind; _round = round; }
@Override public String op() { return "mov"; }
@Override public StringBuilder _print1(StringBuilder sb, BitSet visited) {
- return in(1)._print0(sb.append("mov("),visited).append(")");
+ sb.append("mov(");
+ if( in(1) == null ) sb.append("---");
+ else in(1)._print0(sb,visited);
+ return sb.append(")");
}
@Override public Type compute() { return in(0)._type; }
@Override public Node idealize() { return null; }
diff --git a/src/main/java/com/seaofnodes/simple/node/StartNode.java b/src/main/java/com/seaofnodes/simple/node/StartNode.java
index 22a1750d..f40203b4 100644
--- a/src/main/java/com/seaofnodes/simple/node/StartNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/StartNode.java
@@ -25,7 +25,7 @@ public class StartNode extends LoopNode implements MultiNode {
@Override public String label() { return "Start"; }
@Override
- StringBuilder _print1(StringBuilder sb, BitSet visited) {
+ public StringBuilder _print1(StringBuilder sb, BitSet visited) {
return sb.append(label());
}
@@ -51,7 +51,7 @@ public class StartNode extends LoopNode implements MultiNode {
@Override public Node idealize() { return null; }
// No immediate dominator, and idepth==0
- @Override public int idepth() { return 0; }
+ @Override public int idepth() { return CodeGen.CODE.iDepthAt(0); }
@Override public CFGNode idom(Node dep) { return null; }
}
diff --git a/src/main/java/com/seaofnodes/simple/node/StopNode.java b/src/main/java/com/seaofnodes/simple/node/StopNode.java
index b4cb002b..9be08ad9 100644
--- a/src/main/java/com/seaofnodes/simple/node/StopNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/StopNode.java
@@ -21,12 +21,13 @@ public class StopNode extends CFGNode {
}
@Override
- StringBuilder _print1(StringBuilder sb, BitSet visited) {
+ public StringBuilder _print1(StringBuilder sb, BitSet visited) {
// For the sake of many old tests, and single value prints as "return val"
if( ret()!=null ) return ret()._print0(sb,visited);
sb.append("Stop[ ");
for( Node ret : _inputs )
- ret._print0(sb, visited).append(" ");
+ if( ret!=null )
+ ret._print0(sb, visited).append(" ");
return sb.append("]");
}
diff --git a/src/main/java/com/seaofnodes/simple/node/StoreNode.java b/src/main/java/com/seaofnodes/simple/node/StoreNode.java
index 93294dba..a6992f04 100644
--- a/src/main/java/com/seaofnodes/simple/node/StoreNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/StoreNode.java
@@ -20,7 +20,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;
}
@@ -33,7 +33,7 @@ public class StoreNode extends MemOpNode {
public Node val() { return in(4); }
@Override
- StringBuilder _print1(StringBuilder sb, BitSet visited) {
+ public StringBuilder _print1(StringBuilder sb, BitSet visited) {
return sb.append(".").append(_name).append("=").append( val()).append(";");
}
@@ -84,6 +84,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;
}
@@ -95,7 +100,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;
}
@@ -106,7 +111,7 @@ public class StoreNode extends MemOpNode {
TypeMemPtr tmp = (TypeMemPtr)ptr()._type;
if( 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 076b342a..4efc0de9 100644
--- a/src/main/java/com/seaofnodes/simple/node/StructNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/StructNode.java
@@ -14,7 +14,7 @@ public class StructNode extends Node {
@Override public String label() { return _ts==null ? "STRUCT?" : _ts.str(); }
@Override
- StringBuilder _print1(StringBuilder sb, BitSet visited) {
+ public StringBuilder _print1(StringBuilder sb, BitSet visited) {
if( _ts==null ) return sb.append("STRUCT?");
sb.append(_ts._name).append(" {");
for( int i=0; i<nIns(); i++ ) {
@@ -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/SubFNode.java b/src/main/java/com/seaofnodes/simple/node/SubFNode.java
index b591718d..5c7e969f 100644
--- a/src/main/java/com/seaofnodes/simple/node/SubFNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/SubFNode.java
@@ -13,7 +13,7 @@ public class SubFNode extends Node {
@Override public String glabel() { return "-"; }
@Override
- StringBuilder _print1(StringBuilder sb, BitSet visited) {
+ public StringBuilder _print1(StringBuilder sb, BitSet visited) {
in(1)._print0(sb.append("("), visited);
in(2)._print0(sb.append("-"), visited);
return sb.append(")");
diff --git a/src/main/java/com/seaofnodes/simple/node/SubNode.java b/src/main/java/com/seaofnodes/simple/node/SubNode.java
index 339c65ca..4290ff9b 100644
--- a/src/main/java/com/seaofnodes/simple/node/SubNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/SubNode.java
@@ -13,7 +13,7 @@ public class SubNode extends Node {
@Override public String glabel() { return "-"; }
@Override
- StringBuilder _print1(StringBuilder sb, BitSet visited) {
+ public StringBuilder _print1(StringBuilder sb, BitSet visited) {
in(1)._print0(sb.append("("), visited);
in(2)._print0(sb.append("-"), visited);
return sb.append(")");
diff --git a/src/main/java/com/seaofnodes/simple/node/ToFloatNode.java b/src/main/java/com/seaofnodes/simple/node/ToFloatNode.java
index 1f4d67e0..c098ad99 100644
--- a/src/main/java/com/seaofnodes/simple/node/ToFloatNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/ToFloatNode.java
@@ -14,7 +14,7 @@ public class ToFloatNode extends Node {
@Override public String glabel() { return "(flt)"; }
@Override
- StringBuilder _print1(StringBuilder sb, BitSet visited) {
+ public StringBuilder _print1(StringBuilder sb, BitSet visited) {
return in(1)._print0(sb.append("(flt)"), visited);
}
diff --git a/src/main/java/com/seaofnodes/simple/node/XCtrlNode.java b/src/main/java/com/seaofnodes/simple/node/XCtrlNode.java
index da021246..8c620a04 100644
--- a/src/main/java/com/seaofnodes/simple/node/XCtrlNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/XCtrlNode.java
@@ -8,7 +8,7 @@ import java.util.BitSet;
public class XCtrlNode extends CFGNode {
public XCtrlNode() { super(new Node[]{CodeGen.CODE._start}); }
@Override public String label() { return "Xctrl"; }
- @Override StringBuilder _print1(StringBuilder sb, BitSet visited) { return sb.append("Xctrl"); }
+ @Override public StringBuilder _print1(StringBuilder sb, BitSet visited) { return sb.append("Xctrl"); }
@Override public boolean isConst() { return true; }
@Override public Type compute() { return Type.XCONTROL; }
@Override public Node idealize() { return null; }
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/AddARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/AddARM.java
index c4014571..f37738bf 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,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.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,arm.OP_ADD); }
// General form: "rd = rs1 + rs2"
@Override public void asm(CodeGen code, SB sb) {
sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" + ").p(code.reg(in(2)));
}
- @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..209f24f4 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,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.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,arm.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..a1cd8306 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,27 @@
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.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), arm.OPI_ADD,_imm);
}
-
// General form: "addi rd = rs1 + imm"
- @Override
- public void asm(CodeGen code, SB sb) {
- sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" + #");
- _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..ad76ca4c 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,19 @@
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.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,arm.OP_AND); }
// General form: #rd = rs1 & rs2
@Override public void asm(CodeGen code, SB sb){
sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" & ").p(code.reg(in(2)));
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/AndIARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/AndIARM.java
index ee05654b..1f12ef13 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,24 @@
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.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), arm.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(arm.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..342b7519 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,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.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,arm.OP_ASR); }
@Override public void asm(CodeGen code, SB sb) {
-
sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" >> ").p(code.reg(in(2)));
}
- @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..a1775dde 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,31 @@
package com.seaofnodes.simple.node.cpus.arm;
-
-import com.seaofnodes.simple.*;
+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(arm.imm_shift(arm.OPI_ASR,_imm, 0b111111, rn, rd));
+ }
+ // General form: "asri rd = rs1 >> imm"
@Override public void asm(CodeGen code, SB sb) {
- sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" >> #");
- _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..b36a395b 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,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 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 +27,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( arm.b_cond(arm.OP_BRANCH, 0, arm.make_condition(_bop)) );
+ } else {
+ if( _bop=="!=" ) return; // Inverted, no code
+ enc.add4(arm.b(arm.OP_UJMP, 0));
+ }
+ enc.jump(this,cproj(0));
+ }
- // 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,arm.b_cond(arm.OP_BRANCH, delta, arm.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 4359c045..7f6fe93c 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,11 @@
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.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 +17,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(arm.b(arm.OP_CALL,0)); // Target patched at link time
+ }
+
+ // Delta is from opcode start, but X86 measures from the end of the 5-byte encoding
+ @Override public byte encSize(int delta) { return 4; }
+
+ // Delta is from opcode start
+ @Override public void patch( Encoding enc, int opStart, int opLen, int delta ) {
+ enc.patch4(opStart,arm.b(arm.OP_CALL,delta));
}
@Override public void asm(CodeGen code, SB sb) {
sb.p(_name);
for( int i=0; i<nargs(); i++ )
- sb.p(code.reg(arg(i))).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..87dde38c 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,25 @@
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 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(arm.blr(arm.OP_CALLRARM, self));
}
@Override public void asm(CodeGen code, SB sb) {
@@ -29,4 +28,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..dbc86f62 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,17 @@
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.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,arm.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..8a4f242a 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,34 @@
package com.seaofnodes.simple.node.cpus.arm;
import com.seaofnodes.simple.*;
-import com.seaofnodes.simple.codegen.CodeGen;
-import com.seaofnodes.simple.codegen.RegMask;
+import com.seaofnodes.simple.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), arm.OPI_CMP,_imm); }
// General form: "cmp rs1, 1"
@Override public void asm(CodeGen code, SB sb) {
@@ -49,6 +37,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..40bd8387 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,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.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,arm.OP_DIV,3); }
// General form: "div dst /= src"
@Override public void asm(CodeGen code, SB sb) {
sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" / ").p(code.reg(in(2)));
}
- @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..72b76444 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,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.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,arm.OPF_DIV); }
// General form: "VDIF = dst /= src"
@Override public void asm(CodeGen code, SB sb) {
sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" / ").p(code.reg(in(2)));
}
-
- @Override public String op() { return "divf"; }
}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/FloatARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/FloatARM.java
index 7fafc31c..6de65b03 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
@@ -2,28 +2,36 @@ package com.seaofnodes.simple.node.cpus.arm;
import com.seaofnodes.simple.SB;
import com.seaofnodes.simple.Utils;
-import com.seaofnodes.simple.codegen.CodeGen;
-import com.seaofnodes.simple.codegen.RegMask;
+import com.seaofnodes.simple.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(arm.load_pc(arm.OPF_ARM, 0, dst));
+ }
+
+ // Delta is from opcode start.
+ @Override public byte encSize(int delta) { return 4; }
+
+ // Delta is from opcode start
+ @Override public void patch( Encoding enc, int opStart, int opLen, int delta ) {
+ short dst = (short)(enc.reg(this) - arm.D_OFFSET);
+ enc.patch4(opStart, arm.load_pc(arm.OPF_ARM, delta, dst));
}
// Human-readable form appended to the SB. Things like the encoding,
@@ -31,6 +39,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..32777f03 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.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);
+ if( _hasCalls ) // If non-leaf, pad to 16b
+ _frameAdjust = ((_frameAdjust+8) & -16);
+ }
+ @Override public void encoding( Encoding enc ) {
+ int sz = _frameAdjust;
+ if( sz == 0 ) return; // Skip if no frame adjust
+ if( sz >= 1L<<12 ) throw Utils.TODO();
+ enc.add4(arm.imm_inst(arm.OPI_ADD, -sz&0xFFF, arm.RSP, arm.RSP));
}
}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/I2F8ARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/I2F8ARM.java
index b106ab8a..5ecc98b6 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,25 @@
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.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 = arm.float_cast(arm.OP_FLOAT_C, 1, reg1, self);
+ enc.add4(body);
}
// General form: "i2f8 (flt)int_value"
@Override public void asm(CodeGen code, SB sb) {
sb.p(code.reg(this)).p(" = ").p("(flt)").p(code.reg(in(1)));
}
+
}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/IntARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/IntARM.java
index 1f81300e..0a07b07a 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,59 @@
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.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();
+ int nb0 = 0;
+ int nb1 = 0;
+ // Count number of 0000 and FFFF blocks
+ for (int i=0; i<64; i+=16) {
+ int block = (int)(x >> i) & 0xFFFF;
+ if (block == 0) nb0++;
+ if (block == 0xFFFF) nb1++;
+ }
+ int pattern;
+ int op;
+ if(nb0 >= nb1) {
+ // More 0 blocks then F blocks, use movz
+ pattern = 0;
+ op = arm.OP_MOVZ;
+ } else {
+ // More F blocks then 0 blocks, use movn
+ pattern = 0xFFFF;
+ op = arm.OP_MOVN;
+ }
+ int invert = pattern;
+ for (int i=0; i<4; i++) {
+ int block = (int)x & 0xFFFF;
+ x >>= 16;
+ if (block != pattern) {
+ enc.add4(arm.mov(op, i, block ^ invert, self));
+ op = arm.OP_MOVK;
+ invert = 0;
+ }
+ }
+ if (op != arm.OP_MOVK) {
+ // All blocks are the same, special case
+ enc.add4(arm.mov(op, 0, 0, self));
+ }
}
// Human-readable form appended to the SB. Things like the encoding,
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..44c13321 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,12 +1,10 @@
package com.seaofnodes.simple.node.cpus.arm;
-
import com.seaofnodes.simple.SB;
-import com.seaofnodes.simple.codegen.CodeGen;
-import com.seaofnodes.simple.codegen.RegMask;
+import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.LoadNode;
import com.seaofnodes.simple.node.Node;
-
+import com.seaofnodes.simple.type.TypeFloat;
// Load memory addressing on ARM
// Support imm, reg(direct), or reg+off(indirect) addressing
@@ -14,24 +12,28 @@ import com.seaofnodes.simple.node.Node;
// 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 RegMask regmap(int i) {
- return arm.RMASK;
- }
-
- // Wide mask loads both ints and floats; encoding varies.
+ @Override public String op() { return "ld"+_sz; }
@Override public RegMask outregmap() { return arm.MEM_MASK; }
+ private static final int[] OP_LOADS = new int[]{ arm.OP_LOAD_IMM_8, arm.OP_LOAD_IMM_16, arm.OP_LOAD_IMM_32, arm.OP_LOAD_IMM_64, };
+ private int imm_op() {
+ return _declaredType == TypeFloat.F32 ? arm.OPF_LOAD_IMM_32
+ : _declaredType == TypeFloat.F64 ? arm.OPF_LOAD_IMM_64
+ : OP_LOADS[_declaredType.log_size()];
+ }
- @Override public String op() { return "ld"+_sz; }
+ private static final int[] OP_LOAD_RS = new int[]{ arm.OP_LOAD_R_8 , arm.OP_LOAD_R_16 , arm.OP_LOAD_R_32 , arm.OP_LOAD_R_64, };
+ // ldr(immediate - unsigned offset) | ldr(register)
+ @Override public void encoding( Encoding enc ) {
+ ldst_encode(enc, imm_op(), OP_LOAD_RS[_declaredType.log_size()], this, size());
+ }
@Override public void asm(CodeGen code, SB sb) {
sb.p(code.reg(this)).p(",");
asm_address(code,sb);
}
-
}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/LslARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/LslARM.java
index 6108c889..055d6dcc 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,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.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,arm.OP_LSL); }
@Override public void asm(CodeGen code, SB sb) {
-
sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" << ").p(code.reg(in(2)));
}
- @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..94d3e25b 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,33 @@
package com.seaofnodes.simple.node.cpus.arm;
-
-import com.seaofnodes.simple.*;
+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(arm.imm_shift(arm.OPI_LSL, 64 - _imm, (64 - _imm) - 1, rn, rd));
+ }
+ // General form: "lsli rd = rs1 << imm"
@Override public void asm(CodeGen code, SB sb) {
- sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" << #");
- _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..a691e0fe 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,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.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,arm.OP_LSR); }
@Override public void asm(CodeGen code, SB sb) {
sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" >>> ").p(code.reg(in(2)));
}
- @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..094cd30f 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
@@ -2,40 +2,28 @@ 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.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(arm.imm_shift(arm.OPI_LSR,_imm, 0b111111, rn,rd));
+ }
+ // General form: "lsri rd = rs1 >>> imm"
@Override public void asm(CodeGen code, SB sb) {
- sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" >>> #");
- _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 adffa501..6d8344a2 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,57 +1,68 @@
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.*;
-import java.io.ByteArrayOutputStream;
+
import java.lang.StringBuilder;
import java.util.BitSet;
-public class MemOpARM extends MemOpNode implements MachNode {
- final int _off; // Limit 32 bits
- final int _imm; // Limit 32 bits
+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 public StringBuilder _printMach(StringBuilder sb, BitSet visited) { return sb.append(".").append(_name); }
- @Override public String label() { return op(); }
@Override public Type compute() { throw Utils.TODO(); }
@Override public Node idealize() { throw Utils.TODO(); }
+ 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 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);
+ int body = off() == null
+ ? arm.load_str_imm(opcode_imm, _off, ptr, val, size)
+ : arm.indr_adr(opcode_reg, off, arm.STORE_LOAD_OPTION.SXTX, 0, ptr, val);
+ enc.add4(body);
+ }
SB asm_address(CodeGen code, SB sb) {
sb.p("[").p(code.reg(ptr())).p("+");
- if( 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..3514db9b 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,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.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,arm.OP_MUL,31); }
// General form: "rd = rs1 * rs2"
@Override public void asm(CodeGen code, SB sb) {
sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" * ").p(code.reg(in(2)));
}
-
- @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..89dba172 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,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.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,arm.OPF_MUL); }
// Default on double precision for now(64 bits)
// General form: "VMUL.f32 rd = src1 * src2
@Override public void asm(CodeGen code, SB sb) {
sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" * ").p(code.reg(in(2)));
}
-
- @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 bd88fa83..cb51eea2 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,36 @@
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 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 aliases edges
- @Override public RegMask regmap(int i) { return i == 1 ? arm. X0_MASK : null; }
- @Override public RegMask outregmap(int i) { return i == 1 ? arm. X0_MASK : null; }
- @Override public RegMask outregmap() { return null; }
-
- // Encoding is appended into the byte array; size is returned
- @Override public int encoding(ByteArrayOutputStream bytes) {
- throw Utils.TODO();
+ @Override public void encoding( Encoding enc ) {
+ // BL(branch with link)
+ enc.external(this,"calloc").
+ add4(arm.mov(arm.OP_MOVZ, 0, 1, 0)). // movz x0,#1
+ add4(arm.b(arm.OP_CALL, 0));
}
- // 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, arm.b_calloc(arm.OP_CALL, delta-4));
+ } else {
+ throw Utils.TODO();
+ }
+ }
+ // General form: "alloc #bytes PC"
@Override public void asm(CodeGen code, SB sb) {
- sb.p(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..5a8d882d 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,28 @@
package com.seaofnodes.simple.node.cpus.arm;
import com.seaofnodes.simple.*;
-import com.seaofnodes.simple.codegen.CodeGen;
-import com.seaofnodes.simple.codegen.LRG;
-import com.seaofnodes.simple.codegen.RegMask;
+import com.seaofnodes.simple.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 = arm.imm_inst(arm.OP_SUBS, 0, reg1, self);
+ enc.add4(subs);
+ int cset = arm.cond_set(arm.OP_CSET, 31, arm.COND.EQ, 63, reg1);
+ enc.add4(cset);
}
@Override public void asm(CodeGen code, SB sb) { sb.p(code.reg(this)); }
- @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..b5cfa8cf 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,20 @@
package com.seaofnodes.simple.node.cpus.arm;
import com.seaofnodes.simple.SB;
-import com.seaofnodes.simple.Utils;
-import com.seaofnodes.simple.codegen.CodeGen;
-import com.seaofnodes.simple.codegen.RegMask;
-import com.seaofnodes.simple.node.MachConcreteNode;
-import com.seaofnodes.simple.node.MachNode;
-import com.seaofnodes.simple.node.Node;
-
-import java.io.ByteArrayOutputStream;
+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,arm.OP_OR); }
// General form: #rd = rs1 & rs2
@Override public void asm(CodeGen code, SB sb) {
-
sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" | ").p(code.reg(in(2)));
}
- @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..2a2d199a 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,24 @@
package com.seaofnodes.simple.node.cpus.arm;
-import com.seaofnodes.simple.SB;
-import com.seaofnodes.simple.Utils;
-import com.seaofnodes.simple.codegen.CodeGen;
-import com.seaofnodes.simple.codegen.RegMask;
-import com.seaofnodes.simple.node.MachConcreteNode;
-import com.seaofnodes.simple.node.MachNode;
-import com.seaofnodes.simple.node.Node;
+import com.seaofnodes.simple.*;
+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), arm.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..2136ebf5 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,15 @@
package com.seaofnodes.simple.node.cpus.arm;
import com.seaofnodes.simple.*;
-import com.seaofnodes.simple.codegen.CodeGen;
-import com.seaofnodes.simple.codegen.RegMask;
-import com.seaofnodes.simple.node.FunNode;
-import com.seaofnodes.simple.node.ReturnNode;
-import com.seaofnodes.simple.node.MachNode;
-import com.seaofnodes.simple.type.TypeFloat;
-
-import java.io.ByteArrayOutputStream;
-
+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(arm.imm_inst(arm.OPI_ADD, (frameAdjust*-8)&0xFFF, arm.RSP, arm.RSP));
+ enc.add4(arm.ret(arm.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..246e482a 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,13 @@
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.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 +15,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 = arm.cset(arm.OP_CSET,0b11111, arm.make_condition(_bop), 0b011111, enc.reg(this));
+ enc.add4(body);
}
@Override public void asm(CodeGen code, SB sb) {
@@ -36,6 +29,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..62bb75aa 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,66 @@
package com.seaofnodes.simple.node.cpus.arm;
+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(arm.load_str_imm(arm.OP_STORE_IMM_64, off, arm.RSP, src, 8));
+ return;
+ }
+
+ if(src >= arm.MAX_REG) {
+ // Load from SP
+ int off = enc._fun.computeStackOffset(enc._code,src);
+ if( dstX ) dst -= arm.D_OFFSET;
+ enc.add4(arm.load_str_imm(arm.OP_LOAD_IMM_64, off, arm.RSP, dst, 8));
+ return;
+ }
+
+ // pick opcode based on regs
+ if(!dstX && !srcX) {
+ // GPR->GPR
+ enc.add4(arm.mov_reg(arm.OP_MOV, src, dst));
+ } else if(dstX && srcX) {
+ // FPR->FPR
+ // fmov reg
+ enc.add4(arm.f_mov_reg(arm.OP_FMOV_REG, src - arm.D_OFFSET,dst - arm.D_OFFSET));
+ } else if(!srcX && dstX) {
+ // GPR->FPR
+ // FMOV(general) 64 bits to DOUBLE-PRECISION
+ enc.add4(arm.f_mov_general(arm.OP_FMOV, 0b01, 0, 0b111, src, dst - arm.D_OFFSET));
+ } else if( srcX && !dstX) {
+ //FPF->GPR
+ // FMOV(general) DOUBLE-PRECISION to 64 bits
+ enc.add4(arm.f_mov_general(arm.OP_FMOV, 0b01, 0, 0b110, src - arm.D_OFFSET, dst));
+ }
+ }
+
+ // General form: "mov dst = src"
+ @Override public void asm(CodeGen code, SB sb) {
+ FunNode fun = code._encoding==null ? null : code._encoding._fun;
+ sb.p(code.reg(this,fun)).p(" = ").p(code.reg(in(1),fun));
}
}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/StoreARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/StoreARM.java
index 49045b28..688d8731 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,40 @@
package com.seaofnodes.simple.node.cpus.arm;
import com.seaofnodes.simple.SB;
-import com.seaofnodes.simple.Utils;
-import com.seaofnodes.simple.codegen.CodeGen;
-import com.seaofnodes.simple.codegen.RegMask;
-import com.seaofnodes.simple.node.MachConcreteNode;
-import com.seaofnodes.simple.node.MachNode;
-import com.seaofnodes.simple.node.Node;
+import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.StoreNode;
-import java.io.ByteArrayOutputStream;
-import java.util.BitSet;
+import com.seaofnodes.simple.node.Node;
+import com.seaofnodes.simple.type.TypeFloat;
// 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;
- throw Utils.TODO();
- }
- // Register mask allowed as a result. 0 for no register.
@Override public RegMask outregmap() { return null; }
- // Encoding is appended into the byte array; size is returned
- @Override public int encoding(ByteArrayOutputStream bytes) {
- throw Utils.TODO();
+ private static final int[] OP_STORES = new int[]{ arm.OP_STORE_IMM_8, arm.OP_STORE_IMM_16, arm.OP_STORE_IMM_32, arm.OP_STORE_IMM_64, };
+ private int imm_op() {
+ return _declaredType == TypeFloat.F32 ? arm.OPF_STORE_IMM_32
+ : _declaredType == TypeFloat.F64 ? arm.OPF_STORE_IMM_64
+ : OP_STORES[_declaredType.log_size()];
}
+ private static final int[] OP_STORE_RS = new int[]{ arm.OP_STORE_R_8, arm.OP_STORE_R_16, arm.OP_STORE_R_32, arm.OP_STORE_R_64, };
+
+ @Override public void encoding( Encoding enc ) {
+ ldst_encode(enc, imm_op(), OP_STORE_RS[_declaredType.log_size()], val(), size());
+ }
@Override public void asm(CodeGen code, SB sb) {
asm_address(code,sb).p(",");
if( val()==null ) sb.p("#").p(_imm);
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..552b46df 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,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.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,arm.OP_SUB); }
// General form: "sub # rd = rs1 - rs2"
@Override public void asm(CodeGen code, SB sb) {
sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" - ").p(code.reg(in(2)));
}
-
- @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..0d8d3c10 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,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.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,arm.OPF_SUB); }
// Default on double precision for now(64 bits)
// General form: "vsub.f32 rd = src1 + src2
@Override public void asm(CodeGen code, SB sb) {
sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" - ").p(code.reg(in(2)));
}
-
- @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..b882c5a4 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,29 @@
package com.seaofnodes.simple.node.cpus.arm;
+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), arm.OPI_SUB,_imm);
}
// General form: "subi rd = rs1 - imm"
@Override public void asm(CodeGen code, SB sb) {
- sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" - #");
- _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..16b56553 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,53 @@
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.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 = arm.adrp(1,0, arm.OP_ADRP, 0,self);
+ // add x0, x0, 0
+ enc.add4(adrp);
+ arm.imm_inst(enc,arm.OPI_ADD,0, 0);
+ }
- // 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, arm.adrp(1, adrp_delta & 0b11, 0b10000, adrp_delta >> 2, rpc));
+ // low 12 bits via add
+ enc.patch4(next, arm.imm_inst_l(arm.OPI_ADD, delta & 0xfff, rpc));
+ } else {
+ // should not happen as one instruction is 4 byte, and TFP arm encodes 2.
+ throw Utils.TODO();
+ }
+ }
+
@Override public void asm(CodeGen code, SB sb) {
String reg = code.reg(this);
_con.print(sb.p(reg).p(" #"));
}
-
}
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..a69c3c7c
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/arm/UJmpARM.java
@@ -0,0 +1,48 @@
+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.type.Type;
+import java.util.BitSet;
+
+// unconditional jump
+public class UJmpARM extends CFGNode implements MachNode, RIPRelSize {
+ UJmpARM() { }
+ @Override public String op() { return "jmp"; }
+ @Override public String label() { return op(); }
+ @Override public StringBuilder _print1( StringBuilder sb, BitSet visited ) {
+ return sb.append("jmp ");
+ }
+ @Override public RegMask regmap(int i) {return null; }
+ @Override public RegMask outregmap() { return null; }
+ @Override public Type compute() { throw Utils.TODO(); }
+ @Override public Node idealize() { throw Utils.TODO(); }
+ @Override public void encoding( Encoding enc ) {
+ enc.jump(this,uctrl());
+ int body = arm.b(arm.OP_UJMP, 0);
+ enc.add4(body);
+ }
+
+ // Delta is from opcode start
+ @Override public byte encSize(int delta) {
+ if( -(1<<26) <= delta && delta < (1<<26) ) return 4;
+ // 2 word encoding needs a tmp register, must teach RA
+ throw Utils.TODO();
+ }
+
+ // Delta is from opcode start
+ @Override public void patch( Encoding enc, int opStart, int opLen, int delta ) {
+ if( opLen==4 ) {
+ enc.patch4(opStart,arm.b(arm.OP_UJMP, delta));
+ } else {
+ throw Utils.TODO();
+ }
+ }
+
+ @Override public void asm(CodeGen code, SB sb) {
+ CFGNode target = uctrl();
+ //assert target.nOuts() > 1; // Should optimize jmp to empty targets
+ sb.p(label(target));
+ }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/arm/XorARM.java b/src/main/java/com/seaofnodes/simple/node/cpus/arm/XorARM.java
index fd127b18..ec86cfa5 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,19 @@
package com.seaofnodes.simple.node.cpus.arm;
import com.seaofnodes.simple.SB;
-import com.seaofnodes.simple.Utils;
-import com.seaofnodes.simple.codegen.CodeGen;
-import com.seaofnodes.simple.codegen.RegMask;
-import com.seaofnodes.simple.node.MachConcreteNode;
-import com.seaofnodes.simple.node.MachNode;
-import com.seaofnodes.simple.node.Node;
-import com.seaofnodes.simple.type.TypeInteger;
-import java.io.ByteArrayOutputStream;
+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,arm.OP_XOR); }
// General form: "rd = x1 ^ x2"
@Override public void asm(CodeGen code, SB sb) {
sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" ^ ").p(code.reg(in(2)));
}
- @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..e2582d22 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,27 @@
package com.seaofnodes.simple.node.cpus.arm;
-import com.seaofnodes.simple.SB;
-import com.seaofnodes.simple.Utils;
-import com.seaofnodes.simple.codegen.CodeGen;
-import com.seaofnodes.simple.codegen.RegMask;
import com.seaofnodes.simple.node.MachConcreteNode;
import com.seaofnodes.simple.node.MachNode;
-import com.seaofnodes.simple.node.Node;
-import com.seaofnodes.simple.type.TypeInteger;
-import java.io.ByteArrayOutputStream;
-public class XorIARM extends MachConcreteNode implements MachNode{
- final TypeInteger _ti;
- XorIARM(Node xor, TypeInteger ti) {super(xor); _inputs.pop(); _ti = ti;}
+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), arm.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 fd207157..3e39adc0 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,34 @@
package com.seaofnodes.simple.node.cpus.arm;
import com.seaofnodes.simple.Utils;
-import com.seaofnodes.simple.codegen.LRG;
-import com.seaofnodes.simple.codegen.Machine;
-import com.seaofnodes.simple.codegen.RegMask;
+import com.seaofnodes.simple.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 +41,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 +57,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);
@@ -81,6 +81,102 @@ public class arm extends Machine {
static final RegMask D6_MASK = new RegMask(D6);
static final RegMask D7_MASK = new RegMask(D7);
+ // major opcode: OP
+ public static int OP_ADD = 0b10_001_011;
+ public static int OPF_ADD = 0b00_011_110;
+ public static int OPF_OP_ADD = 0b00_101_0;
+ public static int OPI_ADD = 0b10_010_00100;
+
+ public static int OPI_SUB = 0b11_010_00100;
+ public static int OP_UJMP = 0b000101;
+
+ public static int OP_ADRP = 0b10000;
+
+ public static int OP_ASR = 0b1010;
+ public static int OPI_ASR = 0b10_0100_1101;
+
+ public static int OP_LSL = 0b1000;
+ public static int OPI_LSL =0b1101001101;
+
+ public static int OP_LSR = 0b1001;
+ public static int OPI_LSR = 0b1101001101;
+
+ public static int OP_BRANCH = 0b01_0101_00;
+ public static int OP_CALL = 0b10_010_1;
+
+ public static int OP_CALLRARM = 0b1101011000111111000000;
+
+ public static int OP_SUBS = 0b1111000100;
+ public static int OP_CSET = 0b10011010100;
+ public static int OP_CMP = 0b11101011;
+ public static int OPI_CMP = 0b1111000100;
+
+ public static int OP_DIV = 0b10011010110;
+ public static int OPF_DIV = 0b000110;
+
+ public static int OP_MUL = 0b10011011000;
+ public static int OPF_MUL = 0b10;
+
+ public static int OPF_ARM = 0b01011100;
+
+ public static int OP_FLOAT_C = 0b10011110;
+
+ public static int OP_XOR = 0b11001010;
+ public static int OPI_XOR = 0b110100100;
+
+ public static int OP_AND = 0b10_0010_10;
+ public static int OPI_AND = 0b10_0100_100;
+
+ public static int OP_OR = 0b10101010;
+ public static int OPI_OR = 0b101100100;
+
+ public static int OP_SUB = 0b11001011;
+ public static int OPF_SUB = 0b1110;
+
+ public static int OP_MOVK = 0b111100101;
+ public static int OP_MOVN = 0b100100101;
+ public static int OP_MOVZ = 0b110100101;
+
+ public static int OP_RET = 0b1101011001011111000000;
+ // Store/load opcodes
+ public static int OP_LOAD_R_64 = 0b11111000011;
+ public static int OP_LOAD_R_32 = 0b10111000011;
+ public static int OP_LOAD_R_16 = 0b01111000010;
+ public static int OP_LOAD_R_8 = 0b00111000011;
+
+ public static int OP_LOAD_IMM_64 = 0b1111100101;
+ public static int OP_LOAD_IMM_32 = 0b1011100101;
+ public static int OP_LOAD_IMM_16 = 0b0111100101;
+ public static int OP_LOAD_IMM_8 = 0b0011100101;
+
+ // load
+ public static int OPF_LOAD_R_64 = 0b11111100011;
+ public static int OPF_LOAD_R_32 = 0b10111100011;
+
+ public static int OPF_LOAD_IMM_64 = 0b1111110101;
+ public static int OPF_LOAD_IMM_32 = 0b1011110101;
+
+ public static int OP_STORE_R_64 = 0b11111000001;
+ public static int OP_STORE_R_32 = 0b10111000001;
+ public static int OP_STORE_R_16 = 0b01111000001;
+ public static int OP_STORE_R_8 = 0b00111000001;
+
+ public static int OP_STORE_IMM_64 = 0b1111100100;
+ public static int OP_STORE_IMM_32 = 0b1011100100;
+ public static int OP_STORE_IMM_16 = 0b0111100100;
+ public static int OP_STORE_IMM_8 = 0b0011100100;
+
+ public static int OPF_STORE_R_64 = 0b11111100001;
+ public static int OPF_STORE_R_32 = 0b10111100001;
+
+ public static int OPF_STORE_IMM_32 = 0b1111110100;
+ public static int OPF_STORE_IMM_64 = 0b1111110100;
+
+
+ public static int OP_FMOV = 0b10011110;
+ public static int OP_FMOV_REG = 0b00011110;
+ // https://docsmirror.github.io/A64/2023-06/mov_orr_log_shift.html
+ public static int OP_MOV = 0b10101010000;
// Calling convention; returns a machine-specific register
// for incoming argument idx.
// index 0 for control, 1 for memory, real args start at index 2
@@ -105,13 +201,376 @@ public class arm extends Machine {
D7_MASK,
};
- static RegMask callInMask( TypeFunPtr tfp, int idx ) {
- if( idx==0 ) return RPC_MASK;
+ // ARM ENCODING
+ public enum OPTION {
+ UXTB,
+ UXTH,
+ UXTX,
+ SXTB,
+ SXTH,
+ SXTW,
+ SXTX,
+ }
+
+ static public int cset(int opcode, int rm, COND cond, int rn, int rd) {
+ assert 0 <= rm && rm < 32;
+ assert 0 <= rn && rn < 32;
+ assert 0 <= rd && rd < 32;
+ return (opcode << 21) | (rm << 15) | (cond.ordinal() << 12) | (rn << 5) | rd;
+ }
+
+ static public int cset(int opcode, COND cond, int rn, int rd) {
+ assert 0 <= rn && rn < 32;
+ assert 0 <= rd && rd < 32;
+ return (opcode << 16) | (cond.ordinal() << 12) | (rn << 5) | rd;
+ }
+
+ public enum STORE_LOAD_OPTION {
+ UXTW, // base+ u32 index [<< logsize]
+ UXTX, // base+ u64 index [<< logsize]
+ SXTW, // base+ s32 index [<< logsize]
+ SXTX, // base+ s64 index [<< logsize]
+ }
+
+ public enum COND {
+ EQ,
+ NE,
+ CS,
+ CC,
+ MI,
+ PL,
+ VS,
+ VC,
+ HI,
+ LS,
+ GE,
+ LT,
+ GT,
+ LE,
+ AL,
+ NV
+ }
+
+ // True if signed 9-bit immediate
+ private static boolean imm9(TypeInteger ti) {
+ // 55 = 64-9
+ return ti.isConstant() && ((ti.value()<<55)>>55) == ti.value();
+ }
+ // True if signed 12-bit immediate
+ private static boolean imm12(TypeInteger ti) {
+ // 52 = 64-12
+ return ti.isConstant() && ((ti.value()<<52)>>52) == ti.value();
+ }
+
+
+ // Can we encode this in ARM's 12-bit LOGICAL immediate form?
+ // Some combination of shifted bit-masks.
+ private static int imm12Logical(TypeInteger ti) {
+ if( !ti.isConstant() ) return -1;
+ if( !ti.isConstant() ) return -1;
+ long val = ti.value();
+ if (val == 0 || val == -1) return -1; // Special cases are not allowed
+ int immr = 0;
+ // Rotate until we have 0[...]1
+
+ // Rotate until:
+ // The number is negative (MSB is 1)
+ // Or the LSB is not 1
+ while (val < 0 || (val & 1)==0) {
+ // circular rotation
+ val = (val >>> 63) | (val << 1);
+ immr++;
+ }
+ // Start by assuming that val might be made of two 32-bit chunks that repeat.
+ int size = 32;
+ long pattern = val;
+ // Is upper half of pattern the same as the lower?
+ while ((pattern & ((1L<<size)-1)) == (pattern >> size)) {
+ // Then only take one half
+ pattern >>= size;
+ size >>= 1;
+ }
+ size <<= 1;
+ int imms = Long.bitCount(pattern);
+ // Pattern should now be zeros followed by ones 0000011111
+ if (pattern != (1L<<imms)-1) return -1;
+ imms--;
+ if (size == 64) return 0x1000 | immr << 6 | imms;
+ return (32-size)<<1 | immr << 6 | imms;
+ }
+
+ // sh is encoded in opcode
+ public static int imm_inst(int opcode, int imm12, int rn, int rd) {
+ assert 0 <= rn && rn < 32;
+ assert 0 <= rd && rd < 32;
+ assert opcode >=0 && imm12 >= 0; // Caller zeros high order bits
+ return (opcode << 22) | (imm12 << 10) | (rn << 5) | rd;
+ }
+
+ public static int imm_shift(int opcode, int imm, int imms, int rn, int rd) {
+ assert 0 <= rn && rn < 32;
+ assert 0 <= rd && rd < 32;
+ return (opcode << 22) | (1 << 22) | (imm << 16) | (imms << 10) | (rn << 5) | rd;
+ }
+
+ public static void imm_inst(Encoding enc, Node n,Node n2, int opcode, int imm12) {
+ short self = enc.reg(n);
+ short reg1 = enc.reg(n2);
+
+ int body = imm_inst(opcode, imm12&0xFFF, reg1, self);
+ enc.add4(body);
+ }
+
+ public static void imm_inst_subs(Encoding enc, Node n,Node n2, int opcode, int imm12) {
+ short reg1 = enc.reg(n2);
+ // 31 = 11111
+ int body = imm_inst(opcode, imm12&0xFFF, reg1, 31);
+ enc.add4(body);
+ }
+
+ // for cases where rs1 and dst are the same, eg add x0, x0, 1
+ public static void imm_inst(Encoding enc, int opcode, int imm12, int self) {
+ int body = imm_inst(opcode, imm12&0xFFF, self, self);
+ enc.add4(body);
+ }
+
+ public static void imm_inst_n(Encoding enc, Node n, Node n2, int opcode, int imm13) {
+ short self = enc.reg(n);
+ short reg1 = enc.reg(n2);
+ int body = imm_inst_n(opcode, imm13, reg1, self);
+ enc.add4(body);
+ }
+
+ // nth bit comes from immediate and not opcode
+ public static int imm_inst_n(int opcode, int imm13, int rn, int rd) {
+ assert 0 <= rn && rn < 32;
+ assert 0 <= rd && rd < 32;
+ assert 0 <= imm13 && imm13 <= 0x1FFF;
+ return (opcode << 23) | (imm13 << 10) | (rn << 5) | rd;
+ }
+
+ public static int imm_inst_l(int opcode, int imm12, int self) {
+ int body = imm_inst(opcode, imm12&0xFFF, self, self);
+ return body;
+ }
+
+ // for cases where rs1 and dst are the same, eg add x0, x0, 1
+ public static int imm_inst_l(Encoding enc, Node n, int opcode, int imm12) {
+ short self = enc.reg(n);
+ short reg1 = enc.reg(n.in(1));
+ int body = imm_inst(opcode, imm12&0xFFF, reg1, self);
+ return body;
+ }
+
+ // for normal add, reg1, reg2 cases (reg-to-reg)
+ // using shifted-reg form
+ public static int r_reg(int opcode, int shift, int rm, int imm6, int rn, int rd) {
+ assert 0 <= rm && rm < 32;
+ assert 0 <= rn && rn < 32;
+ assert 0 <= rd && rd < 32;
+ return (opcode << 24) | (shift << 21) | (rm << 16) | (imm6 << 10) | (rn << 5) | rd;
+ }
+ public static void r_reg(Encoding enc, Node n, int opcode) {
+ short self = enc.reg(n);
+ short reg1 = enc.reg(n.in(1));
+ short reg2 = enc.reg(n.in(2));
+ int body = r_reg(opcode, 0, reg2, 0, reg1, self >= 32 ? reg1: self);
+ enc.add4(body);
+ }
+
+ public static void r_reg_subs(Encoding enc, Node n, int opcode) {
+ short reg1 = enc.reg(n.in(1));
+ short reg2 = enc.reg(n.in(2));
+ // 31 = 11111
+ int body = r_reg(opcode, 0, reg2, 0, reg1, 31);
+ enc.add4(body);
+ }
+
+ public static int shift_reg(int opcode, int rm, int op2, int rn, int rd) {
+ assert 0 <= rn && rn < 32;
+ assert 0 <= rm && rm < 32;
+ assert 0 <= rd && rd < 32;
+ return (opcode << 21) | (rm << 16) | (op2 << 10) | (rn << 5) | rd;
+ }
+ public static void shift_reg(Encoding enc, Node n, int op2) {
+ short self = enc.reg(n);
+ short reg1 = enc.reg(n.in(1));
+ short reg2 = enc.reg(n.in(2));
+ int body = shift_reg(0b10011010110, reg2, op2, reg1, self);
+ enc.add4(body);
+ }
+
+ // MUL can be considered an alias for MADD with the third operand Ra being set to 0
+ public static int madd(int opcode, int rm, int ra, int rn, int rd) {
+ assert 0 <= rn && rn < 32;
+ assert 0 <= rd && rd < 32;
+ assert 0 <= rm && rm < 32;
+ return (opcode << 21) | (rm << 16) | (ra << 10) | (rn << 5) | rd;
+ }
+ public static void madd(Encoding enc, Node n, int opcode, int ra) {
+ short self = enc.reg(n);
+ short reg1 = enc.reg(n.in(1));
+ short reg2 = enc.reg(n.in(2));
+ int body = madd(opcode, reg2, ra, reg1, self);
+ enc.add4(body);
+ }
+
+ // encodes movk, movn, and movz
+ public static int mov(int opcode, int shift, int imm16, int rd) {
+ assert 0 <= rd && rd < 32;
+ return (opcode << 23) | (shift << 21) | (imm16 << 5) | rd;
+ }
+
+ public static int mov_reg(int opcode, int src, int rd) {
+ assert 0 <= rd && rd < 32;
+ return (opcode << 21) | (src << 16) | 0b11111 << 5 | rd;
+ }
+
+ public static int ret(int opcode) {
+ return (opcode << 10);
+ }
+
+ // FMOV (scalar, immediate)
+ public static int f_scalar(int opcode, int ftype, int rm, int op, int rn, int rd) {
+ assert 0 <= rn && rn < 32;
+ assert 0 <= rd && rd < 32;
+ assert 0 <= rm && rm < 32;
+ return (opcode << 24) | (ftype << 22) | (1 << 21) | (rm << 16) | (op << 10) | (rn << 5) | rd;
+ }
+
+ public static int f_mov_reg(int opcode, int rn, int rd) {
+ assert 0 <= rn && rn < 32;
+ assert 0 <= rd && rd < 32;
+ return (opcode << 24) | (0b01100000010000 << 10) | (rn << 5) | rd;
+ }
+ public static int f_mov_general(int opcode, int ftype, int rmode, int opcode1, int rn, int rd) {
+ assert 0 <= rn && rn < 32;
+ assert 0 <= rd && rd < 32;
+ return (opcode << 24) |(ftype << 22) | (1 << 21) | (rmode << 19) | (opcode1 << 16) | (rn << 5) | rd;
+ }
+ public static void f_scalar(Encoding enc, Node n, int op ) {
+ short self = (short)(enc.reg(n) -D_OFFSET);
+ short reg1 = (short)(enc.reg(n.in(1))-D_OFFSET);
+ short reg2 = (short)(enc.reg(n.in(2))-D_OFFSET);
+ int body = f_scalar(0b00011110, 1, reg2, op, reg1, self);
+ enc.add4(body);
+ }
+
+ // share same encoding with LDR (literal, SIMD&FP) flavour
+ public static int load_pc(int opcode, int offset, int rt) {
+ offset>>=2;
+ return (opcode << 24) | (offset << 5) | rt;
+ }
+ // int l
+ public static int adrp(int op, int imlo,int opcode, int imhi, int rd) {
+ assert 0 <= rd && rd < 32;
+ return (op << 31) | (imlo << 29) |(opcode << 24) | (imhi << 5) | rd;
+ }
+
+ // [Rptr+Roff]
+ public static int indr_adr(int opcode, int off, STORE_LOAD_OPTION option, int s, int ptr, int rt) {
+ assert 0 <= ptr && ptr < 32;
+ assert 0 <= rt && rt < 32;
+ return (opcode << 21) | (off << 16) | (option.ordinal() << 13) | (s << 12) | (2 << 10) | (ptr << 5) | rt;
+ }
+ // [Rptr+imm9]
+ public static int load_str_imm(int opcode, int imm12, int ptr, int rt, int size) {
+ assert 0 <= ptr && ptr < 32;
+ assert 0 <= rt && rt < 32;
+
+ if(size == 8) imm12 = imm12 >> 3;
+ if(size == 4) imm12 = imm12 >> 2;
+ if(size == 2) imm12 = imm12 >> 1;
+ // size == 1 imm12 = imm12
+ return (opcode << 22) | ((imm12) << 10) | (ptr << 5) | rt;
+ }
+
+ // encoding for vcvt, size is encoded in operand
+ // <Qd>, <Qm>
+ // F32.S32
+ //encoded as op = 0b00, size = 0b10.
+ // VCVT<c>.<Td>.<Tm> <Dd>, <Dm>
+ // opcode is broken down into 4 pieces
+ public static int f_convert(int opcode_1, int opcode_2, int opcode_3, int opcode_4, int vd, int vm) {
+ return (opcode_1 << 28) | (opcode_2 << 24) | (opcode_3 << 20) | (opcode_4 << 16) |
+ (vd << 12) | (0x01100010 << 4) | vm;
+ }
+ public static int float_cast(int opcode, int ftype, int rn, int rd) {
+ assert 0 <= rd && rd < 32;
+ assert 0 <= rn && rn < 32;
+ return (opcode << 24) | (ftype << 22) | (2176 << 10) | (rn << 5) | rd;
+ }
+
+
+ // ftype = 3
+ public static int f_cmp(int opcode, int ftype, int rm, int rn) {
+ assert 0 <= rn && rn < 32;
+ assert 0 <= rm && rm < 32;
+ // Todo: |8 is not needed
+ return (opcode << 24) | (ftype << 21) | (rm << 16) | (8 << 10) | (rn << 5);
+ }
+ public static void f_cmp(Encoding enc, Node n) {
+ short reg1 = (short)(enc.reg(n.in(1))-D_OFFSET);
+ short reg2 = (short)(enc.reg(n.in(2))-D_OFFSET);
+ int body = f_cmp(0b00011110, 3, reg1, reg2);
+ enc.add4(body);
+ }
+
+ public static COND make_condition(String bop) {
+ return switch (bop) {
+ case "==" -> COND.EQ;
+ case "!=" -> COND.NE;
+ case "<" -> COND.LT;
+ case "<=" -> COND.LE;
+ case ">=" -> COND.GE;
+ case ">" -> COND.GT;
+ default -> throw Utils.TODO();
+ };
+ }
+
+ public static int b_cond(int opcode, int delta, COND cond) {
+ // 24-5 == 19bits offset range
+ assert -(1<<19) <= delta && delta < (1<<19);
+ assert (delta&3)==0;
+ delta>>=2;
+ delta &= (1L<<19)-1; // Zero extend
+ return (opcode << 24) | ((delta)<< 5) | cond.ordinal();
+ }
+
+ public static int cond_set(int opcode, int rm, COND cond, int rn, int rd) {
+ assert 0 <= rd && rd < 32;
+ assert 0 <= rn && rn < 32;
+ return (opcode << 21) | (rm << 16) | (cond.ordinal() << 12) | (rn << 5) | rd;
+ }
+
+ // Branch with Link to Register calls a subroutine at an address in a register, setting register X30 to PC+4.
+ public static int blr(int opcode, int rd) {
+ assert 0 <= rd && rd < 32;
+ return opcode << 10 | rd << 5;
+ }
+ public static int b(int opcode, int delta) {
+ assert -(1<<26) <= delta && delta < (1<<26);
+ assert (delta&3)==0;
+ delta>>=2;
+ delta &= (1L<<26)-1; // Zero extend
+ return (opcode << 26) | delta;
+ }
+ // no aligned assert(assert (delta&3)==0;)
+ public static int b_calloc(int opcode, int delta) {
+ assert -(1<<26) <= delta && delta < (1<<26);
+ delta>>=2;
+ delta &= (1L<<26)-1; // Zero extend
+ return (opcode << 26) | delta;
+ }
+
+ @Override public RegMask callArgMask(TypeFunPtr tfp, int idx, int maxArgSlot ) { return callInMask(tfp,idx,maxArgSlot); }
+ static RegMask callInMask(TypeFunPtr tfp, int idx, int maxArgSlot ) {
+ if( idx==0 ) return CodeGen.CODE._rpcMask;
if( idx==1 ) return null;
// Count floats in signature up to index
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,121 +584,119 @@ public class arm extends Machine {
throw Utils.TODO(); // Pass on stack slot
}
- static final long CALLEE_SAVE =
+ public static long decodeImm12(int imm12) {
+ int immr = (imm12 >> 6) & 0x3F;
+ int imms = imm12 & 0x3F;
+ int size;
+ if ((imm12 & 0x1000) != 0) {
+ size = 64;
+ } else {
+ size = 31-(imms >> 1);
+ size |= size >> 1;
+ size |= size >> 2;
+ size |= size >> 4;
+ size++;
+ imms &= ~((32-size) << 1);
+ }
+ long val = (2L << imms)-1;
+ while (size < 64) {
+ val |= val << size;
+ size <<= 1;
+ }
+ val = (val >>> immr) | val << (64-immr);
+ return val;
+ }
+
+ // Return the max stack slot used by this signature, or 0
+ @Override public short maxArgSlot( TypeFunPtr tfp ) {
+ int icnt=0, fcnt=0; // Count of ints, floats
+ for( int i=0; i<tfp.nargs(); i++ ) {
+ if( tfp.arg(i) instanceof TypeFloat ) fcnt++;
+ else icnt++;
+ }
+ int nstk = Math.max(icnt-8,0)+Math.max(fcnt-8,0);
+ return (short)nstk;
+ }
+
+ private static final long CALLEE_SAVE =
1L<<X19 |
1L<<X20 | 1L<<X21 | 1L<<X22 | 1L<<X23 |
1L<<X24 | 1L<<X25 | 1L<<X26 | 1L<<X27 |
1L<<X28 |
1L<<D9 | 1L<<D10 | 1L<<D11 |
1L<<D12 | 1L<<D13 | 1L<<D14 | 1L<<D15;
-
- static final 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 PhiNode phi -> new PhiNode(phi);
- case ProjNode prj -> prj(prj);
+ 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 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 XorNode xor -> xor(xor);
-
- case LoopNode loop -> new LoopNode(loop);
- case RegionNode region-> new RegionNode(region);
+ case ReturnNode ret -> new RetARM(ret,ret.fun());
+ case SarNode sar -> asr(sar);
+ case ShlNode shl -> lsl(shl);
+ case ShrNode shr -> lsr(shr);
+ case StartNode start -> new StartNode(start);
+ case StopNode stop -> new StopNode(stop);
+ case StoreNode st -> st(st);
+ case SubFNode subf -> new SubFARM(subf);
+ case SubNode sub -> sub(sub);
+ case ToFloatNode tfn-> new I2F8ARM(tfn);
+ case XorNode xor -> xor(xor);
+
+ case LoopNode loop -> new LoopNode(loop);
+ case RegionNode region-> new RegionNode(region);
default -> throw Utils.TODO();
};
}
- private Node 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());
+ return new SetARM(cmp, IfNode.negate(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) {
@@ -250,129 +707,101 @@ 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 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 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 xor(XorNode xor) {
+ int imm12;
+ return xor.in(2) instanceof ConstantNode off && off._con instanceof TypeInteger ti && (imm12 = imm12Logical(ti)) != -1
+ ? new XorIARM(xor, imm12)
+ : new XorARM(xor);
+ }
+ private Node and(AndNode and) {
+ int imm12;
+ return and.in(2) instanceof ConstantNode off && off._con instanceof TypeInteger ti && (imm12 = imm12Logical(ti)) != -1
+ ? new AndIARM(and, imm12)
+ : new AndARM(and);
}
- private Node 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 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 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 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 prj(ProjNode prj) {
- return new ProjARM(prj);
+ 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 {
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..37383608
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/AUIPC.java
@@ -0,0 +1,41 @@
+package com.seaofnodes.simple.node.cpus.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..4d2ac916 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,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 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..a85ce5d0 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
@@ -4,8 +4,18 @@ 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..ba88bb48 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
@@ -4,8 +4,9 @@ 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 6a1cf7dc..44ff4e8b 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,47 +1,92 @@
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 java.io.ByteArrayOutputStream;
import java.util.BitSet;
// 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 public StringBuilder _print1(StringBuilder sb, BitSet visited) {
- in(1)._print0(sb.append("if( "),visited).append(_bop);
+ sb.append("if( ");
+ if( in(1)==null ) sb.append("0");
+ else in(1)._print0(sb,visited);
+ sb.append(_bop);
if( in(2)==null ) sb.append("0");
else in(2)._print0(sb,visited);
return sb.append(" )");
}
- // 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..7a0f3313 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,58 @@
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.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..dcc53cbf 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,25 @@
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 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 +28,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..c67d7b3d 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,19 @@
package com.seaofnodes.simple.node.cpus.riscv;
-import com.seaofnodes.simple.*;
-import com.seaofnodes.simple.codegen.CodeGen;
-import com.seaofnodes.simple.codegen.RegMask;
+import com.seaofnodes.simple.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/FltRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/FltRISC.java
index 2832ac52..35de7e04 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
@@ -2,30 +2,46 @@ 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.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..84f0f4c7 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,22 @@
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.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..5519fe5b 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,23 @@
package com.seaofnodes.simple.node.cpus.riscv;
import com.seaofnodes.simple.*;
-import com.seaofnodes.simple.codegen.CodeGen;
-import com.seaofnodes.simple.codegen.RegMask;
+import com.seaofnodes.simple.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 02191d05..9303e566 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,54 @@
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.util.BitSet;
-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 public StringBuilder _print1(StringBuilder sb, BitSet visited) {
+ int imm12 = (_imm12<<20)>>20; // Sign extend 12 bits
in(1)._print0(sb.append("( "), visited);
- return sb.append(String.format(" %s #%d )",glabel(),_imm));
+ return sb.append(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..70d5c18f 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,28 @@
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.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..75ad6496
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/LUI.java
@@ -0,0 +1,32 @@
+package com.seaofnodes.simple.node.cpus.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..7b0e9138 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,29 @@
package com.seaofnodes.simple.node.cpus.riscv;
import com.seaofnodes.simple.*;
-import com.seaofnodes.simple.codegen.CodeGen;
-import com.seaofnodes.simple.codegen.RegMask;
+import com.seaofnodes.simple.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 bcd7924e..b8eb7644 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/MemOpRISC.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/MemOpRISC.java
@@ -1,57 +1,74 @@
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.Utils;
+import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.*;
import com.seaofnodes.simple.type.*;
-import java.io.ByteArrayOutputStream;
-import java.lang.StringBuilder;
import java.util.BitSet;
-
-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 public StringBuilder _printMach(StringBuilder sb, BitSet visited) { return sb.append(".").append(_name); }
+ @Override public StringBuilder _printMach(StringBuilder sb, BitSet visited) { return sb.append(".").append(_name); }
- @Override public String label() { return op(); }
@Override public Type compute() { throw Utils.TODO(); }
@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..c9917b4f 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,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 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..77f4a497
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/NegRISC.java
@@ -0,0 +1,20 @@
+package com.seaofnodes.simple.node.cpus.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 53ad0af2..e395a811 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,46 @@
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 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 aliases edges
- @Override public RegMask regmap(int i) { return i == 1 ? riscv.A0_MASK : null; }
- @Override public RegMask outregmap(int i) { return i == 1 ? riscv.A0_MASK : null; }
- @Override public RegMask outregmap() { return null; }
+ @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..9cbd5025 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,20 @@
package com.seaofnodes.simple.node.cpus.riscv;
-import com.seaofnodes.simple.*;
-import com.seaofnodes.simple.codegen.CodeGen;
-import com.seaofnodes.simple.codegen.LRG;
-import com.seaofnodes.simple.codegen.RegMask;
-import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.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));
+ enc.add4(riscv.i_type(riscv.OP_IMM, dst, 2, src, 1));
}
-
@Override public void asm(CodeGen code, SB sb) { sb.p(code.reg(this)); }
- @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..f6a888d9 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
@@ -4,8 +4,9 @@ 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..8a8186cd 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
@@ -2,47 +2,20 @@ package com.seaofnodes.simple.node.cpus.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..9ee19e42 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,14 @@
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.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 +16,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..f270bf12 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,17 @@
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 ) {
+ super(src,imm12);
+ _unsigned = unsigned;
}
- @Override int opcode() { throw Utils.TODO(); }
- @Override public String glabel() { return _bop; }
- @Override public String op() { return "set"+_bop; }
+ @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..4edecc04 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
@@ -4,8 +4,9 @@ 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..569fa2e0 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,66 @@
package com.seaofnodes.simple.node.cpus.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..26404e95 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
@@ -4,8 +4,9 @@ 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..ed697678 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
@@ -4,8 +4,9 @@ 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 c57cf11d..7305c4d9 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
@@ -2,14 +2,9 @@ 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.*;
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 +19,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;
- throw Utils.TODO();
+ // 0 - ctrl
+ if( i==1 ) return null; // mem
+ if( i==2 ) return riscv.RMASK; // ptr
+ // 2 - index
+ if( i==4 ) return riscv.MEM_MASK; // Wide mask to store GPR and FPR
+ return null; // Anti-dependence
}
-
-
- // 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..1e1b43c7 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,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 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..17613009 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,55 @@
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.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) {
+ @Override public byte encSize(int delta) {
+ if( -(1L<<11) <= delta && delta < (1L<<11) ) 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 ) {
+ // AUIPC (upper 20 bits)
+ // opstart of add
+ int next = opStart + opLen;
+ enc.patch4(opStart,riscv.u_type(riscv.OP_AUIPC, rpc, delta));
+ // addi(low 12 bits)
+ enc.patch4(next,riscv.i_type(riscv.OP_IMM, rpc, 0, rpc, delta & 0xFFF));
+ // addi
+ } else {
+ // should not happen as one instruction is 4 byte, and TFP arm encodes 2.
+ throw Utils.TODO();
+ }
+ }
+
@Override public void asm(CodeGen code, SB sb) {
- 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..6473d81c
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/UJmpRISC.java
@@ -0,0 +1,46 @@
+package com.seaofnodes.simple.node.cpus.riscv;
+
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.codegen.*;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.type.Type;
+import java.util.BitSet;
+
+// unconditional jump
+public class UJmpRISC extends CFGNode implements MachNode, RIPRelSize {
+ @Override public String op() { return "jmp"; }
+ @Override public String label() { return op(); }
+ @Override public StringBuilder _print1( StringBuilder sb, BitSet visited ) { return sb.append("jmp "); }
+ @Override public RegMask regmap(int i) {return null; }
+ @Override public RegMask outregmap() { return null; }
+ @Override public Type compute() { throw Utils.TODO(); }
+ @Override public Node idealize() { throw Utils.TODO(); }
+ @Override public void encoding( Encoding enc ) {
+ // Short form +/-4K: beq r0,r0,imm12
+ // Long form: auipc rX,imm20/32; jal r0,[rX+imm12/32]
+ enc.jump(this,uctrl());
+ enc.add4(riscv.j_type(riscv.OP_JAL, 0, 0));
+ }
+
+ // Delta is from opcode start
+ @Override public byte encSize(int delta) {
+ if( -(1L<<20) <= delta && delta < (1L<<20) ) return 4;
+ // 2 word encoding needs a tmp register, must teach RA
+ throw Utils.TODO();
+ }
+
+ // Delta is from opcode start
+ @Override public void patch( Encoding enc, int opStart, int opLen, int delta ) {
+ if( opLen==4 ) {
+ enc.patch4(opStart,riscv.j_type(riscv.OP_JAL, 0, delta));
+ } else {
+ throw Utils.TODO();
+ }
+ }
+
+ @Override public void asm(CodeGen code, SB sb) {
+ CFGNode target = uctrl();
+ //assert target.nOuts() > 1; // Should optimize jmp to empty targets
+ sb.p(label(target));
+ }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/riscv/XorIRISC.java b/src/main/java/com/seaofnodes/simple/node/cpus/riscv/XorIRISC.java
index a2e32f06..61f25be2 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
@@ -4,8 +4,9 @@ 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 83706cbf..54c6b828 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,63 @@
package com.seaofnodes.simple.node.cpus.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;
@@ -56,26 +70,26 @@ public class riscv extends Machine {
// 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 +104,157 @@ 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 ) {
+ // major opcode: OP
+ public static int OP_LOAD = 0b00_000_11;
+ public static int OP_LOADFP = 0b00_001_11;
+ public static int OP_CUSTOM0 = 0b00_010_11;
+ public static int OP_IMM = 0b00_100_11;
+ public static int OP_AUIPC = 0b00_101_11;
+
+ public static int OP_STORE = 0b01_000_11;
+ public static int OP_STOREFP = 0b01_001_11;
+ public static int OP_CUSTOM1 = 0b01_010_11;
+ public static int OP = 0b01_100_11;
+ public static int OP_LUI = 0b01_101_11;
+
+ public static int OP_CUSTOM2 = 0b10_010_11;
+ public static int OP_FP = 0b10_100_11;
+
+ public static int OP_BRANCH = 0b11_000_11;
+ public static int OP_JALR = 0b11_001_11;
+ public static int OP_RESERVED= 0b11_010_11;
+ public static int OP_JAL = 0b11_011_11;
+
+
+ // Since riscv instructions are fixed we can just or them together
+ public static int r_type(int opcode, int rd, int func3, int rs1, int rs2, int func7) {
+ assert 0 <= rs1 && rs1 < 32;
+ assert 0 <= rs2 && rs2 < 32;
+ assert 0 <= rd && rd < 32;
+ return (func7 << 25) | (rs2 << 20) | (rs1 << 15) | (func3 << 12) | (rd << 7) | opcode;
+ }
+ public static void r_type(Encoding enc, Node n, int func3, int func7) {
+ short dst = enc.reg(n);
+ short src1 = enc.reg(n.in(1));
+ short src2 = enc.reg(n.in(2));
+ int body = r_type(OP,dst,func3,src1,src2,func7);
+ enc.add4(body);
+ }
+
+ public static void rf_type(Encoding enc, Node n, RM func3, int func7) {
+ short dst = (short)(enc.reg(n )-F_OFFSET);
+ short src1 = (short)(enc.reg(n.in(1))-F_OFFSET);
+ short src2 = (short)(enc.reg(n.in(2))-F_OFFSET);
+ int body = r_type(OP_FP,dst,func3.ordinal(),src1,src2,func7);
+ enc.add4(body);
+ }
+
+
+ public static int u_type(int opcode, int rd, int imm20) {
+ assert 0 <= rd && rd < 32;
+ return (imm20 << 12) | (rd << 7) | opcode;
+ }
+
+ public static int j_type(int opcode, int rd, int delta) {
+ assert -(1L<<20) <= delta && delta < (1L<<20);
+ assert 0 <= rd && rd < 32;
+ // Messy branch offset encoding
+ // 31 30-21 20 19-12 11-7 6-0
+ // 20 10- 1 11 19-12 rpc JAL
+ assert (delta&1)==0; // Low bit is always zero, not encoded
+ int imm10_01 = (delta>> 1) & 0x3FF;
+ int imm11 = (delta>>11) & 1;
+ int imm12_19 = (delta>>12) & 0xFF;
+ int imm20 = (delta>>19) & 1;
+ int bits = imm20<<19 | imm10_01 << 9 | imm11 << 8 | imm12_19;
+ return bits << 12 | rd << 7 | opcode;
+ }
+
+
+ public static int i_type(int opcode, int rd, int func3, int rs1, int imm12) {
+ assert 0 <= rd && rd < 32;
+ assert 0 <= rs1 && rs1 < 32;
+ assert opcode >= 0 && func3 >=0 && imm12 >= 0; // Zero-extend by caller
+ return (imm12 << 20) | (rs1 << 15) | (func3 << 12) | (rd << 7) | opcode;
+ }
+
+
+ // S-type instructions(store)
+ public static int s_type(int opcode, int func3, int rs1, int rs2, int imm12) {
+ assert 0 <= rs1 && rs1 < 32;
+ assert 0 <= rs2 && rs2 < 32;
+ assert 0 <= func3;
+
+ assert imm12 >= 0; // Masked to high zero bits by caller
+ int imm_lo = imm12 & 0x1F;
+ int imm_hi = imm12 >> 5;
+ return (imm_hi << 25) | (rs2 << 20) | (rs1 << 15) | (func3 << 12) | (imm_lo << 7) | opcode;
+ }
+
+ // BRANCH
+ public static int b_type(int opcode, int func3, short rs1, short rs2, int delta) {
+ assert 0 <= rs1 && rs1 < 32;
+ assert 0 <= rs2 && rs2 < 32;
+ assert -4*1024 <= delta && delta < 4*1024;
+ assert (delta&1)==0; // Low bit is always zero, not encoded
+ // Messy branch offset encoding
+ // 31 30 29 28 27 26 25 24-20 19-15 14-12 11 10 9 8 7 6-0
+ // 12 10 9 8 7 6 5 SRC2 SRC1 FUNC3 4 3 2 1 11 OP
+ int imm4_1 = (delta>> 1) & 0xF;
+ int imm10_5= (delta>> 5) &0x3F;
+ int imm11 = (delta>>11) & 1;
+ int imm12 = (delta>>12) & 1;
+ int imm5 = imm4_1<<1 | imm11;
+ int imm7 = imm12<<6 | imm10_5;
+ return (imm7 << 25 ) | (rs2 << 20) | (rs1 << 15) | (func3 << 12) | (imm5 << 7) | opcode;
+ }
+
+ public enum RM {
+ RNE, // Round to Nearest, ties to Even
+ RTZ, // Round towards Zero
+ RDN, // Round Down
+ RUP, // Round Up
+ DIRECT, // Round to Nearest, ties to Max Magnitude
+ RESERVED1, // Reserved for futue use
+ RESERVED2, // Reserved for future use
+ DYN, // In instruction’s rm field, selects dynamic rounding mode; In Rounding Mode register, reserved
+ }
+
+ // Since opcode is the same just return back func3
+ static public int jumpop(String op) {
+ return switch(op) {
+ case "==" -> 0x0;
+ case "!=" -> 0x1;
+ case "<" -> 0x4;
+ case ">=" -> 0x5;
+ case "u<" -> 0x6;
+ case "u<=" -> 0x7;
+ default -> throw Utils.TODO();
+ };
+ }
+
+ // Since opcode is the same just return back func3
+ static public int fsetop(String op) {
+ return switch(op) {
+ case "<" -> 1;
+ case "<=" -> 0;
+ case "==" -> 2;
+ default -> throw Utils.TODO();
+ };
+ }
+
+ @Override public RegMask callArgMask( TypeFunPtr tfp, int idx, int maxArgSlot ) { return callInMask(tfp,idx,maxArgSlot); }
+ static RegMask callInMask( TypeFunPtr tfp, int idx, int maxArgSlot ) {
if( idx==0 ) return RPC_MASK;
if( idx==1 ) return null;
// Count floats in signature up to index
@@ -117,109 +271,94 @@ public class riscv extends Machine {
if( idx-2-fcnt < cargs.length )
return cargs[idx-2-fcnt];
}
- throw Utils.TODO(); // Pass on stack slot
- }
-
- // 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);
+ // Pass on stack slot(8 and higher)
+ if( maxArgSlot>0 ) throw Utils.TODO();
+ return new RegMask(MAX_REG + 1 + (idx - 2));
+ }
+
+ @Override public short maxArgSlot( TypeFunPtr tfp ) {
+ int icnt=0, fcnt=0; // Count of ints, floats
+ for( int i=0; i<tfp.nargs(); i++ ) {
+ if( tfp.arg(i) instanceof TypeFloat ) fcnt++;
+ else icnt++;
}
+ int nstk = Math.max(icnt-8,0)+Math.max(fcnt-8,0);
+ return (short)nstk;
}
- static RegMask retMask( TypeFunPtr tfp, int i ) {
- return i==2
- ? (tfp.ret() instanceof TypeFloat ? FA0_MASK : A0_MASK)
- : RET_MASKS[i];
+
+ 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 PhiNode phi -> new PhiNode(phi);
- case ProjNode prj -> prj(prj);
- case ReadOnlyNode read -> new ReadOnlyNode(read);
- case ReturnNode ret -> new RetRISC(ret, ret.fun());
- case SarNode sar -> sra(sar);
- case ShlNode shl -> sll(shl);
- case ShrNode shr -> srl(shr);
- case StartNode start -> new StartNode(start);
- case StopNode stop -> new StopNode(stop);
- case StoreNode st -> st(st);
- case SubFNode subf -> new SubFRISC(subf);
- case SubNode sub -> sub(sub);
- case ToFloatNode tfn -> i2f8(tfn);
- case XorNode xor -> xor(xor);
+ case MinusNode neg -> new NegRISC(neg);
+ case MulFNode mulf -> new MulFRISC(mulf);
+ case MulNode mul -> new MulRISC(mul);
+ case NewNode nnn -> nnn(nnn);
+ case NotNode not -> new NotRISC(not);
+ case OrNode or -> or(or);
+ case ParmNode parm -> new ParmRISC(parm);
+ case PhiNode phi -> new PhiNode(phi);
+ case ProjNode prj -> prj(prj);
+ case ReadOnlyNode read-> new ReadOnlyNode(read);
+ case ReturnNode ret -> new RetRISC(ret, ret.fun());
+ case SarNode sar -> sra(sar);
+ case ShlNode shl -> sll(shl);
+ case ShrNode shr -> srl(shr);
+ case StartNode start -> new StartNode(start);
+ case StopNode stop -> new StopNode(stop);
+ case StoreNode st -> st(st);
+ case SubFNode subf -> new SubFRISC(subf);
+ case SubNode sub -> sub(sub);
+ case ToFloatNode tfn -> i2f8(tfn);
+ case XorNode xor -> xor(xor);
case LoopNode loop -> new LoopNode(loop);
case RegionNode region -> new RegionNode(region);
@@ -232,36 +371,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);
+ 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();
@@ -272,45 +456,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);
}
@@ -324,28 +513,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);
@@ -353,9 +534,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..d54c7c31 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
@@ -2,15 +2,14 @@ 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.*;
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 +17,23 @@ 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 ) {
+ // addsd xmm0, DWORD PTR [rdi+0xc]
+ short dst = enc.reg(this );
+ short ptr = enc.reg(ptr());
+ short idx = enc.reg(idx());
+ // F opcode
+ enc.add1(0xF2);
+ // rex prefix must come next (REX.W is not set)
+ x86_64_v2.rexF(dst, ptr, idx, false, enc);
+ // FP ADD
+ enc.add1(0x0F).add1(0x58);
+
+ x86_64_v2.indirectAdr(_scale, idx, ptr, _off, dst, enc);
+ }
// General form: "add dst = src + [base + idx<<2 + 12]"
@Override public void asm(CodeGen code, SB sb) {
@@ -29,5 +41,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..a4166bc7 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,33 @@
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.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 ) {
+ // F2 0F 58 /r ADDSD xmm1, xmm2/m64
+ short dst = (short)(enc.reg(this ) - x86_64_v2.XMM_OFFSET);
+ short src = (short)(enc.reg(in(2)) - x86_64_v2.XMM_OFFSET);
+ // Fopcode
+ enc.add1(0xF2);
+ // rex prefix must come next (REX.W is not set)
+ x86_64_v2.rexF(dst, src, 0, false, enc);
+
+ enc.add1(0x0F).add1(0x58);
+
+ enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, dst, src));
}
// General form: "addf dst += src"
@Override public void asm(CodeGen code, SB sb) {
- sb.p(code.reg(this)).p(" = ").p(code.reg(in(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..f97a7cb8 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,34 @@
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.*;
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 ) {
+ // add something to register from memory
+ // add eax,DWORD PTR [rdi+0xc]
+ // REX.W + 03 /r ADD r64, r/m64
+ short dst = enc.reg(this );
+ short ptr = enc.reg(ptr());
+ short idx = enc.reg(idx());
+
+ enc.add1(x86_64_v2.rex(dst, ptr, idx));
+ // opcode
+ enc.add1(0x03);
+
+ x86_64_v2.indirectAdr(_scale, idx, ptr, _off, dst, enc);
+ }
- // General form: "add dst = 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..1a74f817 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,26 @@
package com.seaofnodes.simple.node.cpus.x86_64_v2;
import com.seaofnodes.simple.SB;
-import com.seaofnodes.simple.Utils;
-import com.seaofnodes.simple.codegen.CodeGen;
-import com.seaofnodes.simple.codegen.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 ) {
+ // FF /2 CALL r/m64
+ // calls the function in the register
+ short src = enc.reg(fptr());
+ enc.add1(0xFF);
+ enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.INDIRECT,2,src));
}
@Override public void asm(CodeGen code, SB sb) {
@@ -36,5 +30,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..35b92170 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,26 @@
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.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 ) {
+ // UCOMISD
+ short src1 = (short)(enc.reg(in(1)) - x86_64_v2.XMM_OFFSET);
+ short src2 = (short)(enc.reg(in(2)) - x86_64_v2.XMM_OFFSET);
+ // float opcode
+ enc.add1(0x66);
+ // rex prefix must come next (REX.W is not set)
+ x86_64_v2.rexF(src1, src2, 0, false, enc);
+
+ enc.add1(0x0F).add1(0x2E);
+ enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, src1, src2));
}
// General form: "cmp src1,src2"
@@ -24,6 +29,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..91b6d43c 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,44 @@
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.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 ) {
+ short dst = enc.reg(in(1)); // Also src1
+ enc.add1(x86_64_v2.rex(0, dst, 0));
+ // opcode; 0x81 or 0x83; 0x69 or 0x6B
+ enc.add1( 0x81 + (x86_64_v2.imm8(_imm) ? 2 : 0) );
+ enc.add1( x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, 7, dst) );
+ // immediate(4 bytes) 32 bits or (1 byte)8 bits
+ if( x86_64_v2.imm8(_imm) ) enc.add1(_imm);
+ else enc.add4(_imm);
}
-
@Override public void asm(CodeGen code, SB sb) {
String dst = code.reg(this);
if( dst!="flags" ) sb.p(dst).p(" = ");
sb.p(code.reg(in(1)));
if( _imm != 0 ) sb.p(", #").p(_imm);
}
-
- @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..489853eb 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,78 @@
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.*;
+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 ) {
+ // REX.W + 81 /7 id CMP r/m64, imm32 | REX.W + 39 /r CMP r/m64,r64
+ // CMP [mem], imm32
+
+ short ptr = enc.reg(ptr());
+ short idx = enc.reg(idx());
+ short src = enc.reg(val());
+
+ if(src == -1) {
+ assert !_swap;
+ int log = _declaredType.log_size();
+ x86_64_v2.rexF(-1, ptr, idx, log==3, enc);
+ switch(log) {
+ case 0: enc.add1(0x80); break;
+ case 1: enc.add1(0x66).add1(0x81); break;
+ case 2: enc.add1(0x81); break;
+ case 3: enc.add1(0x81); break;
+ }
+ x86_64_v2.indirectAdr(_scale, idx, ptr, _off, 7, enc);
+ switch(log) {
+ case 0: enc.add1(_imm); break;
+ case 1: enc.add2(_imm); break;
+ case 2: enc.add4(_imm); break;
+ case 3: enc.add8(_imm); break;
+ }
+ } else {
+ encVal(enc, _declaredType, ptr, idx, src, _off, _scale, _swap);
+ }
+ }
+
+ static void encVal(Encoding enc, Type decl, short ptr, short idx, short src, int off, int scale, boolean _swap) {
+ if(decl instanceof TypeFloat) {
+ src -= (short)x86_64_v2.XMM_OFFSET;
+ enc.add1(decl==TypeFloat.F32 ? 0xF3 : 0xF2).add1(0x0F).add1(0xC2);
+ } else {
+ int log = decl.log_size();
+ x86_64_v2.rexF(src, ptr, idx, log == 3, enc);
+ int swap = _swap ? 2 : 0;
+ switch(log) {
+ case 0: enc.add1(0x38+swap); break;
+ case 1: enc.add1(0x66).add1(0x39+swap); break;
+ case 2: enc.add1(0x39+swap); break;
+ case 3: enc.add1(0x39+swap); break;
+ }
+ }
+ x86_64_v2.indirectAdr(scale, idx, ptr, off, src, enc);
+ }
// General form: "cmp dst = src, [base + idx<<2 + 12]"
@Override public void asm(CodeGen code, SB sb) {
String dst = code.reg(this);
- if( dst!="FLAGS" ) sb.p(dst).p(" = ");
- if( _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..06a36d99 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,26 @@
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.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 ) {
+ short dst = enc.reg(in(1));
+ short src = enc.reg(in(2));
- // General form: "add dst += src"
+ enc.add1(x86_64_v2.rex(dst, src, 0));
+ enc.add1(0x3B);
+ enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, dst, src));
+ }
@Override public void asm(CodeGen code, SB sb) {
String dst = code.reg(this);
- if( dst!="FLAGS" ) sb.p(dst).p(" = ");
+ 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..9509a1f5 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,34 @@
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.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 ) {
+ // F2 0F 5E /r DIVSD xmm1, xmm2/m64
+ short dst = (short)(enc.reg(this ) - x86_64_v2.XMM_OFFSET);
+ short src = (short)(enc.reg(in(2)) - x86_64_v2.XMM_OFFSET);
+
+ // Fopcode
+ enc.add1(0xF2);
+ // rex prefix must come next (REX.W is not set)
+ x86_64_v2.rexF(dst, src, 0, false, enc);
+
+ enc.add1(0x0F).add1(0x5E);
+
+ enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, dst, src));
}
// General form: "divf dst /= src"
@Override public void asm(CodeGen code, SB sb) {
- sb.p(code.reg(this)).p(" = ").p(code.reg(in(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..87b8b0c3 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,33 @@
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.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 ) {
+ // REX.W + F7 /7 IDIV r/m64
+ short src = enc.reg(in(2));
+ // sign extend rax before 128/64 bits division
+ // sign extend rax to rdx:rax
+ enc.add1(x86_64_v2.REX_W);
+ enc.add1(0x99);
+ enc.add1(x86_64_v2.rex(0, src, 0));
+ // divide
+ enc.add1(0xF7); // opcode
+ enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, 0x07, src));
}
-
// General form: "div dst /= src"
@Override public void asm(CodeGen code, SB sb) {
sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" / ").p(code.reg(in(2)));
}
-
- @Override public String op() { return "div"; }
@Override public String comment() { return "kill rdx"; }
}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/FltX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/FltX86.java
index 47f7149d..e3b20003 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,47 @@
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.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 ) {
+ // Load the constant into a FPR
+ // movsd xmm, [rip + 0]
+ // F2 0F 10 /r MOVSD xmm1, m64
+ _encSize = 1/*Fop*/+0/*REX*/+2/*op*/+1/*MODRM*/+4/*offset*/;
+ short dst = (short)(enc.reg(this ) - x86_64_v2.XMM_OFFSET);
+ // Fopcode
+ enc.add1(0xF2);
+ // rex prefix must come next (REX.W is not set)
+ _encSize += x86_64_v2.rexF(dst, 0, 0, false, enc);
+ enc.add1(0x0F).add1(0x10);
+ // hard-code rip here
+ enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.INDIRECT, dst, 0x05));
+ enc.add4(0);
+ enc.largeConstant(this,_con,_encSize-4,2/*ELF encoding PC32*/);
}
- // 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..07418eb0 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,34 @@
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.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
+ // opcode: 0x83, addi rsp with immediate 8
+ enc.add1( x86_64_v2.REX_W ).add1( x86_64_v2.imm8(sz) ? 0x83 : 0x81 );
+ enc.add1( x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, 0b101, x86_64_v2.RSP) );
+ if( x86_64_v2.imm8(sz) ) enc.add1(sz);
+ else enc.add4(sz);
}
+
}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/I2f8X86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/I2f8X86.java
index d99c92a8..d3213ae4 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,31 @@
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.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 ) {
+ // F2 0F 2A /r CVTSI2SD xmm1, r32/m32
+ short dst = (short)(enc.reg(this ) - x86_64_v2.XMM_OFFSET);
+ short src = enc.reg(in(1));
+
+ // Fopcode
+ enc.add1(0xF2);
+ // rex prefix must come next (REX.W is not set)
+ x86_64_v2.rexF(dst, src, 0, true, enc);
- // General form: "i2f8 (flt)int_value"
+ enc.add1(0x0F).add1(0x2A);
+
+ enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, dst, src));
+ }
@Override public void asm(CodeGen code, SB sb) {
sb.p(code.reg(this)).p(" = ").p("(flt)").p(code.reg(in(1)));
}
-
- @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..760d6805
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ImmX86.java
@@ -0,0 +1,37 @@
+package com.seaofnodes.simple.node.cpus.x86_64_v2;
+
+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 ) {
+ short dst = enc.reg(this); // Also src1
+ enc.add1(x86_64_v2.rex(0, dst, 0));
+ // opcode; 0x81 or 0x83; 0x69 or 0x6B
+ enc.add1( opcode() + (x86_64_v2.imm8(_imm) ? 2 : 0) );
+ enc.add1( x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, mod(), dst) );
+ // immediate(4 bytes) 32 bits or (1 byte)8 bits
+ if( x86_64_v2.imm8(_imm) ) enc.add1(_imm);
+ else enc.add4(_imm);
+ }
+
+ // General form: "addi dst += #imm"
+ @Override public void asm(CodeGen code, SB sb) {
+ sb.p(code.reg(this)).p(" ").p(glabel()).p("= #").p(_imm);
+ }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/IntX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/IntX86.java
index fcd39e3f..6d9110f2 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,65 @@
package com.seaofnodes.simple.node.cpus.x86_64_v2;
import com.seaofnodes.simple.*;
-import com.seaofnodes.simple.codegen.CodeGen;
-import com.seaofnodes.simple.codegen.RegMask;
-import com.seaofnodes.simple.node.ConstantNode;
-import com.seaofnodes.simple.node.MachNode;
-import com.seaofnodes.simple.node.Node;
+import com.seaofnodes.simple.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); }
+ @Override public void encoding( Encoding enc ) {
+ short dst = enc.reg(this);
+ // Short form for zero
+ if( _con==Type.NIL || _con==TypeInteger.ZERO ) {
+ // XOR dst,dst. Can skip REX is dst is low 8, makes this a 32b
+ // xor, which will also zero the high bits.
+ if( dst >= 8 ) enc.add1(x86_64_v2.rex(dst, dst, 0));
+ enc.add1(0x33); // opcode
+ enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, dst, dst));
+ return;
+ }
+
+ // Simply move the constant into a GPR
+ // Conditional encoding based on 64 or 32 bits
+ //REX.W + C7 /0 id MOV r/m64, imm32
- // Encoding is appended into the byte array; size is returned
- @Override public int encoding(ByteArrayOutputStream bytes) {
- throw Utils.TODO();
+ long imm = ((TypeInteger)_con).value();
+ if (Integer.MIN_VALUE <= imm && imm < 0) {
+ // We need sign extension, so use imm32 into 64 bit register
+ // REX.W + C7 /0 id MOV r/m64, imm32
+ enc.add1(x86_64_v2.rex(0, dst, 0));
+ enc.add1(0xC7); // 32 bits encoding
+ enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, 0x00, dst));
+ enc.add4((int)imm);
+ } else if (0 <= imm && imm <= 0xFFFFFFFFL) {
+ // We want zero extension into 64 bit register
+ // so move 32 bit into 32 bit register which zeros
+ // the upper bits in the 64 bit register.
+ // B8+ rd id MOV r32, imm32
+ if (dst >= 8) enc.add1(0x41);
+ enc.add1(0xB8 + (dst & 0x07));
+ enc.add4((int)imm);
+ } else {
+ // Just write the full 64 bit constant
+ // REX.W + B8+ rd io MOV r64, imm64
+ enc.add1(x86_64_v2.rex(0, dst, 0));
+ enc.add1(0xB8 + (dst & 0x07));
+ enc.add8(imm); // 64 bits encoding
+ }
}
// Human-readable form appended to the SB. Things like the encoding,
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..d7acf8b8 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,80 @@
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.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 ) {
+ int op = x86_64_v2.jumpop(_bop);
+ enc.add1(op-16).add1(0); // Short form jump
+ } else {
+ if( _bop=="!=" ) return; // Inverted, no code
+ enc.add1(0xEB).add1(0); // Never-node
+ }
+ enc.jump(this,cproj(0));
}
- @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; // Inverted never-node, no code
+ return (byte)(x86_64_v2.imm8(delta-2) ? 2 : 6);
}
- @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=="!=" ); // Inverted never-node, no code no patch
+ byte[] bits = enc.bits();
+ if( opLen==2 ) {
+ assert in(1)==null || bits[opStart] == x86_64_v2.jumpop(_bop)-16;
+ delta -= 2; // Offset from opcode END
+ assert (byte)delta==delta;
+ bits[opStart+1] = (byte)delta;
+ } else {
+ assert in(1)==null || bits[opStart] == x86_64_v2.jumpop(_bop)-16;
+ delta -= 6; // Offset from opcode END
+ bits[opStart] = 0x0F;
+ bits[opStart+1] = (byte)(in(1)==null ? 0xE9 : x86_64_v2.jumpop(_bop));
+ enc.patch4(opStart+2,delta);
+ }
+ }
- @Override public 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..dce8a49a 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,15 @@
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.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 +18,30 @@ 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 ) {
+ // REX.W + 8D /r LEA r64,m
+ short dst = enc.reg(this);
+ short ptr = enc.reg(in(1));
+ short idx = enc.reg(in(2));
+ // ptr is null
+ // just do: [(index * s) + disp32]
+ if( ptr == -1 ) {
+ enc.add1(x86_64_v2.rex(dst, 0, idx));
+ enc.add1(0x8D); // opcode
+ enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.INDIRECT, dst, 0x04));
+ enc.add1(x86_64_v2.sib(_scale, idx, x86_64_v2.RBP));
+ enc.add4(_offset);
+ return;
+ }
+
+ enc.add1(x86_64_v2.rex(dst, ptr, idx));
+ enc.add1(0x8D); // opcode
+ // rsp is hard-coded here(0x04)
+ x86_64_v2.indirectAdr(_scale, idx, ptr, _offset, dst, enc);
}
// General form: "lea dst = base + 4*idx + 12"
@@ -36,9 +49,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..6e72a540 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,55 @@
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.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 ) {
+ if( decl == TypeFloat.F32) enc.add1(0xF3);
+ if( decl == TypeFloat.F64) enc.add1(0xF2);
+
+ if( decl.isa(TypeFloat.F64) )
+ dst -= (short)x86_64_v2.XMM_OFFSET;
+
+ x86_64_v2.rexF(dst, ptr, idx, decl != TypeInteger.U32 && decl != TypeFloat.F32 && decl != TypeFloat.F64, enc);
+
+ if( false ) ;
+ else if( decl == TypeFloat.F32 ) enc.add1(0x0F).add1(0x10); // F3 0F 10 /r MOVSS xmm1, m32
+ else if( decl == TypeFloat.F64 ) enc.add1(0x0F).add1(0x10); // F2 0F 10 /r MOVSD xmm1, m64
+ else if( decl.isa(TypeInteger.I8)) enc.add1(0x0F).add1(0xBE); // sign extend: REX.W + 0F BE /r MOVSX r64, r/m8
+ else if( decl == TypeInteger.I16 ) enc.add1(0x0F).add1(0xBF); // sign extend: REX.W + 0F BF /r MOVSX r64, r/m16
+ else if( decl == TypeInteger.I32 ) enc.add1(0x63); // sign extend: REX.W + 63 /r MOVSXD r64, r/m32
+ else if( decl == TypeInteger.U8 ) enc.add1(0x0F).add1(0xB6); // zero extend: REX.W + 0F B6 /r MOVZX r64, r/m8
+ else if( decl == TypeInteger.U16 ) enc.add1(0x0F).add1(0xB7); // zero extend: REX.W + 0F B7 /r MOVZX r64, r/m16
+ // Covers U32, I64/BOT, TMP
+ else if( decl.log_size()>=2 ) enc.add1(0x8B); // zero extend: 8B /r MOV r32, r/m32
+ else throw Utils.TODO();
+
+ // includes modrm internally
+ x86_64_v2.indirectAdr(scale, idx, ptr, off, dst, enc);
+ }
// General form: "ldN dst,[base + idx<<2 + 12]"
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..a97f3d9f 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,34 @@
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.*;
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 ) {
+ // add something to memory
+ // REX.W + 01 /r | REX.W + 81 /0 id
+ // ADD [mem], imm32/reg
+ short ptr = enc.reg(ptr());
+ short idx = enc.reg(idx());
+ short src = enc.reg(val());
+ enc.add1(x86_64_v2.rex(src, ptr, idx));
+ // opcode
+ enc.add1( src == -1 ? 0x81: 0x01);
+ // includes modrm
+ x86_64_v2.indirectAdr(_scale, idx, ptr, _off, src == -1 ? 0 : src, enc);
+ if( src == -1 ) enc.add4(_imm);
+ }
// General form: "add [base + idx<<2 + 12] += src"
@Override public void asm(CodeGen code, SB sb) {
asm_address(code,sb);
@@ -21,8 +36,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 dee4afd4..97586d87 100644
--- a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MemOpX86.java
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MemOpX86.java
@@ -1,11 +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.codegen.*;
import com.seaofnodes.simple.node.*;
import com.seaofnodes.simple.type.*;
-import java.io.ByteArrayOutputStream;
+
import java.lang.StringBuilder;
import java.util.BitSet;
@@ -15,7 +14,7 @@ import java.util.BitSet;
// 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
@@ -24,13 +23,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
@@ -42,6 +41,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
@@ -66,20 +66,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 x86_64_v2.MEM_MASK; // value in GPR or XMM
- throw Utils.TODO();
+ if( i==4 ) return _sz >= 2
+ ? x86_64_v2.MEM_MASK // value in GPR or XMM
+ : x86_64_v2.RMASK; // Bytes and shorts in GPR only
+ return null; // Anti-dependence
}
- @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..c1edb082 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,32 @@
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.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 ) {
+ // F2 0F 59 /r MULSD xmm1,xmm2/m64
+ short dst = (short)(enc.reg(this ) - x86_64_v2.XMM_OFFSET);
+ short src = (short)(enc.reg(in(2)) - x86_64_v2.XMM_OFFSET);
+
+ // Fopcode
+ enc.add1(0xF2);
+ // rex prefix must come next (REX.W is not set)
+ x86_64_v2.rexF(dst, src, 0, false, enc);
+
+ enc.add1(0x0F).add1(0x59).add1(x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, dst, src));
}
// General form: "mulf dst *= src"
@Override public void asm(CodeGen code, SB sb) {
- sb.p(code.reg(this)).p(" = ").p(code.reg(in(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..a9a83fb7 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,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 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.
- @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: "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);
- }
+public class MulIX86 extends ImmX86 {
+ MulIX86( Node add, int imm ) { super(add,imm); }
@Override public String op() { return "muli"; }
+ @Override public String glabel() { return "*"; }
+ @Override int opcode() { return 0x69; }
+ @Override int mod() { return 0; }
}
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..3cd1f795 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,29 @@
package com.seaofnodes.simple.node.cpus.x86_64_v2;
import com.seaofnodes.simple.*;
-import com.seaofnodes.simple.codegen.CodeGen;
-import com.seaofnodes.simple.codegen.RegMask;
+import com.seaofnodes.simple.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 ) {
+ // REX.W + 0F AF /r IMUL r64, r/m64
+ short dst = enc.reg(this ); // src1
+ short src = enc.reg(in(2)); // src2
+ enc.add1(x86_64_v2.rex(dst, src, 0));
+ enc.add1(0x0F); // opcode
+ enc.add1(0xAF); // opcode
+ enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, dst, src));
}
// General form: "mul dst *= src"
@Override public void asm(CodeGen code, SB sb) {
sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" * ").p(code.reg(in(2)));
}
-
- @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..b8b83cb0
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/NegX86.java
@@ -0,0 +1,21 @@
+package com.seaofnodes.simple.node.cpus.x86_64_v2;
+
+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 ) {
+ short dst = enc.reg(this );
+ enc.add1(x86_64_v2.rex(0, dst, 0));
+ enc.add1(0xF7); // opcode
+ enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, 3, dst));
+ }
+ @Override public void asm(CodeGen code, SB sb) { sb.p(code.reg(this)); }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/NewX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/NewX86.java
index cd47c64b..0439adfd 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,25 @@
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.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 aliases edges
- @Override public RegMask regmap(int i) { return i == 1 ? x86_64_v2.RDI_MASK : null; }
- @Override public RegMask outregmap(int i) { return i == 1 ? x86_64_v2.RAX_MASK : null; }
- @Override public RegMask outregmap() { return null; }
-
- // Encoding is appended into the byte array; size is returned
- @Override public int encoding(ByteArrayOutputStream bytes) {
- throw Utils.TODO();
+ @Override public void encoding( Encoding enc ) {
+ enc.external(this,"calloc");
+ // ldi rcx,#1 // number of elements to calloc
+ enc.add1(0xB8 + _arg2Reg).add4(1);
+ // E8 cd CALL rel32;
+ enc.add1(0xE8);
+ enc.add4(0); // offset
}
-
- // General form: "alloc #bytes"
+ // 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..b23249b8 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,39 @@
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.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 void encoding( Encoding enc ) {
+ assert !(in(1) instanceof NotNode); // Cleared out by peeps
+ assert !(in(1) instanceof BoolNode); // Cleared out by peeps
+ short dst = enc.reg(this );
+ short src = enc.reg(in(1));
- @Override public int twoAddress( ) { return 0; }
+ // Pre-zero using XOR dst,dst; since zero'd will not have a
+ // byte-dependency from the setz. Can skip REX is dst is low 8, makes
+ // this a 32b xor, which will also zero the high bits.
+ if( dst >= 8 ) enc.add1(x86_64_v2.rex(dst, dst, 0));
+ enc.add1(0x33); // opcode
+ enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, dst, dst));
- // 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();
+ // test rdi,rdi
+ enc.add1(x86_64_v2.rex(src, src, 0));
+ enc.add1(0x85);
+ enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, src, src));
+
+ // setz (sete dil)
+ enc.add1(x86_64_v2.rex(dst, 0, 0));
+ enc.add1(0x0F);
+ enc.add1(0x94);
+ enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, dst, 0));
}
@Override public void asm(CodeGen code, SB sb) { sb.p(code.reg(this)); }
- @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..0997644d
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/RegX86.java
@@ -0,0 +1,26 @@
+package com.seaofnodes.simple.node.cpus.x86_64_v2;
+
+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 ) {
+ // REX.W + 01 /r
+ short dst = enc.reg(in(1)); // src1
+ short src = enc.reg(in(2)); // src2
+
+ enc.add1(x86_64_v2.rex(dst, src, 0));
+ enc.add1(opcode()); // opcode
+ enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, dst, src));
+ }
+ // General form: "add dst += src"
+ @Override public void asm(CodeGen code, SB sb) {
+ sb.p(code.reg(this)).p(" ").p(glabel()).p("= ").p(code.reg(in(2)));
+ }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/RetX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/RetX86.java
index 433f64cc..a35d43a2 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,19 @@
package com.seaofnodes.simple.node.cpus.x86_64_v2;
import com.seaofnodes.simple.*;
-import com.seaofnodes.simple.codegen.CodeGen;
-import com.seaofnodes.simple.codegen.RegMask;
-import com.seaofnodes.simple.node.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 ) {
+ enc.add1( x86_64_v2.REX_W ).add1( x86_64_v2.imm8(sz) ? 0x83 : 0x81 );
+ enc.add1( x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, 0b000, x86_64_v2.RSP) );
+ if( x86_64_v2.imm8(sz) ) enc.add1(sz);
+ else enc.add4(sz);
+ }
+ enc.add1(0xC3);
}
-
- @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..07256604 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,40 @@
package com.seaofnodes.simple.node.cpus.x86_64_v2;
-import com.seaofnodes.simple.*;
+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;}
-
- // 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 outregmap() { return x86_64_v2.WMASK; }
+ 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; }
- // Output is same register as input#1
+ @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; }
- // 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); // Also src1
+ enc.add1(x86_64_v2.rex(0, dst, 0));
+ enc.add1( opcode());
+
+ enc.add1( x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, mod(), dst) );
+
+ // immediate(4 bytes) 32 bits or (1 byte)8 bits
+ if( x86_64_v2.imm8(_imm) ) enc.add1(_imm);
+ else enc.add4(_imm);
}
- // 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..b1be643c 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,24 @@
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.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 ) {
+ short dst = enc.reg(this ); // src1
+ short src = enc.reg(in(2)); // src2
+ enc.add1(x86_64_v2.rex(0, dst, 0));
+ enc.add1(opcode()); // opcode
+ enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, 7, dst));
}
-
- @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..b2902086 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,48 @@
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.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 ) {
+ // REX + 0F 94
+ short dst = enc.reg(this );
+
+ // Optional rex, for dst
+ if( dst >= 8 ) enc.add1(x86_64_v2.rex(0, dst, 0));
+ enc.add1(0x0F); // opcode
+ enc.add1(switch (_bop) {
+ case "==" -> 0x94; // SETE
+ case "<" -> _unsigned ? 0x92 : 0x9C; // SETB /SETL
+ case "<=" -> _unsigned ? 0x96 : 0x9E; // SETBE/SETLE
+ case ">=" -> _unsigned ? 0x93 : 0x9D; // SETAE/SETGE
+ default -> throw Utils.TODO();
+ });
+ enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, 0, dst));
+
+ // low 8 bites are set, now zero extend for next instruction
+ if( dst >= 8 ) enc.add1(x86_64_v2.rex(0, dst, 0));
+ enc.add1(0x0F); // opcode
+ enc.add1(0xB6); // opcode
+ enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, dst, dst));
}
@Override public void asm(CodeGen code, SB sb) {
@@ -35,7 +50,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..0b75b456 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,40 @@
package com.seaofnodes.simple.node.cpus.x86_64_v2;
+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) {
+ short dst = enc.reg(this); // Also src1
+ enc.add1(x86_64_v2.rex(0, dst, 0));
+ enc.add1( opcode());
- // General form
- // General form: "shli dst << #imm"
+ enc.add1( x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, mod(), dst) );
+
+ // immediate(4 bytes) 32 bits or (1 byte)8 bits
+ if( x86_64_v2.imm8(_imm) ) enc.add1(_imm);
+ else enc.add4(_imm);
+ }
@Override public void asm(CodeGen code, SB sb) {
- sb.p(code.reg(this)).p(" = ").p(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..9f358575 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,24 @@
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.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 ) {
+ short dst = enc.reg(this ); // src1
+ short src = enc.reg(in(2)); // src2
+ enc.add1(x86_64_v2.rex(0, dst, 0));
+ enc.add1(opcode()); // opcode
+ enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, 4, dst));
}
-
- @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..893eb23d 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,35 @@
package com.seaofnodes.simple.node.cpus.x86_64_v2;
+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) {
+ short dst = enc.reg(this); // Also src1
+ enc.add1(x86_64_v2.rex(0, dst, 0));
+ enc.add1( opcode());
- // General form
- // General form: "shri dst >>> #imm"
+ enc.add1( x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, mod(), dst) );
+
+ // immediate(4 bytes) 32 bits or (1 byte)8 bits
+ if( x86_64_v2.imm8(_imm) ) enc.add1(_imm);
+ else enc.add4(_imm);
+ }
@Override public void asm(CodeGen code, SB sb) {
- sb.p(code.reg(this)).p(" = ").p(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..d869260f 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,24 @@
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.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 ) {
+ short dst = enc.reg(this ); // src1
+ short src = enc.reg(in(2)); // src2
+ enc.add1(x86_64_v2.rex(0, dst, 0));
+ enc.add1(opcode()); // opcode
+ enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, 5, dst));
}
-
- @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..960ea6f8 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,107 @@
package com.seaofnodes.simple.node.cpus.x86_64_v2;
+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 ) {
+ // REX.W + 8B /r MOV r64, r/m64
+ short dst = enc.reg(this );
+ short src = enc.reg(in(1));
+ if( dst == x86_64_v2.FLAGS ) {
+ // mov reg, flags
+ // push rcx
+ // popf (Pop the top of the stack into the FLAGS register)
+ // 50+rd PUSH r64
+ x86_64_v2.rexF(0,src,0,false,enc);
+ enc.add1(0x50 + src);
+ // popf
+ enc.add1(0x9D);
+ return;
+ }
+ if( src == x86_64_v2.FLAGS ) {
+ // mov flags, reg
+ // pushf; pop reg
+ enc.add1(0x9C);
+ // 58+ rd POP r64
+ x86_64_v2.rexF(0,dst,0,false, enc);
+ enc.add1(0x58 + dst);
+ return;
+ }
+
+ // Flag for being either XMM or stack
+ boolean dstX = dst >= x86_64_v2.XMM_OFFSET;
+ boolean srcX = src >= x86_64_v2.XMM_OFFSET;
+
+ // Stack spills
+ if( dst >= x86_64_v2.MAX_REG ) {
+ int off = enc._fun.computeStackOffset(enc._code,dst);
+ if( src >= x86_64_v2.MAX_REG ) {
+ // Rare stack-stack move. push [RSP+soff]; pop [RSP+doff]
+ int soff = enc._fun.computeStackOffset(enc._code,src);
+ enc.add1(0xFF);
+ x86_64_v2.indirectAdr(0, (short)-1/*index*/, (short)x86_64_v2.RSP, soff, 6, enc);
+ enc.add1(0x8F);
+ x86_64_v2.indirectAdr(0, (short)-1/*index*/, (short)x86_64_v2.RSP, off, 0, enc);
+ return;
+ }
+ StoreX86.encVal(enc, srcX ? TypeFloat.F64 : TypeInteger.BOT, (short)x86_64_v2.RSP, (short)-1/*index*/, src, off, 0);
+ return;
+ }
+ if( src >= x86_64_v2.MAX_REG ) {
+ int off = enc._fun.computeStackOffset(enc._code,src);
+ LoadX86.enc(enc, dstX ? TypeFloat.F64 : TypeInteger.BOT, dst, (short)x86_64_v2.RSP, (short)-1, off, 0);
+ return;
+ }
+
+ // reg-reg move. Adjust numbering for GPR vs FPR reg set
+ if( dstX ) dst -= (short) x86_64_v2.XMM_OFFSET;
+ if( srcX ) src -= (short) x86_64_v2.XMM_OFFSET;
+
+ // 0x66 if moving between register classes
+ if( dstX ^ srcX ) enc.add1(0x66);
+ if( !dstX && srcX )
+ { short tmp=src; src=dst; dst=tmp; }
+ enc.add1(x86_64_v2.rex(dst, src, 0));
+
+ // pick opcode based on regs
+ if( !dstX && !srcX ) {
+ // reg->reg (MOV r64, r/m64)
+ enc.add1(0x8B);
+ } else if( dstX && srcX ) {
+ // xmm->xmm (NP 0F 28 /r MOVAPS xmm1, xmm2/m128)
+ enc.add1(0x0F);
+ enc.add1(0x28);
+ } else if( dstX && !srcX ) {
+ // reg->xmm (66 REX.W 0F 6E /r MOVQ xmm, r/m64)
+ enc.add1(0x0F);
+ enc.add1(0x6E);
+ } else if( !dstX && srcX ) {
+ // xmm->reg(66 0F 7E /r MOVQ r/m64, xmm)
+ enc.add1(0x0F);
+ enc.add1(0x7E);
+ }
+
+ enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, dst, src));
+ }
+
+ // General form: "mov dst = src"
+ @Override public void asm(CodeGen code, SB sb) {
+ FunNode fun = code._encoding==null ? null : code._encoding._fun;
+ sb.p(code.reg(this,fun)).p(" = ").p(code.reg(in(1),fun));
}
}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/StoreX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/StoreX86.java
index 024ef904..76fb4e8d 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,19 +1,19 @@
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.ConstantNode;
import com.seaofnodes.simple.node.Node;
import com.seaofnodes.simple.node.StoreNode;
+import com.seaofnodes.simple.type.Type;
+import com.seaofnodes.simple.type.TypeFloat;
import java.util.BitSet;
public class StoreX86 extends MemOpX86 {
StoreX86( StoreNode st, Node base, Node idx, int off, int scale, int imm, Node val ) {
super(st,st, base, idx, off, scale, imm, val);
}
-
@Override public String op() { return "st"+_sz; }
-
@Override public StringBuilder _printMach(StringBuilder sb, BitSet visited) {
Node val = val();
sb.append(".").append(_name).append("=");
@@ -21,9 +21,60 @@ public class StoreX86 extends MemOpX86 {
else val._print0(sb,visited);
return sb.append(";");
}
-
// Register mask allowed as a result. 0 for no register.
@Override public RegMask outregmap() { return null; }
+ @Override public void encoding( Encoding enc ) {
+ // REX.W + C7 /0 id MOV r/m64, imm32 |
+ // REX.W + 89 /r MOV r/m64, r64
+ short ptr = enc.reg(ptr());
+ short idx = enc.reg(idx());
+ short src = enc.reg(val());
+
+ if( src == -1 ) {
+ int log = _declaredType.log_size();
+ x86_64_v2.rexF(-1, ptr, idx, log==3, enc);
+ switch( log ) {
+ case 0: enc .add1(0xC6); break;
+ case 1: enc.add1(0x66).add1(0xC7); break;
+ case 2: enc .add1(0xC7); break;
+ case 3: enc .add1(0xC7); break;
+ }
+ x86_64_v2.indirectAdr(_scale, idx, ptr, _off, src, enc);
+ switch( log ) {
+ case 0: enc.add1(_imm); break;
+ case 1: enc.add2(_imm); break;
+ case 2: enc.add4(_imm); break;
+ case 3: enc.add4(_imm); break; // Limit of a 4 byte immediate
+ }
+ } else {
+ encVal(enc,_declaredType,ptr,idx,src,_off,_scale);
+ }
+ }
+
+ // Non-immediate encoding
+ static void encVal( Encoding enc, Type decl, short ptr, short idx, short src, int off, int scale ) {
+ int log = decl.log_size();
+ // Float reg being stored
+ if( src >= x86_64_v2.XMM_OFFSET ) {
+ src -= (short)x86_64_v2.XMM_OFFSET;
+ assert log == 2 || log == 3;
+ enc.add1( log==2 ? 0xF3 : 0xF2 );
+ x86_64_v2.rexF(src,ptr,idx,false,enc);
+ enc.add1(0x0F).add1(0x11);
+ } else {
+ // byte stores from sil, dil, bpl, spl need a rex
+ if( log == 0 && src >= x86_64_v2.RSP ) enc.add1(x86_64_v2.rex(src,ptr,idx,false));
+ else x86_64_v2.rexF(src,ptr,idx,log==3,enc);
+ switch( log ) {
+ case 0: enc .add1(0x88); break;
+ case 1: enc.add1(0x66).add1(0x89); break;
+ case 2: enc .add1(0x89); break;
+ case 3: enc .add1(0x89); break;
+ }
+ }
+ x86_64_v2.indirectAdr(scale, idx, ptr, off, src, enc);
+ }
+
// General form: "stN [base + idx<<2 + 12],val"
@Override public void asm(CodeGen code, SB sb) {
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..0a169a22 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,31 @@
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.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 ) {
+ // F2 0F 5C /r SUBSD xmm1, xmm2/m64
+ short dst = (short)(enc.reg(this ) - x86_64_v2.XMM_OFFSET);
+ short src = (short)(enc.reg(in(2)) - x86_64_v2.XMM_OFFSET);
+ // Fopcode
+ enc.add1(0xF2);
+ // rex prefix must come next (REX.W is not set)
+ x86_64_v2.rexF(dst, src, 0, false, enc);
+
+ enc.add1(0x0F).add1(0x5C);
+ enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, dst, src));
}
// General form: "subf dst -= src"
@Override public void asm(CodeGen code, SB sb) {
- sb.p(code.reg(this)).p(" = ").p(code.reg(in(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..be4d1a83 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,55 @@
package com.seaofnodes.simple.node.cpus.x86_64_v2;
import com.seaofnodes.simple.*;
-import com.seaofnodes.simple.codegen.CodeGen;
-import com.seaofnodes.simple.codegen.RegMask;
-import com.seaofnodes.simple.node.ConstantNode;
-import com.seaofnodes.simple.node.MachNode;
+import com.seaofnodes.simple.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 ) {
+ short dst = enc.reg(this);
+ // Short form for zero
+ if( _con==Type.NIL ) {
+ // XOR dst,dst. Can skip REX is dst is low 8, makes this a 32b
+ // xor, which will also zero the high bits.
+ if( dst >= 8 ) enc.add1(x86_64_v2.rex(dst, dst, 0));
+ enc.add1(0x33); // opcode
+ enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, dst, dst));
+ return;
+ }
+
+ // lea to load function pointer address
+ // lea rax, [rip+disp32]
+ enc.relo(this);
+ enc.add1(x86_64_v2.rex(dst, 0, 0));
+ enc.add1(0x8D); // opcode
+ enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.INDIRECT, dst, 0b101));
+ enc.add4(0);
+ }
+
+ @Override public byte encSize(int delta) {
+ return 7;
+ }
- // 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 +61,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..ec4eeb3b
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/UJmpX86.java
@@ -0,0 +1,53 @@
+package com.seaofnodes.simple.node.cpus.x86_64_v2;
+
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.codegen.*;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.type.Type;
+import java.util.BitSet;
+
+// unconditional jump
+public class UJmpX86 extends CFGNode implements MachNode, RIPRelSize {
+ UJmpX86() { }
+ @Override public String op() { return "jmp"; }
+ @Override public String label() { return op(); }
+ @Override public StringBuilder _print1( StringBuilder sb, BitSet visited ) {
+ return sb.append("jmp ");
+ }
+ @Override public RegMask regmap(int i) {return null; }
+ @Override public RegMask outregmap() { return null; }
+ @Override public Type compute() { throw Utils.TODO(); }
+ @Override public Node idealize() { throw Utils.TODO(); }
+ @Override public void encoding( Encoding enc ) {
+ enc.jump(this,uctrl());
+ // Short-form jump
+ enc.add1(0xEB).add1(0);
+ //// E9 cd JMP rel32
+ //enc.add1(0xE9).add4(0);
+ }
+
+ // Delta is from opcode start, but X86 measures from the end of the 2-byte encoding
+ @Override public byte encSize(int delta) { return (byte)(x86_64_v2.imm8(delta-2) ? 2 : 5); }
+
+ // Delta is from opcode start
+ @Override public void patch( Encoding enc, int opStart, int opLen, int delta ) {
+ byte[] bits = enc.bits();
+ if( opLen==2 ) {
+ assert bits[opStart] == (byte)0xEB;
+ delta -= 2; // Offset from opcode END
+ assert (byte)delta==delta;
+ bits[opStart+1] = (byte)delta;
+ } else {
+ assert bits[opStart] == (byte)0xEB;
+ bits[opStart] = (byte)0xE9; // Long form
+ delta -= 5; // Offset from opcode END
+ enc.patch4(opStart+1,delta);
+ }
+ }
+
+ @Override public void asm(CodeGen code, SB sb) {
+ CFGNode target = uctrl();
+ //assert target.nOuts() > 1; // Should optimize jmp to empty targets
+ sb.p(label(target));
+ }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/XorIX86.java b/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/XorIX86.java
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 aacd2352..2b7fddb1 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,229 @@ 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);
+ public static RegMask RAX_MASK = new RegMask(RAX);
+ public static RegMask RCX_MASK = new RegMask(RCX);
+ public static RegMask RDX_MASK = new RegMask(RDX);
+ public static RegMask RDI_MASK = new RegMask(RDI);
+ public static RegMask R08_MASK = new RegMask(R08);
+ public static RegMask R09_MASK = new RegMask(R09);
+ public static RegMask RSI_MASK = new RegMask(RSI);
+
+ public static RegMask XMM0_MASK = new RegMask(XMM0);
+
+ // Encoding
+ public static int REX = 0x40;
+ public static int REX_W = 0x48;
+ public static int REX_WR = 0x4C;
+ public static int REX_WRB= 0x4D;
+ public static int REX_WB = 0x49;
+
+ public enum MOD {
+ INDIRECT, // [mem]
+ INDIRECT_disp8, // [mem + 0x12]
+ INDIRECT_disp32, // [mem + 0x12345678]
+ DIRECT, // mem
+ };
- static final RegMask XMM0_MASK = new RegMask(XMM0);
+ // 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]",
+ public static int modrm(MOD mod, int reg, int m_r) {
+ if( reg == -1 ) reg=0; // Missing reg in this flavor
+ // combine all the bits
+ assert 0 <= reg && reg < 16;
+ assert 0 <= m_r && m_r < 16;
+ return (mod.ordinal() << 6) | ((reg & 0x07) << 3) | m_r & 0x07;
+ }
+
+ // 00 000 000
+ // same bit-layout as modrm
+ public static int sib(int scale, int index, int base) {
+ assert 0 <= base && base < 16;
+ assert 0 <= index && index < 16;
+ return (scale << 6) | ((index & 0x07) << 3) | base & 0x07;
+ }
+
+ // reg1 is reg(R)
+ // reg2 is r/mem(B)
+ // reg3 is X(index)
+ // reg4 is X(base)
+
+ // 0 denotes no direct register
+ public static int rex(int reg, int ptr, int idx, boolean wide) {
+ // assuming 64 bit by default so: 0100 1000
+ assert -1 <= reg && reg < 16;
+ assert -1 <= ptr && ptr < 16;
+ assert -1 <= idx && idx < 16;
+
+ int rex = wide ? REX_W : REX;
+ if( 8 <= reg ) rex |= 0b00000100; // REX.R
+ if( 8 <= ptr ) rex |= 0b00000001; // REX.B
+ if( 8 <= idx ) rex |= 0b00000010; // REX.X
+ return rex;
+ }
+
+ public static int rex(int reg, int ptr, int idx) {
+ return rex(reg, ptr, idx, true);
+ }
+
+ // rex for floats. Return size (0 or 1)
+ // don't need to use REX if 0x40.
+ public static byte rexF(int reg, int ptr, int idx, boolean wide, Encoding enc) {
+ int rex = rex(reg, ptr, idx, wide);
+ if (rex == REX) return 0;
+ enc.add1(rex);
+ return 1;
+ }
+ // Function used for encoding indirect memory addresses
+ // Does not always generate SIB byte e.g index == -1.
+ // -1 denotes empty value, not set - note 0 is different from -1 as it can represent rax.
+ // Looks for best mod locally
+ public static void indirectAdr( int scale, short index, short base, int offset, int reg, Encoding enc ) {
+ // Assume indirect
+ assert 0 <= base && base < 16;
+ assert -1 <= index && index < 16 && index != RSP;
+
+ MOD mod = MOD.INDIRECT;
+ // is 1 byte enough or need more?
+ if( offset != 0 )
+ mod = imm8(offset)
+ ? MOD.INDIRECT_disp8
+ : MOD.INDIRECT_disp32;
+
+ if( mod == MOD.INDIRECT && (base == RBP || base == R13) ) {
+ mod = MOD.INDIRECT_disp8;
+ } else if( index == -1 && (base == RSP || base == R12) ) {
+ index = RSP;
+ }
+
+ if( index == -1 ) {
+ enc.add1(modrm(mod, reg, base));
+ } else {
+ enc.add1(modrm(mod, reg, x86_64_v2.RSP));
+ enc.add1(sib(scale, index, base));
+ }
+
+ if( mod == MOD.INDIRECT_disp8 ) {
+ enc.add1(offset);
+ } else if( mod == MOD.INDIRECT_disp32 ) {
+ enc.add4(offset);
+ }
+ }
+
+
+ // Limit of float args passed in registers
+ static RegMask[] XMMS8 = new RegMask[]{
+ new RegMask(XMM0), new RegMask(XMM1), new RegMask(XMM2), new RegMask(XMM3),
+ new RegMask(XMM4), new RegMask(XMM5), new RegMask(XMM6), new RegMask(XMM7),
};
- @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();
+ };
+ }
+
+ // 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(param passing)
- static RegMask[] CALLINMASK_WIN64 = new RegMask[] {
+ // 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,285 +271,243 @@ 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++;
}
- throw Utils.TODO(); // Pass on stack slot
+ int nstk = Math.max(icnt-6,0)+Math.max(fcnt-8,0);
+ return tfp.arg(idx-2) instanceof TypeFloat
+ ? fcnt<8 ? XMMS8 [fcnt] : new RegMask(MAX_REG+maxArgSlot+nstk)
+ : icnt<6 ? SYS5_CALL[icnt] : new RegMask(MAX_REG+maxArgSlot+nstk);
+ }
+ static short maxArgSlotSys5(TypeFunPtr tfp) {
+ int icnt=0, fcnt=0; // Count of ints, floats
+ for( int i=0; i<tfp.nargs(); i++ ) {
+ if( tfp.arg(i) instanceof TypeFloat ) fcnt++;
+ else icnt++;
+ }
+ int nstk = Math.max(icnt-6,0)+Math.max(fcnt-8,0);
+ return (short)nstk;
}
- // 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 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 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 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);
- 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 XorNode xor -> xor(xor);
-
- case LoopNode loop -> new LoopNode(loop);
- case RegionNode region-> new RegionNode(region);
+ @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 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 XCtrlNode x -> new ConstantNode(Type.XCONTROL);
+ case XorNode xor -> xor(xor);
+
+ case LoopNode loop -> new LoopNode(loop);
+ case RegionNode region -> new RegionNode(region);
default -> throw Utils.TODO();
};
}
+ public static boolean imm8( long imm ) { return -128 <= imm && imm <= 127; }
+
+ public static boolean imm32( long imm ) { return (int)imm==imm; }
// Attempt a full LEA-style break down.
- private Node add( AddNode add ) {
+ private Node add(AddNode add) {
Node lhs = add.in(1);
Node rhs = add.in(2);
- if( lhs instanceof LoadNode ld && ld.nOuts()==1 )
- return new AddMemX86(add,address(ld),ld.ptr(),idx,off,scale, imm(rhs),val);
+ 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 )
- throw Utils.TODO(); // Swap load sides
+// if(rhs instanceof LoadNode ld && ld.nOuts() == 1 && ld._declaredType.log_size() >= 3) {
+// throw Utils.TODO(); // Swap load sides
+// }
// Attempt a full LEA-style break down.
// Returns one of AddX86, AddIX86, LeaX86, or LHS
- if( rhs instanceof ConstantNode 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);
};
}
@@ -367,28 +516,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);
}
@@ -396,21 +547,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);
}
@@ -418,58 +569,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();
@@ -479,10 +630,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 1a233aa2..da269d31 100644
--- a/src/main/java/com/seaofnodes/simple/print/ASMPrinter.java
+++ b/src/main/java/com/seaofnodes/simple/print/ASMPrinter.java
@@ -1,11 +1,12 @@
package com.seaofnodes.simple.print;
-import com.seaofnodes.simple.codegen.CodeGen;
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.node.*;
-import com.seaofnodes.simple.type.TypeFunPtr;
-import com.seaofnodes.simple.type.TypeMem;
-import com.seaofnodes.simple.type.TypeRPC;
+import com.seaofnodes.simple.type.*;
+import java.util.HashSet;
public abstract class ASMPrinter {
@@ -18,17 +19,70 @@ public abstract class ASMPrinter {
for( int i=0; i<code._cfg._len; i++ )
if( code._cfg.at(i) instanceof FunNode fun )
iadr = print(iadr,sb,code,fun,i);
+
+ // Skip padding
+ while( ((iadr+7) & -8) > iadr )
+ iadr++;
+
+ // constant pool
+ Encoding enc = code._encoding;
+ if( enc!=null && !enc._bigCons.isEmpty() ) {
+ iadr = (iadr+15)&-16; // pad to 16
+ HashSet<Type> targets = new HashSet<>();
+ sb.p("--- Constant Pool ------").nl();
+ // By log size
+ for( int log = 3; log >= 0; log-- ) {
+ for( Node op : enc._bigCons.keySet() ) {
+ Encoding.Relo relo = enc._bigCons.get(op);
+ if( targets.contains(relo._t) ) continue;
+ targets.add(relo._t);
+ if( relo._t.log_size()==log ) {
+ sb.hex2(iadr).p(" ");
+ if( relo._t instanceof TypeTuple tt ) {
+ for( Type tx : tt._types ) {
+ switch( log ) {
+ case 0: sb.hex1(enc.read1(iadr)); break;
+ case 1: sb.hex2(enc.read2(iadr)); break;
+ case 2: sb.hex4(enc.read4(iadr)); break;
+ case 3: sb.hex8(enc.read8(iadr)); break;
+ }
+ iadr += (1<<log);
+ sb.p(" ");
+ }
+ } else {
+ switch( log ) {
+ case 0: sb.hex1(enc.read1(iadr)).fix(9-1,""); break;
+ case 1: sb.hex2(enc.read2(iadr)).fix(9-2,""); break;
+ case 2: sb.hex4(enc.read4(iadr)).fix(9-4,""); break;
+ case 3: sb.hex8(enc.read8(iadr)).p(" "); break;
+ }
+ iadr += (1<<log);
+ }
+ relo._t.print(sb).nl();
+ }
+ }
+ }
+ }
+
+
return sb;
}
private static int print(int iadr, SB sb, CodeGen code, FunNode fun, int cfgidx) {
+ FunNode old=null;
+ if( code._encoding!=null ) {
+ old = code._encoding._fun;
+ code._encoding._fun = fun; // Useful printing after RA
+ }
// Function header
sb.nl().p("---");
if( fun._name != null ) sb.p(fun._name).p(" ");
fun.sig().print(sb);
sb.p("---------------------------").nl();
+ iadr = (iadr + 15)&-16; // All function entries padded to 16 align
-
+ if( fun._frameAdjust != 0 )
+ iadr = doInst(iadr,sb,code,fun,cfgidx,fun,true,true);
while( !(code._cfg.at(cfgidx) instanceof ReturnNode) )
iadr = doBlock(iadr,sb,code,fun,cfgidx++);
@@ -36,69 +90,74 @@ public abstract class ASMPrinter {
sb.p("---");
fun.sig().print(sb);
sb.p("---------------------------").nl();
+ if( code._encoding != null )
+ code._encoding._fun = old;
return iadr;
}
- static private final int encWidth = 8;
static private final int opWidth = 5;
static private final int argWidth = 30;
static int doBlock(int iadr, SB sb, CodeGen code, FunNode fun, int cfgidx) {
+ final int encWidth = code._mach==null ? 2 : code._mach.defaultOpSize()*2;
CFGNode bb = code._cfg.at(cfgidx);
if( bb != fun && !(bb instanceof IfNode) && !(bb instanceof CallEndNode) && !(bb instanceof CallNode) && !(bb instanceof CProjNode && bb.in(0) instanceof CallEndNode ))
- sb.p(bb instanceof LoopNode ? "LOOP" : "L").p(bb._nid).p(":").nl();
+ sb.p(label(bb)).p(":").nl();
if( bb instanceof CallNode ) return iadr;
final boolean postAlloc = code._phase.ordinal() > CodeGen.Phase.RegAlloc.ordinal();
+ final boolean postEncode= code._phase.ordinal() >=CodeGen.Phase.Encoding.ordinal();
- // Count Phis
- int nPhi=0;
- for( ; nPhi<bb.nOuts(); nPhi++ )
- if( !(bb.out(nPhi) instanceof PhiNode) )
- break;
-
- if( nPhi>0 ) {
- // Post-alloc phi prints all on one line
+ boolean once=false;
+ for( Node n : bb.outs() ) {
+ if( !(n instanceof PhiNode phi) ) continue;
+ if( phi._type instanceof TypeMem || phi._type instanceof TypeRPC ) continue; // Nothing for the hidden ones
+ // Post-RegAlloc phi prints all on one line
if( postAlloc ) {
- sb.fix(4," ").p(" ").fix(encWidth,"").p(" ");
- for( int i=0; i<nPhi; i++ ) {
- PhiNode phi = (PhiNode)bb.out(i);
- if( !(phi._type instanceof TypeMem || phi._type instanceof TypeRPC) ) // Nothing for the hidden ones
- sb.p(phi._label).p(':').p(code.reg(phi)).p(',');
- }
- sb.unchar().nl();
-
+ if( !once ) { once=true; sb.fix(4," ").p(" ").fix(encWidth,"").p(" "); }
+ sb.p(phi._label).p(':').p(code.reg(phi,fun)).p(',');
} else {
- for( int j=0; j<nPhi; j++ ) {
- PhiNode phi = (PhiNode)bb.out(j);
- if( phi._type instanceof TypeMem || phi._type instanceof TypeRPC ) continue; // Nothing for the hidden ones
- sb.fix(4," ").p(" ").fix(encWidth,"").p(" ").fix(opWidth,phi._label).p(" ");
- sb.p(code.reg(phi));
- if( !(phi instanceof ParmNode) ) {
- sb.p(" = phi( ");
- for( int i=1; i<phi.nIns(); i++ )
- sb.p("N").p(phi.in(i)._nid).p(",");
- sb.unchar().p(" )");
- }
- sb.nl();
+ // Pre-RegAlloc phi prints one line per
+ sb.fix(4," ").p(" ").fix(encWidth,"").p(" ").fix(opWidth,phi._label).p(" ").p(code.reg(phi,fun));
+ if( phi.getClass() == PhiNode.class ) {
+ sb.p(" = phi( ");
+ for( int i=1; i<phi.nIns(); i++ )
+ sb.p("N").p(phi.in(i)._nid).p(",");
+ sb.unchar().p(" )");
}
+ sb.nl();
}
}
+ if( once ) sb.unchar().nl();
// All the non-phis
- for( int i=nPhi; i<bb.nOuts(); i++ )
- iadr = doInst(iadr, sb,code,fun,bb,bb.out(i),postAlloc );
+ for( int i=0; i<bb.nOuts(); i++ )
+ if( !(bb.out(i) instanceof PhiNode) )
+ iadr = doInst(iadr, sb,code, fun, cfgidx, bb.out(i),postAlloc, postEncode );
return iadr;
}
- static int doInst(int iadr, SB sb, CodeGen code, FunNode fun, CFGNode bb, Node n, boolean postAlloc ) {
- if( n instanceof CProjNode ) return iadr;
- if( n instanceof CalleeSaveNode && postAlloc ) return iadr;
+ static int doInst( int iadr, SB sb, CodeGen code, FunNode fun, int cfgidx, Node n, boolean postAlloc, boolean postEncode ) {
+ if( n==null || n instanceof CProjNode ) return iadr;
+ if( postAlloc && n instanceof CalleeSaveNode ) return iadr;
+ if( postEncode && n instanceof ProjNode ) return iadr;
+ if( n instanceof MemMergeNode ) return iadr;
+ if( n.getClass() == ConstantNode.class ) return iadr; // Default placeholders
+ final int dopz = code._mach==null ? 2 : code._mach.defaultOpSize();
+ final int encWidth = dopz*2;
// All blocks ending in a Region will need to either fall into or jump
// to this block. Until the post-reg-alloc block layout cleanup, we
// need to assume a jump. There's no real hardware op here, yet.
- if( n instanceof RegionNode && !(n instanceof FunNode) ) {
- sb.hex4(iadr++).p(" ").fix(encWidth,"??").p(" ").fix(opWidth,"JMP").p(" ").fix(argWidth,"L"+n._nid).nl();
+ if( n instanceof RegionNode cfg && !(n instanceof FunNode) ) {
+ if( postEncode ) return iadr; // All jumps inserted already
+ while( cfgidx < code._cfg._len-1 ) {
+ CFGNode next = code._cfg.at(++cfgidx);
+ if( next == n ) return iadr; // Fall-through, no branch
+ if( next.nOuts()>1 )
+ break; // Has code in the block, need to jump around
+ // No code in the block, can fall through it
+ }
+ sb.hex2(iadr++).p(" ").fix(encWidth,"??").p(" ").fix(opWidth,"JMP").p(" ").fix(argWidth,label(cfg)).nl();
return iadr;
}
@@ -106,30 +165,44 @@ public abstract class ASMPrinter {
// get indent slightly and just print their index & node#
if( n instanceof ProjNode proj ) {
if( proj._type instanceof TypeMem ) return iadr; // Nothing for the hidden ones
- sb.fix(4," ").p(" ").fix(encWidth,"").p(" ").fix(opWidth,proj._label==null ? "---" : proj._label).p(" ").p(code.reg(n)).nl();
+ sb.fix(4," ").p(" ").fix(encWidth,"").p(" ").fix(opWidth,proj._label==null ? "---" : proj._label).p(" ").p(code.reg(n,fun)).nl();
return iadr;
}
// ADDR ENCODING Op--- dst = src op src // Comment
// 1234 abcdefgh ld4 RAX = [Rbase + off] // Comment
- sb.hex4(iadr);
- sb.p(" ");
+ sb.hex2(iadr).p(" ");
// Encoding
- int size = 1; // TODO: Fake encoding size
- iadr += size;
- sb.fix(encWidth,"??");
+ int fatEncoding = 0;
+ if( code._encoding != null ) {
+ int size = code._encoding._opLen[n._nid];
+ if( code._asmLittle )
+ for( int i=0; i<Math.min(size,dopz); i++ )
+ sb.hex1(code._encoding._bits.buf()[iadr++]);
+ else {
+ iadr += Math.min(size,dopz);
+ for( int i=0; i<Math.min(size,dopz); i++ )
+ sb.hex1(code._encoding._bits.buf()[iadr-i-1]);
+ }
+ for( int i=size*2; i<encWidth; i++ )
+ sb.p(" ");
+ fatEncoding = size - (encWidth>>1); // Not-printed parts of encoding
+ } else
+ sb.fix(encWidth,"");
sb.p(" ");
// Op; generally "ld4" or "call"
- sb.fix(opWidth, n instanceof MachNode mach ? mach.op() : n.label());
- sb.p(" ");
+ sb.fix(opWidth, n instanceof MachNode mach ? mach.op() : n.label()).p(" ");
// General asm args
+ String isMultiOp = null;
if( n instanceof MachNode mach ) {
int old = sb.len();
mach.asm(code,sb);
- sb.fix(argWidth-(sb.len()-old),""); // Pad out
+ int len = sb.len();
+ isMultiOp = isMultiOp(sb,old,len);
+ sb.fix(argWidth-(len-old),""); // Pad out
} else if( !(n._type instanceof TypeMem) ) {
// Room for some inputs
@@ -150,14 +223,64 @@ public abstract class ASMPrinter {
sb.nl();
+ // Printing more op bits than fit
+ if( isMultiOp != null && code._encoding != null ) {
+ // Multiple ops, template style, no RA, no scheduling. Print out
+ // one-line-per-newline, with encoding bits up front.
+ int size = code._encoding._opLen[n._nid];
+ int off = Math.min(size,dopz);
+ while( isMultiOp!=null ) {
+ sb.hex2(iadr).p(" ");
+ int len = Math.min(size-off,dopz);
+ for( int i=0; i<len; i++ )
+ sb.hex1(code._encoding._bits.buf()[iadr++]);
+ off += len;
+ for( int i=len; i<dopz; i++ ) sb.p(" ");
+ sb.p(" ");
+ int x = isMultiOp.indexOf('\n');
+ if( x== -1 ) { // Last line
+ sb.p(isMultiOp).nl();
+ isMultiOp = null;
+ } else {
+ sb.p(isMultiOp.substring(0,x+1)); // Includes the newline
+ isMultiOp = isMultiOp.substring(x+1);
+ }
+
+ }
+
+ } else if( fatEncoding > 0 ) {
+ // Extra bytes past the default encoding width, all put on a line by
+ // themselves. X86 special for super long encodings
+ sb.hex2(iadr).p(" ");
+ for( int i=0; i<fatEncoding; i++ )
+ sb.hex1(code._encoding._bits.buf()[iadr++]);
+ sb.nl();
+ }
+
+
// MultiNodes are immediately followed by projection(s)
if( !(n instanceof CFGNode) && n instanceof MultiNode ) {
for( Node proj : n._outputs ) {
- assert proj instanceof ProjNode;
- doInst(iadr,sb,code,fun,bb,proj,postAlloc);
+ if( proj instanceof ProjNode ) // it could also be an ante-dep
+ doInst(iadr,sb,code,fun, cfgidx, proj,postAlloc,postEncode);
}
}
return iadr;
}
+
+ private static String label( CFGNode bb ) {
+ return (bb instanceof LoopNode ? "LOOP" : "L")+bb._nid;
+ }
+
+ private static String isMultiOp(SB sb, int old, int len) {
+ for( int i=old; i<len; i++ )
+ if( sb.at(i)=='\n' ) {
+ String s = sb.subString(i+1,len);
+ sb.setLen(i);
+ return s;
+ }
+ return null;
+ }
+
}
diff --git a/src/main/java/com/seaofnodes/simple/print/IRPrinter.java b/src/main/java/com/seaofnodes/simple/print/IRPrinter.java
index c91f844b..c6e478c5 100644
--- a/src/main/java/com/seaofnodes/simple/print/IRPrinter.java
+++ b/src/main/java/com/seaofnodes/simple/print/IRPrinter.java
@@ -16,6 +16,7 @@ public abstract class IRPrinter {
// NNID NNAME DDEF DDEF [[ UUSE UUSE ]] TYPE
// 1234 sssss 1234 1234 1234 1234 1234 1234 tttttt
public static SB printLine( Node n, SB sb ) {
+ if( n==null ) return sb;
sb.p("%4d %-7.7s ".formatted(n._nid,n.label()));
if( n._inputs==null )
return sb.p("DEAD\n");
@@ -211,7 +212,8 @@ public abstract class IRPrinter {
printLine(n,sb);
if( !(n instanceof CFGNode) && n instanceof MultiNode )
for( Node use : n._outputs )
- printLine(use,sb);
+ if( use instanceof ProjNode )
+ printLine(use,sb);
}
}
@@ -281,7 +283,8 @@ public abstract class IRPrinter {
printLine( n, sb, bns, i--, ds,ns );
if( n instanceof MultiNode && !(n instanceof CFGNode) ) {
for( Node use : n._outputs ) {
- printLine(use,sb,bns,bns.indexOf(use),ds,ns);
+ if( use instanceof ProjNode )
+ printLine(use,sb,bns,bns.indexOf(use),ds,ns);
}
}
}
diff --git a/src/main/java/com/seaofnodes/simple/print/JSViewer.java b/src/main/java/com/seaofnodes/simple/print/JSViewer.java
index a2b674c9..76c64c71 100644
--- a/src/main/java/com/seaofnodes/simple/print/JSViewer.java
+++ b/src/main/java/com/seaofnodes/simple/print/JSViewer.java
@@ -72,7 +72,7 @@ public class JSViewer implements AutoCloseable {
public static void show() { if( SERVER!=null && SHOW ) _show(); }
private static void _show() {
// Skip util we at least get the Parse object made
- if( CODE._phase == null )
+ if( CODE._phase==null || CODE._phase.ordinal() < CodeGen.Phase.Parse.ordinal() )
return;
boolean midParse = CODE._phase == CodeGen.Phase.Parse;
Stack<ScopeNode> xScopes = midParse ? CODE.P._xScopes : null;
diff --git a/src/main/java/com/seaofnodes/simple/type/Type.java b/src/main/java/com/seaofnodes/simple/type/Type.java
index 847235dd..6df4e7ab 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 )
@@ -217,7 +218,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 167a931c..bd64bd81 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 7e607838..284bf581 100644
--- a/src/main/java/com/seaofnodes/simple/type/TypeMemPtr.java
+++ b/src/main/java/com/seaofnodes/simple/type/TypeMemPtr.java
@@ -77,7 +77,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 _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 e981b5dc..aa6f2dfd 100644
--- a/src/main/java/com/seaofnodes/simple/type/TypeStruct.java
+++ b/src/main/java/com/seaofnodes/simple/type/TypeStruct.java
@@ -182,9 +182,12 @@ public class TypeStruct extends Type {
@Override
public SB print(SB sb) {
+ 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(); }
diff --git a/src/test/java/com/seaofnodes/simple/BrainFuckTest.java b/src/test/java/com/seaofnodes/simple/BrainFuckTest.java
index c23bb301..2733620e 100644
--- a/src/test/java/com/seaofnodes/simple/BrainFuckTest.java
+++ b/src/test/java/com/seaofnodes/simple/BrainFuckTest.java
@@ -1,71 +1,38 @@
package com.seaofnodes.simple;
import com.seaofnodes.simple.codegen.CodeGen;
-import java.nio.charset.StandardCharsets;
+import com.seaofnodes.simple.node.cpus.arm.arm;
+import com.seaofnodes.simple.node.cpus.riscv.riscv;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
-import static com.seaofnodes.simple.Main.PORTS;
public class BrainFuckTest {
- @Test
- public void testBrainfuck() {
- var program = "++++++++[>++++[>++>+++>+++>+<<<<-]>+>+>->>+[<]<-]>>.>---.+++++++..+++.>>.<-.<.+++.------.--------.>>+.>++.".getBytes(StandardCharsets.UTF_8);
- var encoded = new StringBuilder("u8[] !program = new u8[").append(program.length).append("];");
- for (int i = 0; i < program.length; i++) {
- int value = program[i] & 0xFF;
- encoded.append("program[").append(i).append("] = ").append(value).append(";");
- }
+ @Test public void testBrainFuck() throws IOException {
+ String brain_fuck = "Hello World!\n";
+ TestC.run("brain_fuck", brain_fuck, 40);
- String src = encoded + """
+ EvalRisc5 R5 = TestRisc5.build("brain_fuck", 0, 44, false);
+ int trap = R5.step(100000);
+ assertEquals(0,trap);
+ int ptr = (int)R5.regs[riscv.A0];
+ assertEquals(brain_fuck.length(),R5.ld4s(ptr));
+ for( int i=0; i<brain_fuck.length(); i++ )
+ assertEquals(brain_fuck.charAt(i), R5.ld1z(ptr+4+i));
-int d = 0;
-u8[] !output = new u8[0];
-u8[] !data = new u8[100];
+ EvalArm64 A5 = TestArm64.build("brain_fuck", 0, 28, false);
+ int trap_arm = A5.step(100000);
+ assertEquals(0,trap_arm);
+ assertEquals(0,trap);
+ int ptr_arm = (int)A5.regs[arm.X0];
+ assertEquals(brain_fuck.length(),A5.ld4s(ptr_arm));
-for( int pc = 0; pc < program#; pc++ ) {
- var command = program[pc];
- if (command == 62) {
- d++;
- } else if (command == 60) {
- d--;
- } else if (command == 43) {
- data[d]++;
- } else if (command == 45) {
- data[d]--;
- } else if (command == 46) {
- // Output a byte; increase the output array size
- var old = output;
- output = new u8[output# + 1];
- for( int i = 0; i < old#; i++ )
- output[i] = old[i];
- output[old#] = data[d]; // Add the extra byte on the end
- } else if (command == 44) {
- data[d] = 42;
- } else if (command == 91) {
- if (data[d] == 0) {
- for( int d = 1; d > 0; ) {
- command = program[++pc];
- if (command == 91) d++;
- if (command == 93) d--;
- }
- }
- } else if (command == 93) {
- if (data[d]) {
- for( int d = 1; d > 0; ) {
- command = program[--pc];
- if (command == 93) d++;
- if (command == 91) d--;
- }
- }
- }
-}
-return output;
-""";
- Chapter20Test.testCPU(src,"x86_64_v2", "SystemV",28,null);
- Chapter20Test.testCPU(src,"riscv" , "SystemV",18,null);
- Chapter20Test.testCPU(src,"arm" , "SystemV",12,null);
- //assertEquals("Hello World!\n", Eval2.eval(code, 0, 10000));
+ for( int i=0; i<brain_fuck.length(); i++ )
+ assertEquals(brain_fuck.charAt(i), A5.ld1z(ptr+4+i));
}
+
}
diff --git a/src/test/java/com/seaofnodes/simple/Chapter05Test.java b/src/test/java/com/seaofnodes/simple/Chapter05Test.java
index a584ad5a..d1dfd216 100644
--- a/src/test/java/com/seaofnodes/simple/Chapter05Test.java
+++ b/src/test/java/com/seaofnodes/simple/Chapter05Test.java
@@ -100,7 +100,7 @@ else
return a;
""");
code.parse().opto();
- assertEquals("return Phi(Region,Phi(Region,2,3),Phi(Region,4,5));", code.print());
+ assertEquals("return Phi(Region,3,5,2,4);", code.print());
}
@Test
diff --git a/src/test/java/com/seaofnodes/simple/Chapter06Test.java b/src/test/java/com/seaofnodes/simple/Chapter06Test.java
index 24d12921..b6bc2bf3 100644
--- a/src/test/java/com/seaofnodes/simple/Chapter06Test.java
+++ b/src/test/java/com/seaofnodes/simple/Chapter06Test.java
@@ -145,7 +145,7 @@ else
return a;
""");
code.parse().opto();
- assertEquals("return Phi(Region,3,Phi(Region,4,5));", code.print());
+ assertEquals("return Phi(Region,3,5,4);", code.print());
}
@Test
@@ -258,7 +258,7 @@ if( arg ) {
return a+b+c;
""");
code.parse().opto();
- assertEquals("return (Phi(Region,Phi(Region,2,3),0)+Phi(Region,3,1));", code.print());
+ assertEquals("return Phi(Region,6,1,5);", code.print());
}
diff --git a/src/test/java/com/seaofnodes/simple/Chapter08Test.java b/src/test/java/com/seaofnodes/simple/Chapter08Test.java
index 7e90d484..09669cc7 100644
--- a/src/test/java/com/seaofnodes/simple/Chapter08Test.java
+++ b/src/test/java/com/seaofnodes/simple/Chapter08Test.java
@@ -23,7 +23,7 @@ while(arg < 10) {
return arg;
""");
code.parse().opto();
- assertEquals("return Phi(Region,Phi(Region,Phi(Loop,arg,(Phi_arg+1)),Add),Add);", code.print());
+ assertEquals("return Phi(Region,(Phi(Loop,arg,Add)+1),Add,Phi_arg);", code.print());
assertTrue(code.ctrl() instanceof RegionNode);
Assert.assertEquals( "5", Eval2.eval(code, 1));
Assert.assertEquals("10", Eval2.eval(code, 6));
@@ -46,7 +46,7 @@ while(arg < 10) {
return a;
""");
code.parse().opto();
- assertEquals("return Phi(Loop,1,Phi(Region,Phi_a,(Phi_a+1)));", code.print());
+ assertEquals("return Phi(Loop,1,Phi(Region,Phi_a,(Phi_a+1),Phi_a));", code.print());
assertTrue(code.ctrl() instanceof CProjNode);
}
diff --git a/src/test/java/com/seaofnodes/simple/Chapter11Test.java b/src/test/java/com/seaofnodes/simple/Chapter11Test.java
index c2b9a86d..f6539ac4 100644
--- a/src/test/java/com/seaofnodes/simple/Chapter11Test.java
+++ b/src/test/java/com/seaofnodes/simple/Chapter11Test.java
@@ -289,7 +289,7 @@ else {
}
""");
code.parse().opto().typeCheck().GCM();
- assertEquals("return Phi(Region,9,Top,Top,Top,Top);", code.print());
+ assertEquals("return 9;", code.print());
}
@@ -321,7 +321,7 @@ while( prime <= arg ) {
return primeCount;
""");
code.parse().opto();
- assertEquals("return Phi(Region,0,Phi(Loop,1,Phi(Region,Phi_primeCount,Phi(Region,(Phi_primeCount+1),Phi_primeCount))));", code.print());
+ assertEquals("return Phi(Region,0,Phi(Loop,1,Phi(Region,Phi_primeCount,Phi_primeCount,(Phi_primeCount+1))));", code.print());
assertEquals("0", Eval2.eval(code, 1)); // No primes 1 or below
assertEquals("1", Eval2.eval(code, 2)); // 2
assertEquals("2", Eval2.eval(code, 3)); // 2, 3
diff --git a/src/test/java/com/seaofnodes/simple/Chapter13Test.java b/src/test/java/com/seaofnodes/simple/Chapter13Test.java
index 977595cb..6bf45a22 100644
--- a/src/test/java/com/seaofnodes/simple/Chapter13Test.java
+++ b/src/test/java/com/seaofnodes/simple/Chapter13Test.java
@@ -33,7 +33,7 @@ while( arg ) {
return head.next.i;
""");
try { code.parse().opto().typeCheck(); fail(); }
- catch( Exception e ) { assertEquals("Might be null accessing 'i'",e.getMessage()); }
+ catch( Exception e ) { assertEquals("Might be null accessing 'next'",e.getMessage()); }
}
@Test
diff --git a/src/test/java/com/seaofnodes/simple/Chapter14Test.java b/src/test/java/com/seaofnodes/simple/Chapter14Test.java
index b83516e3..7af35b56 100644
--- a/src/test/java/com/seaofnodes/simple/Chapter14Test.java
+++ b/src/test/java/com/seaofnodes/simple/Chapter14Test.java
@@ -174,7 +174,7 @@ while(v1) break;
return v1;
""");
code.parse().opto();
- assertEquals("return (Phi(Loop,((1<<arg)&1),0)&Phi(Loop,Shl,4294967295));", code.print());
+ assertEquals("return Phi(Loop,(((1<<arg)&1)&Shl),0);", code.print());
assertEquals("1", Eval2.eval(code, 0));
}
diff --git a/src/test/java/com/seaofnodes/simple/Chapter15Test.java b/src/test/java/com/seaofnodes/simple/Chapter15Test.java
index 7dd0710a..8b138b46 100644
--- a/src/test/java/com/seaofnodes/simple/Chapter15Test.java
+++ b/src/test/java/com/seaofnodes/simple/Chapter15Test.java
@@ -1,6 +1,9 @@
package com.seaofnodes.simple;
import com.seaofnodes.simple.codegen.CodeGen;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
import org.junit.Test;
import static org.junit.Assert.*;
@@ -127,7 +130,7 @@ else {
return rez;
""");
code.parse().opto();
- assertEquals("return Phi(Region,1.2,Phi(Region,2.3,.y));", code.print());
+ assertEquals("return Phi(Region,1.2,.y,2.3);", code.print());
assertEquals("3.14", Eval2.eval(code, 0));
assertEquals("1.2", Eval2.eval(code, 1));
}
@@ -152,7 +155,7 @@ if( iss[arg] )
rez = iss[arg][2].y;
return rez;""");
code.parse().opto().typeCheck();
- assertEquals("return Phi(Region,Phi(Region,.y,1.2),1.2);", code.print());
+ assertEquals("return Phi(Region,1.2,1.2,.y);", code.print());
assertEquals("3.14", Eval2.eval(code, 0));
try { Eval2.eval(code, 10); fail(); } // Fails AIOOBE in Eval2, no range checks
catch( Exception e ) { assertEquals("Index 10 out of bounds for length 2",e.getMessage()); }
@@ -210,44 +213,12 @@ return ary[1] * 1000 + ary[3]; // 1 * 1000 + 6
}
@Test
- public void sieveOEratosthenes() {
- CodeGen code = new CodeGen(
-"""
-int[] !ary = new int[arg], !primes = new int[arg];
-int nprimes = 0, p=2;
-// Find primes while p^2 < arg
-while( p*p < arg ) {
- // skip marked non-primes
- while( ary[p]==1 ) p = p + 1;
- // p is now a prime
- primes[nprimes] = p; nprimes = nprimes + 1;
- // Mark out the rest non-primes
- int i = p + p;
- while( i < ary# ) {
- ary[i] = 1;
- i = i + p;
- }
- p = p + 1;
-}
-// Now just collect the remaining primes, no more marking
-while( p < arg ) {
- if( ary[p] == 0 ) {
- primes[nprimes] = p; nprimes = nprimes + 1;
- }
- p = p + 1;
-}
-// Copy/shrink the result array
-int[] !rez = new int[nprimes];
-int j = 0;
-while( j < nprimes ) {
- rez[j] = primes[j];
- j = j + 1;
-}
-return rez;
-""");
+ public void sieveOEratosthenes() throws IOException {
+ String src = Files.readString(Path.of("src/test/java/com/seaofnodes/simple/progs/sieve.smp"));
+ CodeGen code = new CodeGen(src+"return sieve(arg);");
code.parse().opto();
- assertEquals("return [int];", code.print());
- assertEquals("int[ 2,3,5,7,11,13,17,19]",Eval2.eval(code, 20));
+ assertEquals("return [u32];", code.print());
+ assertEquals("u32[ 2,3,5,7,11,13,17,19]",Eval2.eval(code, 20));
}
@Test
diff --git a/src/test/java/com/seaofnodes/simple/Chapter17Test.java b/src/test/java/com/seaofnodes/simple/Chapter17Test.java
index 6e43dbc8..78515083 100644
--- a/src/test/java/com/seaofnodes/simple/Chapter17Test.java
+++ b/src/test/java/com/seaofnodes/simple/Chapter17Test.java
@@ -368,7 +368,7 @@ var b = arg ? new Bar { next = (arg==2) ? new Bar{x=2;}; x=1; };
return b ? b.next ? b.next.x : b.x; // parses "b ? (b.next ? b.next.x : b.x) : 0"
""");
code.parse().opto();
- assertEquals("return Phi(Region,Phi(Region,.x,.x),0);", code.print());
+ assertEquals("return Phi(Region,.x,0);", code.print());
assertEquals("0", Eval2.eval(code, 0));
assertEquals("1", Eval2.eval(code, 1));
assertEquals("2", Eval2.eval(code, 2));
diff --git a/src/test/java/com/seaofnodes/simple/Chapter18Test.java b/src/test/java/com/seaofnodes/simple/Chapter18Test.java
index d0623d12..c0e28412 100644
--- a/src/test/java/com/seaofnodes/simple/Chapter18Test.java
+++ b/src/test/java/com/seaofnodes/simple/Chapter18Test.java
@@ -1,5 +1,6 @@
package com.seaofnodes.simple;
+import com.seaofnodes.simple.codegen.CodeGen.Phase;
import com.seaofnodes.simple.codegen.CodeGen;
import org.junit.Ignore;
import org.junit.Test;
@@ -14,7 +15,7 @@ public class Chapter18Test {
"""
return 0;
""");
- code.parse().opto().typeCheck().GCM().localSched();
+ code.driver(Phase.LocalSched);
assertEquals("return 0;", code._stop.toString());
assertEquals("0", Eval2.eval(code, 2));
}
@@ -61,11 +62,10 @@ return x2;
{int -> int}? sq = { int x ->
x*x;
};
-return sq;
""");
code.parse().opto();
- assertEquals("Stop[ return { sq}; return (Parm_x(sq,int)*x); ]", code._stop.toString());
- assertEquals("{ int -> int #1}", Eval2.eval(code, 3));
+ assertEquals("return (Parm_x(sq,int)*x);", code._stop.toString());
+ //assertEquals("{ int -> int #1}", Eval2.eval(code, 3));
}
@Test
@@ -77,7 +77,7 @@ var sq = { int x ->
};
return sq(arg)+sq(3);
""");
- code.parse().opto().typeCheck().GCM().localSched();
+ code.driver(Phase.LocalSched);
assertEquals("Stop[ return (#2+#2); return (Parm_x(sq,int)*x); ]", code._stop.toString());
assertEquals("13", Eval2.eval(code, 2));
}
@@ -175,7 +175,7 @@ for(;;) {
i2i = id(x);
}
""");
- code.parse().opto().typeCheck().GCM().localSched();
+ code.driver(Phase.LocalSched);
assertEquals("Stop[ return #2; return Parm_i(x,int); ]", code._stop.toString());
assertEquals("3", Eval2.eval(code, 0));
}
@@ -191,9 +191,9 @@ for(;;) {
arg = x(3);
}
""");
- code.parse().opto().typeCheck().GCM().localSched();
+ code.driver(Phase.LocalSched);
assertEquals("return Top;", code._stop.toString());
- assertEquals(null, Eval2.eval(code, 0));
+ assertEquals("null", Eval2.eval(code, 0));
}
@@ -216,7 +216,7 @@ ps[0] = new Person;
ps[1] = new Person;
fcn(ps,1);
""");
- code.parse().opto().typeCheck().GCM().localSched();
+ code.driver(Phase.LocalSched);
assertEquals("return 0;", code._stop.toString());
assertEquals("0", Eval2.eval(code, 0));
}
@@ -346,7 +346,7 @@ if (arg) i2i = null;
if (i2i) return i2i(arg);
return f2f(o)(1);
""");
- code.parse().opto().typeCheck().GCM().localSched();
+ code.driver(Phase.LocalSched);
assertEquals("Stop[ return Phi(Region,#2,#2); return Parm_i(o,int); ]", code._stop.toString());
assertEquals("1", Eval2.eval(code, 2));
}
@@ -362,7 +362,7 @@ Person !p = new Person;
p.coffee_count += 1;
return p.coffee_count;
""");
- code.parse().opto().typeCheck().GCM().localSched();
+ code.driver(Phase.LocalSched);
assertEquals("return 1;", code._stop.toString());
assertEquals("1", Eval2.eval(code, 2));
}
diff --git a/src/test/java/com/seaofnodes/simple/Chapter19Test.java b/src/test/java/com/seaofnodes/simple/Chapter19Test.java
index bb7e8716..bce75c81 100644
--- a/src/test/java/com/seaofnodes/simple/Chapter19Test.java
+++ b/src/test/java/com/seaofnodes/simple/Chapter19Test.java
@@ -1,11 +1,12 @@
package com.seaofnodes.simple;
+import com.seaofnodes.simple.codegen.CodeGen.Phase;
import com.seaofnodes.simple.codegen.CodeGen;
-import org.junit.Ignore;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
import org.junit.Test;
-import static com.seaofnodes.simple.Main.PORTS;
import static org.junit.Assert.*;
-import static org.junit.Assert.fail;
public class Chapter19Test {
@@ -21,143 +22,108 @@ return 0;
@Test
- public void testString() {
- CodeGen code = new CodeGen(
-"""
-struct String {
- u8[] cs;
- int _hashCode;
-};
-
-val equals = { String self, String s ->
- if( self == s ) return true;
- if( self.cs# != s.cs# ) return false;
- for( int i=0; i< self.cs#; i++ )
- if( self.cs[i] != s.cs[i] )
- return false;
- return true;
-};
-
-val hashCode = { String self ->
- self._hashCode
- ? self._hashCode
- : (self._hashCode = _hashCodeString(self));
-};
-
-val _hashCodeString = { String self ->
- int hash=0;
- if( self.cs ) {
- for( int i=0; i< self.cs#; i++ )
- hash = hash*31 + self.cs[i];
- }
- if( !hash ) hash = 123456789;
- return hash;
-};
-
-String !s = new String { cs = new u8[17]; };
-s.cs[0] = 67; // C
-s.cs[1] = 108; // l
-hashCode(s)+hashCode(s);""");
- code.parse().opto().typeCheck().GCM().localSched();
- assertEquals("Stop[ return (#2+#2); return Phi(Region,1,0,0,1); return Phi(Region,._hashCode,Phi(Region,123456789,Phi(Loop,0,(.[]+((Phi_hash<<5)-Phi_hash))))); ]", code._stop.toString());
- assertEquals("-4898613127354160978", Eval2.eval(code, 2));
+ public void testString() throws IOException {
+ String src = Files.readString(Path.of("src/test/java/com/seaofnodes/simple/progs/stringHash.smp"));
+ CodeGen code = new CodeGen(src).parse().opto().typeCheck().GCM().localSched();
+ assertEquals("Stop[ return Phi(Region,._hashCode,Phi(Region,123456789,Phi(Loop,0,(.[]+((Phi_hash<<5)-Phi_hash))))); return Phi(Region,1,0,0,1); ]", code._stop.toString());
+ //assertEquals("-4898613127354160978", Eval2.eval(code, 2));
}
@Test
public void testBasic0() {
- CodeGen code = new CodeGen("return 0;").parse().opto().typeCheck().instSelect(PORTS,"x86_64_v2", "SystemV").GCM().localSched();
+ CodeGen code = new CodeGen("return 0;").driver(Phase.LocalSched,"x86_64_v2", "SystemV");
assertEquals("return 0;", code._stop.toString());
}
@Test
public void testBasic1() {
- CodeGen code = new CodeGen("return arg+1;").parse().opto().typeCheck().instSelect(PORTS,"x86_64_v2", "SystemV").GCM().localSched();
+ CodeGen code = new CodeGen("return arg+1;").driver(Phase.LocalSched,"x86_64_v2", "SystemV");
assertEquals("return (inc,arg);", code._stop.toString());
}
@Test
public void testBasic2() {
- CodeGen code = new CodeGen("return -17;").parse().opto().typeCheck().instSelect(PORTS,"x86_64_v2", "SystemV").GCM().localSched();
+ CodeGen code = new CodeGen("return -17;").driver(Phase.LocalSched,"x86_64_v2", "SystemV");
assertEquals("return -17;", code._stop.toString());
}
@Test
public void testBasic3() {
- CodeGen code = new CodeGen("return arg==1;").parse().opto().typeCheck().instSelect(PORTS,"x86_64_v2", "SystemV").GCM().localSched();
+ CodeGen code = new CodeGen("return arg==1;").driver(Phase.LocalSched,"x86_64_v2", "SystemV");
assertEquals("return (set==,(cmp,arg));", code._stop.toString());
}
@Test
public void testBasic4() {
- CodeGen code = new CodeGen("return arg<<1;").parse().opto().typeCheck().instSelect(PORTS,"x86_64_v2", "SystemV").GCM().localSched();
+ CodeGen code = new CodeGen("return arg<<1;").driver(Phase.LocalSched,"x86_64_v2", "SystemV");
assertEquals("return (shli,arg);", code._stop.toString());
}
@Test
public void testBasic5() {
- CodeGen code = new CodeGen("return arg >> 1;").parse().opto().typeCheck().instSelect(PORTS,"x86_64_v2", "SystemV").GCM().localSched();
+ CodeGen code = new CodeGen("return arg >> 1;").driver(Phase.LocalSched,"x86_64_v2", "SystemV");
assertEquals("return (sari,arg);", code._stop.toString());
}
@Test
public void testBasic6() {
- CodeGen code = new CodeGen("return arg >>> 1;").parse().opto().typeCheck().instSelect(PORTS,"x86_64_v2", "SystemV").GCM().localSched();
+ CodeGen code = new CodeGen("return arg >>> 1;").driver(Phase.LocalSched,"x86_64_v2", "SystemV");
assertEquals("return (shri,arg);", code._stop.toString());
}
@Test
public void testBasic7() {
- CodeGen code = new CodeGen("return arg / 2;").parse().opto().typeCheck().instSelect(PORTS,"x86_64_v2", "SystemV").GCM().localSched();
+ CodeGen code = new CodeGen("return arg / 2;").driver(Phase.LocalSched,"x86_64_v2", "SystemV");
assertEquals("return (div,arg,2);", code._stop.toString());
}
@Test
public void testBasic8() {
- CodeGen code = new CodeGen("return arg * 6;").parse().opto().typeCheck().instSelect(PORTS,"x86_64_v2", "SystemV").GCM().localSched();
+ CodeGen code = new CodeGen("return arg * 6;").driver(Phase.LocalSched,"x86_64_v2", "SystemV");
assertEquals("return (muli,arg);", code._stop.toString());
}
@Test
public void testBasic9() {
- CodeGen code = new CodeGen("return arg & 2;").parse().opto().typeCheck().instSelect(PORTS,"x86_64_v2", "SystemV").GCM().localSched();
+ CodeGen code = new CodeGen("return arg & 2;").driver(Phase.LocalSched,"x86_64_v2", "SystemV");
assertEquals("return (andi,arg);", code._stop.toString());
}
@Test
public void testBasic10() {
- CodeGen code = new CodeGen("return arg | 2;").parse().opto().typeCheck().instSelect(PORTS,"x86_64_v2", "SystemV").GCM().localSched();
+ CodeGen code = new CodeGen("return arg | 2;").driver(Phase.LocalSched,"x86_64_v2", "SystemV");
assertEquals("return (ori,arg);", code._stop.toString());
}
@Test
public void testBasic11() {
- CodeGen code = new CodeGen("return arg ^ 2;").parse().opto().typeCheck().instSelect(PORTS,"x86_64_v2", "SystemV").GCM().localSched();
+ CodeGen code = new CodeGen("return arg ^ 2;").driver(Phase.LocalSched,"x86_64_v2", "SystemV");
assertEquals("return (xori,arg);", code._stop.toString());
}
@Test
public void testBasic12() {
- CodeGen code = new CodeGen("return arg + 2.0;").parse().opto().typeCheck().instSelect(PORTS,"x86_64_v2", "SystemV").GCM().localSched();
- assertEquals("return (addf,(i2f8,arg),2.0f);", code._stop.toString());
+ CodeGen code = new CodeGen("return arg + 2.0;").driver(Phase.LocalSched,"x86_64_v2", "SystemV");
+ assertEquals("return (addf,(cvtf,arg),2.0f);", code._stop.toString());
}
@Test
public void testBasic13() {
- CodeGen code = new CodeGen("return arg - 2.0;").parse().opto().typeCheck().instSelect(PORTS,"x86_64_v2", "SystemV").GCM().localSched();
- assertEquals("return (subf,(i2f8,arg),2.0f);", code._stop.toString());
+ CodeGen code = new CodeGen("return arg - 2.0;").driver(Phase.LocalSched,"x86_64_v2", "SystemV");
+ assertEquals("return (subf,(cvtf,arg),2.0f);", code._stop.toString());
}
@Test
public void testBasic14() {
- CodeGen code = new CodeGen("return arg * 2.0;").parse().opto().typeCheck().instSelect(PORTS,"x86_64_v2", "SystemV").GCM().localSched();
- assertEquals("return (mulf,(i2f8,arg),2.0f);", code._stop.toString());
+ CodeGen code = new CodeGen("return arg * 2.0;").driver(Phase.LocalSched,"x86_64_v2", "SystemV");
+ assertEquals("return (mulf,(cvtf,arg),2.0f);", code._stop.toString());
}
@Test
public void testBasic15() {
- CodeGen code = new CodeGen("return arg / 2.0;").parse().opto().typeCheck().instSelect(PORTS,"x86_64_v2", "SystemV").GCM().localSched();
- assertEquals("return (mulf,(i2f8,arg),0.5f);", code._stop.toString());
+ CodeGen code = new CodeGen("return arg / 2.0;").driver(Phase.LocalSched,"x86_64_v2", "SystemV");
+ assertEquals("return (mulf,(cvtf,arg),0.5f);", code._stop.toString());
}
@Test
@@ -166,7 +132,7 @@ hashCode(s)+hashCode(s);""");
"""
int arg1 = arg + 1;
return arg1 / arg;""");
- code.parse().opto().typeCheck().instSelect(PORTS,"x86_64_v2", "SystemV").GCM().localSched();
+ code.driver(Phase.LocalSched,"x86_64_v2", "SystemV");
assertEquals("return (div,(inc,arg),arg);", code._stop.toString());
}
@@ -177,18 +143,18 @@ return arg1 / arg;""");
int arg1 = arg + 1;
return arg1 * arg;
""");
- code.parse().opto().typeCheck().instSelect(PORTS,"x86_64_v2", "SystemV").GCM().localSched();
+ code.driver(Phase.LocalSched,"x86_64_v2", "SystemV");
assertEquals("return (mul,(inc,arg),arg);", code._stop.toString());
}
@Test
public void testToFloat() {
CodeGen code = new CodeGen("""
- int a = arg;
- return a + 2.0;
- """
- ).parse().opto().typeCheck().instSelect(PORTS,"x86_64_v2", "SystemV").GCM().localSched();
- assertEquals("return (addf,(i2f8,arg),2.0f);", code._stop.toString());
+int a = arg;
+return a + 2.0;
+"""
+ ).driver(Phase.LocalSched,"x86_64_v2", "SystemV");
+ assertEquals("return (addf,(cvtf,arg),2.0f);", code._stop.toString());
}
@Test
@@ -202,7 +168,7 @@ else {
a = arg-3;
}
return a;""");
- code.parse().opto().typeCheck().instSelect(PORTS,"x86_64_v2", "SystemV").GCM().localSched();
+ code.driver(Phase.LocalSched,"x86_64_v2", "SystemV");
assertEquals("return Phi(Region,(addi,arg),(addi,arg));", code.print());
}
@@ -217,7 +183,7 @@ if( arg==1 )
else
a=b+1;
return a+b;""");
- code.parse().opto().typeCheck().instSelect(PORTS,"x86_64_v2", "SystemV").GCM().localSched();
+ code.driver(Phase.LocalSched,"x86_64_v2", "SystemV");
assertEquals("return (add,(add,Phi(Region,(shli,arg),arg),arg),Phi(Region,4,5));", code.print());
}
@@ -229,7 +195,7 @@ int sum=0;
for( int i=0; i<arg; i++ )
sum += i;
return sum;""");
- code.parse().opto().typeCheck().instSelect(PORTS,"x86_64_v2", "SystemV").GCM().localSched();
+ code.driver(Phase.LocalSched,"x86_64_v2", "SystemV");
assertEquals("return Phi(Loop,0,(add,Phi_sum,Phi(Loop,0,(inc,Phi_i))));", code.print());
}
@@ -239,42 +205,42 @@ return sum;""");
"""
struct S { int a; S? c; };
return new S;""");
- code.parse().opto().typeCheck().instSelect(PORTS,"x86_64_v2", "SystemV").GCM().localSched();
+ code.driver(Phase.LocalSched,"x86_64_v2", "SystemV");
assertEquals("return S;", code.print());
}
@Test
public void testLea1() {
CodeGen code = new CodeGen("int x = arg/3; return arg+x+7;");
- code.parse().opto().typeCheck().instSelect(PORTS,"x86_64_v2", "SystemV").GCM().localSched();
+ code.driver(Phase.LocalSched,"x86_64_v2", "SystemV");
assertEquals("return (lea,arg,(div,arg,3));", code.print());
}
@Test
public void testLea2() {
CodeGen code = new CodeGen("int x = arg/3; return arg+x*4+7;");
- code.parse().opto().typeCheck().instSelect(PORTS,"x86_64_v2", "SystemV").GCM().localSched();
+ code.driver(Phase.LocalSched,"x86_64_v2", "SystemV");
assertEquals("return (lea,arg,(div,arg,3));", code.print());
}
@Test
public void testLea3() {
CodeGen code = new CodeGen("int x = arg/3; return x*4+arg;");
- code.parse().opto().typeCheck().instSelect(PORTS,"x86_64_v2", "SystemV").GCM().localSched();
+ code.driver(Phase.LocalSched,"x86_64_v2", "SystemV");
assertEquals("return (lea,arg,(div,arg,3));", code.print());
}
@Test
public void testAlloc2() {
CodeGen code = new CodeGen("int[] !xs = new int[3]; xs[arg]=1; return xs[arg&1];");
- code.parse().opto().typeCheck().instSelect(PORTS,"x86_64_v2", "SystemV").GCM().localSched();
+ code.driver(Phase.LocalSched,"x86_64_v2", "SystemV");
assertEquals("return .[];", code.print());
}
@Test
public void testAlloc3() {
CodeGen code = new CodeGen("int[] !xs = new int[3]; xs[arg]=1; return xs[arg&1]+3;");
- code.parse().opto().typeCheck().instSelect(PORTS,"x86_64_v2", "SystemV").GCM().localSched();
+ code.driver(Phase.LocalSched,"x86_64_v2", "SystemV");
assertEquals("return .[];", code.print());
}
@@ -291,7 +257,7 @@ for( int i=0; i<ary#-1; i++ )
ary[i+1] += ary[i];
return ary[1] * 1000 + ary[3]; // 1 * 1000 + 6
""");
- code.parse().opto().typeCheck().instSelect(PORTS,"x86_64_v2", "SystemV").GCM().localSched();
+ code.driver(Phase.LocalSched,"x86_64_v2", "SystemV");
assertEquals("return .[];", code.print());
}
@@ -306,7 +272,7 @@ for( int i=0; i<A#; i++ )
for( int i=0; i<A#; i++ )
B[i] += A[i];
""");
- code.parse().opto().typeCheck().instSelect(PORTS,"x86_64_v2", "SystemV").GCM().localSched();
+ code.driver(Phase.LocalSched,"x86_64_v2", "SystemV");
assertEquals("return 0;", code.print());
}
@@ -319,60 +285,25 @@ for( int i=0; i<A#; i++ )
A[i]++;
return A[1];
""");
- code.parse().opto().typeCheck().instSelect(PORTS,"x86_64_v2", "SystemV").GCM().localSched();
+ code.driver(Phase.LocalSched,"x86_64_v2", "SystemV");
assertEquals("return .[];", code.print());
}
@Test
- public void testNewton() {
- CodeGen code = new CodeGen(
-"""
-// Newtons approximation to the square root
-val sqrt = { flt x ->
- flt guess = x;
- while( 1 ) {
- flt next = (x/guess + guess)/2;
- if( next == guess ) return guess;
- guess = next;
- }
-};
-flt farg = arg;
-return sqrt(farg);
-""");
- code.parse().opto().typeCheck().instSelect(PORTS,"x86_64_v2", "SystemV").GCM().localSched();
- assertEquals("return Phi(Loop,(i2f8,arg),(mulf,(addf,(divf,i2f8,Phi_guess),Phi_guess),0.5f));", code.print());
+ public void testNewton() throws IOException {
+ String src = Files.readString(Path.of("src/test/java/com/seaofnodes/simple/progs/newtonFloat.smp"))
+ + "flt farg = arg; return test_sqrt(farg);";
+ CodeGen code = new CodeGen(src).driver(Phase.LocalSched,"x86_64_v2", "SystemV");
+ assertEquals("return Phi(Loop,(cvtf,arg),(mulf,(addf,(divf,cvtf,Phi_guess),Phi_guess),0.5f));", code.print());
};
@Test
- public void sieveOfEratosthenes() {
- CodeGen code = new CodeGen(
-"""
-var ary = new bool[arg], primes = new int[arg];
-var nprimes=0, p=0;
-// Find primes while p^2 < arg
-for( p=2; p*p < arg; p++ ) {
- // skip marked non-primes
- while( ary[p] ) p++;
- // p is now a prime
- primes[nprimes++] = p;
- // Mark out the rest non-primes
- for( int i = p + p; i < ary#; i += p )
- ary[i] = true;
-}
-// Now just collect the remaining primes, no more marking
-for( ; p < arg; p++ )
- if( !ary[p] )
- primes[nprimes++] = p;
-// Copy/shrink the result array
-var !rez = new int[nprimes];
-for( int j=0; j<nprimes; j++ )
- rez[j] = primes[j];
-return rez;
-""");
- code.parse().opto().typeCheck().instSelect(PORTS,"x86_64_v2", "SystemV").GCM().localSched();
- assertEquals("return [int];", code.print());
- //assertEquals("int[ 2,3,5,7,11,13,17,19]",Eval2.eval(code, 20));
+ public void sieveOfEratosthenes() throws IOException {
+ String src = Files.readString(Path.of("src/test/java/com/seaofnodes/simple/progs/sieve.smp"));
+ CodeGen code = new CodeGen(src).driver(Phase.LocalSched,"x86_64_v2", "SystemV");
+ assertEquals("return [u32];", code.print());
+ //assertEquals("u32[ 2,3,5,7,11,13,17,19]",Eval2.eval(code, 20));
}
@@ -383,7 +314,7 @@ return rez;
val fcn = arg ? { int x -> x*x; } : { int x -> x+x; };
return fcn(2)*10 + fcn(3);
""");
- code.parse().opto().typeCheck().instSelect(PORTS,"x86_64_v2", "SystemV").GCM().localSched();
+ code.driver(Phase.LocalSched,"x86_64_v2", "SystemV");
assertEquals("Stop[ return (add,#2,(muli,#2)); return (mul,Parm_x($fun1,int),x); return (shli,Parm_x($fun2,int)); ]", code.print());
}
@@ -394,7 +325,7 @@ return fcn(2)*10 + fcn(3);
val sq = { int x -> x*x; };
return sq(arg) + sq(3);
""");
- code.parse().opto().typeCheck().instSelect(PORTS,"x86_64_v2", "SystemV").GCM().localSched();
+ code.driver(Phase.LocalSched,"x86_64_v2", "SystemV");
assertEquals("Stop[ return (add,#2,#2); return (mul,Parm_x(sq,int),x); ]", code.print());
}
}
diff --git a/src/test/java/com/seaofnodes/simple/Chapter20Test.java b/src/test/java/com/seaofnodes/simple/Chapter20Test.java
index 008f5e0c..5b33d1e7 100644
--- a/src/test/java/com/seaofnodes/simple/Chapter20Test.java
+++ b/src/test/java/com/seaofnodes/simple/Chapter20Test.java
@@ -2,10 +2,12 @@ package com.seaofnodes.simple;
import com.seaofnodes.simple.codegen.CodeGen;
import com.seaofnodes.simple.print.ASMPrinter;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
import org.junit.Assert;
import org.junit.Ignore;
import org.junit.Test;
-import static com.seaofnodes.simple.Main.PORTS;
import static org.junit.Assert.*;
public class Chapter20Test {
@@ -20,10 +22,11 @@ public class Chapter20Test {
static void testCPU( String src, String cpu, String os, int spills, String stop ) {
CodeGen code = new CodeGen(src);
- code.parse().opto().typeCheck().instSelect(PORTS,cpu,os).GCM().localSched().regAlloc().encode();
+ code.driver(CodeGen.Phase.RegAlloc,cpu,os);
int delta = spills>>3;
if( delta==0 ) delta = 1;
- assertEquals("Expect spills:",spills,code._regAlloc._spillScaled,delta);
+ if( spills != -1 )
+ assertEquals("Expect spills:",spills,code._regAlloc._spillScaled,delta);
if( stop != null )
assertEquals(stop, code._stop.toString());
}
@@ -35,7 +38,9 @@ public class Chapter20Test {
}
@Test public void testAlloc0() {
- testAllCPUs("return new u8[arg];", 1, "return [u8];");
+ testCPU("return new u8[arg];","x86_64_v2", "SystemV",3,"return [u8];");
+ testCPU("return new u8[arg];","riscv" , "SystemV",5,"return [u8];");
+ testCPU("return new u8[arg];","arm" , "SystemV",5,"return [u8];");
}
@Test public void testBasic1() {
@@ -60,103 +65,53 @@ val sqrt = { int x ->
};
return sqrt(arg) + sqrt(arg+2);
""";
- testCPU(src,"x86_64_v2", "SystemV",26,null);
+ testCPU(src,"x86_64_v2", "Win64" ,23,null);
testCPU(src,"riscv" , "SystemV",19,null);
- testCPU(src,"arm" , "SystemV",26,null);
+ testCPU(src,"arm" , "SystemV",19,null);
}
@Test
- public void testNewtonFloat() {
- String src =
-"""
-// Newtons approximation to the square root
-val sqrt = { flt x ->
- flt guess = x;
- while( 1 ) {
- flt next = (x/guess + guess)/2;
- if( next == guess ) return guess;
- guess = next;
- }
-};
-flt farg = arg;
-return sqrt(farg) + sqrt(farg+2.0);
-""";
- testCPU(src,"x86_64_v2", "SystemV",25,null);
- testCPU(src,"riscv" , "SystemV",21,null);
+ public void testNewtonFloat() throws IOException {
+ String src = Files.readString(Path.of("src/test/java/com/seaofnodes/simple/progs/newtonFloat.smp"))
+ + "flt farg = arg; return test_sqrt(farg) + test_sqrt(farg+2.0);";
+ testCPU(src,"x86_64_v2", "SystemV",39,null);
+ testCPU(src,"riscv" , "SystemV",17,null);
testCPU(src,"arm" , "SystemV",18,null);
}
@Test
public void testAlloc2() {
String src = "int[] !xs = new int[3]; xs[arg]=1; return xs[arg&1];";
- testAllCPUs(src,1,"return .[];");
+ testCPU(src,"x86_64_v2", "SystemV",-1,"return .[];");
+ testCPU(src,"riscv" , "SystemV", 6,"return .[];");
+ testCPU(src,"arm" , "SystemV", 6,"return .[];");
}
- @Test
- public void testArray1() {
- String src =
-"""
-int[] !ary = new int[arg];
-// Fill [0,1,2,3,4,...]
-for( int i=0; i<ary#; i++ )
- ary[i] = i;
-// Fill [0,1,3,6,10,...]
-for( int i=0; i<ary#-1; i++ )
- ary[i+1] += ary[i];
-return ary[1] * 1000 + ary[3]; // 1 * 1000 + 6
-""";
- testCPU(src,"x86_64_v2", "SystemV",3,"return .[];");
- testCPU(src,"riscv" , "SystemV",1,"return (add,.[],(mul,.[],1000));");
- testCPU(src,"arm" , "SystemV",1,"return (add,.[],(muli,.[]));");
- }
- @Test
- public void testString() {
- String src = """
-struct String {
- u8[] cs;
- int _hashCode;
-};
-
-val equals = { String self, String s ->
- if( self == s ) return true;
- if( self.cs# != s.cs# ) return false;
- for( int i=0; i< self.cs#; i++ )
- if( self.cs[i] != s.cs[i] )
- return false;
- return true;
-};
-val hashCode = { String self ->
- self._hashCode
- ? self._hashCode
- : (self._hashCode = _hashCodeString(self));
-};
-
-val _hashCodeString = { String self ->
- int hash=0;
- if( self.cs ) {
- for( int i=0; i< self.cs#; i++ )
- hash = hash*31 + self.cs[i];
+ @Test
+ public void testArray1() throws IOException {
+ String src = Files.readString(Path.of("src/test/java/com/seaofnodes/simple/progs/array1.smp"));
+ testCPU(src,"x86_64_v2", "SystemV",-1,"return .[];");
+ testCPU(src,"riscv" , "SystemV", 8,"return (add,.[],(mul,.[],1000));");
+ testCPU(src,"arm" , "SystemV", 5,"return (add,.[],(mul,.[],1000));");
}
- if( !hash ) hash = 123456789;
- return hash;
-};
-String !s = new String { cs = new u8[17]; };
-s.cs[0] = 67; // C
-s.cs[1] = 108; // l
-hashCode(s);
-""";
- testCPU(src,"x86_64_v2", "SystemV",18,null);
- testCPU(src,"riscv" , "SystemV", 3,null);
- testCPU(src,"arm" , "SystemV", 3,null);
+
+ @Test
+ public void testString() throws IOException {
+ String src = Files.readString(Path.of("src/test/java/com/seaofnodes/simple/progs/stringHash.smp"));
+ testCPU(src,"x86_64_v2", "SystemV", 9,null);
+ testCPU(src,"riscv" , "SystemV", 5,null);
+ testCPU(src,"arm" , "SystemV", 6,null);
}
@Test
public void testCast() {
String src = "struct Bar { int x; }; var b = arg ? new Bar; return b ? b.x++ + b.x++ : -1;";
- testAllCPUs(src,0,null);
+ testCPU(src,"x86_64_v2", "SystemV",2,null);
+ testCPU(src,"riscv" , "SystemV",2,null);
+ testCPU(src,"arm" , "SystemV",2,null);
}
@Test
diff --git a/src/test/java/com/seaofnodes/simple/Chapter21Test.java b/src/test/java/com/seaofnodes/simple/Chapter21Test.java
new file mode 100644
index 00000000..d8a5cebc
--- /dev/null
+++ b/src/test/java/com/seaofnodes/simple/Chapter21Test.java
@@ -0,0 +1,276 @@
+package com.seaofnodes.simple;
+
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.node.cpus.arm.arm;
+import com.seaofnodes.simple.node.cpus.riscv.riscv;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import org.junit.Ignore;
+import org.junit.Test;
+import static org.junit.Assert.*;
+
+public class Chapter21Test {
+
+ @Test @Ignore
+ public void testJig() throws IOException {
+ String src = Files.readString(Path.of("src/test/java/com/seaofnodes/simple/progs/jig.smp"));
+ testCPU(src,"x86_64_v2", "Win64" ,-1,null);
+ testCPU(src,"riscv" , "SystemV",-1,null);
+ testCPU(src,"arm" , "SystemV",-1,null);
+ }
+
+ static void testCPU( String src, String cpu, String os, int spills, String stop ) {
+ CodeGen code = new CodeGen(src).driver(CodeGen.Phase.Encoding,cpu,os);
+ int delta = spills>>3;
+ if( delta==0 ) delta = 1;
+ if( spills != -1 )
+ assertEquals("Expect spills:",spills,code._regAlloc._spillScaled,delta);
+ if( stop != null )
+ assertEquals(stop, code._stop.toString());
+ }
+
+
+ @Test public void testBasic1() {
+ String src = "return arg | 2;";
+ testCPU(src,"x86_64_v2", "SystemV",1,"return (ori,mov(arg));");
+ testCPU(src,"riscv" , "SystemV",0,"return ( arg | #2 );");
+ testCPU(src,"arm" , "SystemV",0,"return (ori,arg);");
+ }
+
+ @Test public void testInfinite() {
+ String src = "struct S { int i; }; S !s = new S; while(1) s.i++;";
+ testCPU(src,"x86_64_v2", "SystemV",0,"return Top;");
+ testCPU(src,"riscv" , "SystemV",2,"return Top;");
+ testCPU(src,"arm" , "SystemV",2,"return Top;");
+ }
+
+ @Test
+ public void testArray1() throws IOException {
+ String src = Files.readString(Path.of("src/test/java/com/seaofnodes/simple/progs/array1.smp"));
+ testCPU(src,"x86_64_v2", "SystemV",-1,"return .[];");
+ testCPU(src,"riscv" , "SystemV", 7,"return (add,.[],(mul,.[],1000));");
+ testCPU(src,"arm" , "SystemV", 5,"return (add,.[],(mul,.[],1000));");
+ }
+
+ @Test
+ public void testAntiDeps1() throws IOException {
+ String src = Files.readString(Path.of("src/test/java/com/seaofnodes/simple/progs/antiDep1.smp"));
+ testCPU(src,"x86_64_v2", "SystemV", 7,"return mov(mov(S));");
+ testCPU(src,"riscv" , "SystemV",10,"return mov(mov(S));");
+ testCPU(src,"arm" , "SystemV",10,"return mov(mov(S));");
+ }
+
+ @Test
+ public void testString() throws IOException {
+ String src = Files.readString(Path.of("src/test/java/com/seaofnodes/simple/progs/stringHash.smp"));
+ testCPU(src,"x86_64_v2", "SystemV", 9,null);
+ testCPU(src,"riscv" , "SystemV", 5,null);
+ testCPU(src,"arm" , "SystemV", 6,null);
+ }
+
+ @Test public void testStringExport() throws IOException {
+ TestC.run("stringHash", 9);
+ }
+
+ @Test public void testLoop2() throws IOException {
+ String src = Files.readString(Path.of("src/test/java/com/seaofnodes/simple/progs/loop2.smp"));
+ testCPU(src,"x86_64_v2", "Win64" ,0,"return (inc,Phi(Loop,0,inc));");
+ testCPU(src,"riscv" , "SystemV",0,"return ( Phi(Loop,0,addi) + #1 );");
+ testCPU(src,"arm" , "SystemV",0,"return (inc,Phi(Loop,0,inc));");
+ }
+
+ @Test public void testNewtonExport() throws IOException {
+ String result = """
+0 0.000000 (0)
+1 1.000000 (0)
+2 1.414214 (2.22045e-16)
+3 1.732051 (0)
+4 2.000000 (0)
+5 2.236068 (0)
+6 2.449490 (0)
+7 2.645751 (0)
+8 2.828427 (4.44089e-16)
+9 3.000000 (0)
+""";
+ TestC.run("newtonFloat",result,34);
+
+ EvalRisc5 R5 = TestRisc5.build("newtonFloat", 0, 10, false);
+ R5.fregs[riscv.FA0 - riscv.F_OFFSET] = 3.0;
+ int trap_r5 = R5.step(1000);
+ assertEquals(0,trap_r5);
+ // Return register A0 holds fib(8)==55
+ assertEquals(1.732051,R5.fregs[riscv.FA0 - riscv.F_OFFSET], 0.00001);
+
+ // arm
+ EvalArm64 A5 = TestArm64.build("newtonFloat", 0, 10, false);
+ A5.fregs[arm.D0 - arm.D_OFFSET] = 3.0;
+ int trap_arm = A5.step(1000);
+ assertEquals(0,trap_arm);
+ assertEquals(1.732051, A5.fregs[arm.D0 - arm.D_OFFSET], 0.00001);
+ }
+
+
+ @Test public void testSieve() throws IOException {
+ // The primes
+ int[] primes = new int[] { 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, };
+ SB sb = new SB().p(primes.length).p("[");
+ for( int prime : primes )
+ sb.p(prime).p(", ");
+ String sprimes = sb.p("]").toString();
+
+ // Compile, link against native C; expect the above string of primes to be printed out by C
+ TestC.run("sieve",sprimes, 257);
+
+ // Evaluate on RISC5 emulator; expect return of an array of primes in
+ // the simulated heap.
+ EvalRisc5 R5 = TestRisc5.build("sieve", 100, 160, false);
+ int trap = R5.step(10000);
+ assertEquals(0,trap);
+ // Return register A0 holds sieve(100)
+ int ary = (int)R5.regs[riscv.A0];
+ // Memory layout starting at ary(length,pad, prime1, primt2, prime3, prime4)
+ assertEquals(primes.length, R5.ld4s(ary));
+ for( int i=0; i<primes.length; i++ )
+ assertEquals(primes[i], R5.ld4s(ary + 4 + i*4));
+
+ // Evaluate on ARM5 emulator; expect return of an array of primes in
+ // the simulated heap.
+ EvalArm64 A5 = TestArm64.build("sieve", 100, 160, false);
+ int trap_arm = A5.step(10000);
+ assertEquals(0, trap_arm);
+ int ary_arm = (int)A5.regs[arm.X0];
+ // Memory layout starting at ary(length,pad, prime1, primt2, prime3, prime4)
+ assertEquals(primes.length, A5.ld4s(ary_arm));
+ for( int i = 0; i<primes.length; i++ )
+ assertEquals(primes[i], A5.ld4s(ary_arm + 4 + i * 4));
+ }
+
+ @Test public void testFibExport() throws IOException {
+ String fib = "[1, 1, 2, 3, 5, 8, 13, 21, 34, 55]";
+ TestC.run("fib", fib, 24);
+
+ EvalRisc5 R5 = TestRisc5.build("fib", 9, 16, false);
+ int trap = R5.step(100);
+ assertEquals(0,trap);
+ // Return register A0 holds fib(8)==55
+ assertEquals(55,R5.regs[riscv.A0]);
+
+ // arm
+ EvalArm64 A5 = TestArm64.build("fib", 9, 16, false);
+ int trap_arm = A5.step(100);
+ assertEquals(0,trap_arm);
+ // Return register X0 holds fib(8)==55
+ assertEquals(55, A5.regs[arm.X0]);
+ }
+
+ @Test public void testPerson() throws IOException {
+ String person = "6\n";
+ TestC.run("person", person, 0);
+
+ // Memory layout starting at PS:
+ int ps = 1<<16; // Person array pointer starts at heap start
+ // Person[3] = { len,pad,P0,P1,P2 }; // sizeof = 4*8
+ // P0 = { age } // sizeof=8
+ int p0 = ps+4*8+0*8;
+ // P1 = { age } // sizeof=8
+ int p1 = ps+4*8+1*8;
+ // P2 = { age } // sizeof=8
+ int p2 = ps+4*8+2*8;
+ EvalRisc5 R5 = TestRisc5.build("person", ps, 0, false);
+ R5.regs[riscv.A1] = 1; // Index 1
+ R5.st8(ps,3); // Length
+ R5.st8(ps+1*8,p0);
+ R5.st8(ps+2*8,p1);
+ R5.st8(ps+3*8,p2);
+ R5.st8(p0, 5); // age= 5
+ R5.st8(p1,17); // age=17
+ R5.st8(p2,60); // age=60
+
+ int trap = R5.step(100);
+ assertEquals(0,trap);
+ assertEquals( 5+0,R5.ld8(p0));
+ assertEquals(17+1,R5.ld8(p1));
+ assertEquals(60+0,R5.ld8(p2));
+
+ EvalArm64 A5 = TestArm64.build("person", ps, 0, false);
+ A5.regs[arm.X1] = 1; // Index 1
+ A5.st8(ps, 3);
+ A5.st8(ps+1*8,p0);
+ A5.st8(ps+2*8,p1);
+ A5.st8(ps+3*8,p2);
+ A5.st8(p0, 5); // age= 5
+ A5.st8(p1,17); // age=17
+ A5.st8(p2,60); // age=60
+
+ int trap_arm = A5.step(100);
+ assertEquals(0,trap_arm);
+ assertEquals( 5+0, A5.ld8(p0));
+ assertEquals(17+1, A5.ld8(p1));
+ assertEquals(60+0, A5.ld8(p2));
+ }
+
+ @Test public void testArgCount() throws IOException {
+ // Test passes more args than registers in Sys5, which is far far more
+ // than what Win64 allows - so Win64 gets a lot more spills here.
+ String arg_count = "191.000000\n";
+ TestC.run("arg_count", arg_count,
+ TestC.CALL_CONVENTION.equals("Win64") ? 42 : 15);
+
+
+ EvalRisc5 R5 = TestRisc5.build("no_stack_arg_count", 0, 0, false);
+
+ // Todo: handle stack(imaginary stack in emulator)
+ // pass in float arguments
+ R5.fregs[riscv.FA0 - riscv.F_OFFSET] = 1.1;
+ R5.fregs[riscv.FA1 - riscv.F_OFFSET] = 1.1;
+ R5.fregs[riscv.FA2 - riscv.F_OFFSET] = 1.1;
+ R5.fregs[riscv.FA3 - riscv.F_OFFSET] = 1.1;
+ R5.fregs[riscv.FA4 - riscv.F_OFFSET] = 1.1;
+ R5.fregs[riscv.FA5 - riscv.F_OFFSET] = 1.1;
+ R5.fregs[riscv.FA6 - riscv.F_OFFSET] = 1.1;
+ R5.fregs[riscv.FA7 - riscv.F_OFFSET] = 1.1;
+
+ // a0 is passed in arg
+ R5.regs[riscv.A1] = 2;
+ R5.regs[riscv.A2] = 2;
+ R5.regs[riscv.A3] = 2;
+ R5.regs[riscv.A4] = 2;
+ R5.regs[riscv.A5] = 2;
+ R5.regs[riscv.A6] = 2;
+ R5.regs[riscv.A7] = 2;
+
+ int trap = R5.step(100);
+ assertEquals(0,trap);
+
+ double result = R5.fregs[riscv.FA0 - riscv.F_OFFSET];
+
+ assertEquals(22.8, result, 0.00001);
+
+ // arm
+ EvalArm64 A5 = TestArm64.build("no_stack_arg_count", 0, 0, false);
+
+ A5.fregs[arm.D0 - arm.D_OFFSET] = 1.1;
+ A5.fregs[arm.D1 - arm.D_OFFSET] = 1.1;
+ A5.fregs[arm.D2 - arm.D_OFFSET] = 1.1;
+ A5.fregs[arm.D3 - arm.D_OFFSET] = 1.1;
+ A5.fregs[arm.D4 - arm.D_OFFSET] = 1.1;
+ A5.fregs[arm.D5 - arm.D_OFFSET] = 1.1;
+ A5.fregs[arm.D6 - arm.D_OFFSET] = 1.1;
+ A5.fregs[arm.D7 - arm.D_OFFSET] = 1.1;
+
+ A5.regs[arm.X1] = 2;
+ A5.regs[arm.X2] = 2;
+ A5.regs[arm.X3] = 2;
+ A5.regs[arm.X4] = 2;
+ A5.regs[arm.X5] = 2;
+ A5.regs[arm.X6] = 2;
+ A5.regs[arm.X7] = 2;
+
+ int trap_arm = A5.step(100);
+ assertEquals(0,trap_arm);
+
+ double result1 = A5.fregs[arm.D0 - arm.D_OFFSET];
+ assertEquals(22.8, result1, 0.00001);
+ }
+}
diff --git a/src/test/java/com/seaofnodes/simple/Compile.java b/src/test/java/com/seaofnodes/simple/Compile.java
new file mode 100644
index 00000000..088055f9
--- /dev/null
+++ b/src/test/java/com/seaofnodes/simple/Compile.java
@@ -0,0 +1,65 @@
+package com.seaofnodes.simple;
+
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.node.FunNode;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import org.junit.Ignore;
+import org.junit.Test;
+import static org.junit.Assert.*;
+
+public class Compile {
+
+ private static Process run(String simple, String c) throws IOException, InterruptedException {
+ boolean USE_WSL = System.getProperty("os.name").startsWith("Windows");
+ CodeGen code = new CodeGen(simple);
+ code.parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched().regAlloc().encode();
+ if (c != null) for (var n:code._start._outputs) if (n instanceof FunNode f && f._name.equals("main")) f._name = "simple_main";
+ code.exportELF("test.o");
+ var params = new ArrayList<String>();
+ if (USE_WSL) params.add("wsl.exe");
+ params.add("gcc");
+ params.add("-o");
+ params.add("test");
+ if (c != null) {
+ Files.writeString(Path.of("test.c"), c);
+ params.add("test.c");
+ }
+ params.add("test.o");
+ Process p = Runtime.getRuntime().exec(params.toArray(String[]::new));
+ assertEquals(0, p.waitFor());
+ return Runtime.getRuntime().exec(USE_WSL ? new String[]{"wsl.exe", "./test"}:new String[]{"./test"});
+ }
+
+ @Test
+ @Ignore
+ public void testCompile() throws IOException, InterruptedException {
+ var p = run("""
+ val testMy = { flt x ->
+ flt guess = x;
+ while( 1 ) {
+ flt next = (x/guess + guess)/2;
+ if( next == guess ) return guess;
+ guess = next;
+ }
+ };
+ ""","""
+
+ #include <stdio.h>
+
+ extern double testMy(double);
+
+ int main(int argc, char** argv) {
+ printf("%f\\n", testMy(2.0));
+ return 0;
+ }
+
+ """);
+ assertEquals(0, p.waitFor());
+ assertEquals("1.414214\n", new String(p.getInputStream().readAllBytes(), StandardCharsets.UTF_8));
+ }
+
+}
diff --git a/src/test/java/com/seaofnodes/simple/EvalArm64.java b/src/test/java/com/seaofnodes/simple/EvalArm64.java
new file mode 100644
index 00000000..3ea9bea0
--- /dev/null
+++ b/src/test/java/com/seaofnodes/simple/EvalArm64.java
@@ -0,0 +1,751 @@
+package com.seaofnodes.simple;
+
+import com.seaofnodes.simple.codegen.Encoding;
+import com.seaofnodes.simple.node.cpus.arm.arm;
+
+public class EvalArm64 {
+ // Memory image, always 0-based
+ public final byte[] _buf;
+
+ // GPRs
+ final long[] regs;
+ // FRs
+ final double[] fregs;
+
+ int _pc;
+ int SENTINEL_CALLOC = -4;
+ // Start of free memory for allocation
+ int _heap;
+ // Cycle counters
+ int _cycle;
+
+ // flags
+ boolean N = false;
+ boolean Z = false;
+ boolean C = false;
+ boolean V = false;
+
+ EvalArm64(byte[] buf, int stackSize) {
+ _buf = buf;
+ regs = new long[32];
+ fregs = new double[32];
+ // Stack grows down, heap grows up
+ regs[arm.RSP] = _heap = stackSize;
+ _pc = 0;
+ }
+
+ // Little endian
+ public int ld1s(int x) { return _buf[x ] ; }
+ public int ld1z(int x) { return _buf[x ]&0xFF; }
+ public int ld2s(int x) { return ld1z(x) | ld1s(x+1)<< 8; }
+ public int ld2z(int x) {
+ return ld1z(x) | ld1z(x+1)<< 8; }
+ public int ld4s(int x) { return ld2z(x) | ld2s(x+2)<<16; }
+ public long ld8 (int x) { return (long)ld4s(x)&0xFFFFFFFFL | ((long)ld4s(x+4))<<32; }
+
+
+ public float ld4f(int x) { return Float.intBitsToFloat(ld4s(x)); }
+ public double ld8f(int x) { return Double.longBitsToDouble(ld8(x)); }
+
+ public void st1(int x, int val) { _buf[x] = (byte)val; }
+ public void st2(int x, int val) { st1(x,val); st1(x+1,val>> 8); }
+ public void st4(int x, int val) { st2(x,val); st2(x+2,val>>16); }
+ public void st8(int x, long val) { st4(x,(int)val); st2(x+4,(int)(val>>32)); }
+
+ public int step(int maxops) {
+ int trap = 0;
+ long rval = 0;
+ double frval = 0;
+ int pc = _pc;
+ int cycle = _cycle;
+ boolean is_f = false;
+ outer:
+ for(int icount = 0; icount < maxops; icount++) {
+ int ir = 0;
+ rval = 0;
+ frval = 0;
+ cycle++;
+ if( pc >= _buf.length ) {
+ trap = 1 + 1; // Handle access violation on instruction read.
+ break;
+ }
+
+ if( (pc & 3)!=0 ) {
+ trap = 1; // Handle PC-misaligned access
+ break;
+ }
+
+ // Load instruction from image buffer at PC
+ ir = ld4s( pc );
+
+ int rdid = ir & 0x1f;
+ // [31:24]
+ int opcode1 = (ir >> 24) & 0xFF;
+ // [31:26]
+ int opcode2 = (ir >> 26) & 0x3F;
+
+ switch(opcode2) {
+ case 5: {
+ //b
+ int imm26 = ir << 6 >> 6;
+
+ pc = pc + (imm26 * 4) - 4;
+ // won't hit pc += 4 at the bottom because continue
+ // do it manually
+ pc += 4;
+ rdid = -1;
+ continue ;
+ }
+ case 0x25: {
+ // bl (just calloc)
+ rval = pc + 4;
+ regs[arm.X30] = rval;
+ if(pc + 4 == 0) {
+ regs[rdid] = rval;
+ break outer;
+ }
+ int imm26 = (ir & 0x03FFFFFF);
+ int imm = (imm26 << 6) >> 6;
+ pc = pc + (imm << 2) ;
+ if (pc == SENTINEL_CALLOC) {
+ long size = regs[arm.X0]*regs[arm.X1];
+ regs[arm.X0] = _heap;
+ _heap += (int)size;
+ // unwind pc
+ pc = (int)rval;
+ }
+ continue;
+ }
+ }
+ // for the opcode we just match for bits[31:24];
+ switch(opcode1) {
+ case 0xD2: {
+ // movz
+ // get immediate
+ rval = (ir >> 5) & 0x7FFF;
+ break;
+ }
+ case 0x38: {
+ // STRB/LDRB(register)
+ int opc = ir >> 21 & 0x7;
+ int rn = (ir >> 5) & 0x1F;
+ int rm = (ir >> 16) & 0x1F;
+ // full opcode
+ opcode1 = (ir >> 21) & 0x7FF;
+ switch(opc) {
+ case 0x1: {
+ // store
+ if(opcode1 == arm.OP_STORE_R_8) {
+ st1((int)regs[rn] + (int)regs[rm], (int)regs[rdid]);
+ }else {
+ trap = (2+1);
+ }
+ rdid = -1;
+ break;
+ }
+ case 0x3: {
+ // load
+ if(opcode1 == arm.OP_LOAD_R_8) {
+ rval = ld1s((int)regs[rn] + (int)regs[rm]);
+ } else {
+ trap = (2+1);
+ }
+ break;
+ }
+ default: trap = (2+1);
+ }
+ break;
+ }
+ case 0x39: {
+ // STRB/LDRB(immediate)
+ // str/ldr(immediate)
+ // rn
+ int base = ir >> 5 & 0x1F;
+ int imm = (ir >> 10) & 0xFFF;
+ rval = regs[base] + imm;
+ int opc = (ir >> 22) & 0x3;
+ // full opcode
+ opcode1 = (ir >> 22) & 0x3FF;
+
+ switch(opc) {
+ case 0x0: {
+ // store
+ if(opcode1 == arm.OP_STORE_IMM_8) {
+ st1((int)rval, (int)regs[rdid]);
+ } else trap = (2+1);
+ rdid = -1;
+
+ break;
+ }
+ case 0x1: {
+ // LDR (immediate)
+ if(opcode1 == arm.OP_LOAD_IMM_8) {
+ rval = ld1s((int)rval);
+ } else trap = (2+1);
+ rdid = -1;
+ break;
+ }
+ default: trap = (2+1);
+ }
+ break;
+ }
+ case 0x78: {
+ // STRH/LDRH(register)
+ int opc = ir >> 21 & 0x7;
+ int rn = (ir >> 5) & 0x1F;
+ int rm = (ir >> 16) & 0x1F;
+ // full opcode
+ opcode1 = (ir >> 21) & 0x7FF;
+ switch(opc) {
+ case 0x1: {
+ // store
+ if(opcode1 == arm.OP_STORE_R_16) {
+ st1((int)regs[rn] + (int)regs[rm], (int)regs[rdid]);
+ }else {
+ trap = (2+1);
+ }
+ rdid = -1;
+ break;
+ }
+ case 0x3: {
+ // load
+ if(opcode1 == arm.OP_LOAD_R_16) {
+ rval = ld1s((int)regs[rn] + (int)regs[rm]);
+ } else {
+ trap = (2+1);
+ }
+ break;
+ }
+ default: trap = (2+1);
+ }
+ break;
+ }
+
+ case 0x79: {
+ // STRH/LDRH(immediate)
+ int base = ir >> 5 & 0x1F;
+ int imm = (ir >> 10) & 0xFFF;
+ imm *= 2;
+ rval = regs[base] + imm;
+ int opc = (ir >> 22) & 0x3;
+ // full opcode
+ opcode1 = (ir >> 22) & 0x3FF;
+
+ switch(opc) {
+ case 0x0: {
+ // store
+ if(opcode1 == arm.OP_STORE_IMM_16) {
+ st2((int)rval, (int)regs[rdid]);
+ } else trap = (2+1);
+ rdid = -1;
+
+ break;
+ }
+ case 0x1: {
+ // LDR (immediate)
+ if(opcode1 == arm.OP_LOAD_IMM_16) {
+ rval = ld2s((int)rval);
+ } else trap = (2+1);
+ rdid = -1;
+ break;
+ }
+ default: trap = (2+1);
+ }
+ break;
+ }
+
+
+ case 0x5c: {
+ // ldr
+ is_f = true;
+ // address = delta
+ int address = ir << 8 >> 13;
+ address *= 4;
+ if(address >= _buf.length-3) trap = (5 + 1);
+ frval = ld8f(pc + address);
+ break;
+ }
+ case 0xB9: {
+ // str/ldr(immediate) 32 bits
+ int base = ir >> 5 & 0x1F;
+ int imm = (ir >> 10) & 0xFFF;
+ imm *= 4;
+
+ rval = regs[base] + imm;
+ int opc = (ir >> 22) & 0x3;
+
+ opcode1 = (ir >> 22) & 0x3FF;
+ switch(opc) {
+ case 0x0: {
+ // store
+ if(opcode1 == arm.OP_STORE_IMM_32) {
+ st4((int)rval, (int)regs[rdid]);
+ } else trap = (2+1);
+
+ rdid = -1;
+ break;
+ }
+ case 0x1: {
+ // LDR (immediate)
+ if(opcode1 == arm.OP_LOAD_IMM_32) {
+ rval = ld4s((int) rval);
+ } else trap = (2+1);
+ break;
+ }
+ default: trap = (2+1);
+ }
+ break;
+ }
+ case 0xF9: {
+ // str/ldr(immediate) 64 bits
+ int base = ir >> 5 & 0x1F;
+ int imm = (ir >> 10) & 0xFFF;
+
+ imm *= 8;
+
+ rval = regs[base] + imm;
+ int opc = (ir >> 22) & 0x3;
+ // full opcode
+ opcode1 = (ir >> 22) & 0x3FF;
+ switch(opc) {
+ case 0x0: {
+ // store
+ if(opcode1 == arm.OP_STORE_IMM_64) {
+ st8((int)rval, regs[rdid]);
+ } else trap = (2+1);
+
+ rdid = -1;
+ break;
+ }
+ case 0x1: {
+ // LDR (immediate)
+ if(opcode1 == arm.OP_LOAD_IMM_64) {
+ rval = ld8((int)rval);
+ } else trap = (2+1);
+ break;
+ }
+ default: trap = (2+1);
+ }
+ break;
+ }
+ case 0xf8: {
+ // LDR/STR (register + index) 64 bits
+ int opc = ir >> 21 & 0x7;
+ int rn = (ir >> 5) & 0x1F;
+ int rm = (ir >> 16) & 0x1F;
+ // full opcode
+ opcode1 = (ir >> 21) & 0x7FF;
+ switch(opc) {
+ case 0x1: {
+ if(opcode1 == arm.OP_STORE_R_64) {
+ st8((int)regs[rn] + (int)regs[rm], regs[rdid]);
+ }else {
+ trap = (2+1);
+ }
+ // store(expected to store the length here)
+
+ rdid = -1;
+ break;
+ }
+ case 0x3: {
+ // load
+ if(opcode1 == arm.OP_LOAD_R_64) {
+ rval = ld8((int) (regs[rn] + regs[rm]));
+ } else {
+ trap = (2+1);
+ }
+ break;
+ }
+ default: trap = (2+1);
+ }
+
+ break;
+ }
+
+ case 0xB8: {
+ // LDR/STR (register + index) 32 bits
+ int opc = ir >> 21 & 0x7;
+ int rn = (ir >> 5) & 0x1F;
+ int rm = (ir >> 16) & 0x1F;
+ // full opcode
+ opcode1 = (ir >> 21) & 0x7FF;
+ switch(opc) {
+ case 0x1: {
+ if(opcode1 == arm.OP_STORE_R_32) {
+ st4((int)regs[rn] + (int)regs[rm], (int)regs[rdid]);
+ }else {
+ trap = (2+1);
+ }
+ // store(expected to store the length here)
+
+ rdid = -1;
+ break;
+ }
+ case 0x3: {
+ // load
+ if( opcode1 == arm.OP_LOAD_R_32) {
+ rval = ld4s((int) (regs[rn] + regs[rm]));
+ } else {
+ trap = (2+1);
+ }
+ break;
+ }
+ default: trap = (2+1);
+ }
+
+ break;
+ }
+
+ case 0xFC: {
+ // LDR/STR (register + index) float 64 bits
+ int opc = ir >> 21 & 0x7;
+ int rn = (ir >> 5) & 0x1F;
+ int rm = (ir >> 16) & 0x1F;
+ is_f = true;
+ // full opcode
+ opcode1 = (ir >> 21) & 0x7FF;
+ switch(opc) {
+ case 0x1: {
+ if(opcode1 == arm.OPF_STORE_R_64) {
+ st8((int)regs[rn] + (int)regs[rm], (int)regs[rdid]);
+ }else {
+ trap = (2+1);
+ }
+ // store(expected to store the length here)
+
+ rdid = -1;
+ break;
+ }
+ case 0x3: {
+ // load
+ if(opcode1 == arm.OPF_LOAD_R_64) {
+ frval = ld8f((int) (regs[rn] + regs[rm]));
+ } else {
+ trap = (2+1);
+ }
+ break;
+ }
+ default: trap = (2+1);
+ }
+
+ break;
+ }
+
+ case 0xBC: {
+ // LDR/STR (register + index) float 32 bits
+ int opc = ir >> 21 & 0x7;
+ int rn = (ir >> 5) & 0x1F;
+ int rm = (ir >> 16) & 0x1F;
+ is_f = true;
+ // full opcode
+ opcode1 = (ir >> 21) & 0x7FF;
+ switch(opc) {
+ case 0x1: {
+ if(opcode1 == arm.OPF_STORE_R_32) {
+ st4((int)regs[rn] + (int)regs[rm], (int)regs[rdid]);
+ }else {
+ trap = (2+1);
+ }
+ // store(expected to store the length here)
+
+ rdid = -1;
+ break;
+ }
+ case 0x3: {
+ // load
+ if(opcode1 == arm.OPF_LOAD_R_32) {
+ frval = ld4f((int) (regs[rn] + regs[rm]));
+ } else {
+ trap = (2+1);
+ }
+ break;
+ }
+ default: trap = (2+1);
+ }
+
+ break;
+ }
+
+ case 0xBD: {
+ // LDR/STR(immediate) 32 bits floats
+ int base = ir >> 5 & 0x1F;
+ int imm = (ir >> 10) & 0xFFF;
+
+ imm *= 4;
+ is_f = true;
+ rval = regs[base] + imm;
+ int opc = (ir >> 22) & 0x3;
+ // full opcode
+ opcode1 = (ir >> 22) & 0x3FF;
+ switch(opc) {
+ case 0x0: {
+ // store
+ if(opcode1 == arm.OPF_STORE_IMM_32) {
+ st4((int)rval, (int)regs[rdid]);
+ } else trap = (2+1);
+
+ rdid = -1;
+ break;
+ }
+ case 0x1: {
+ // LDR (immediate)
+ if(opcode1 == arm.OPF_LOAD_IMM_32) {
+ frval = ld4s((int)rval);
+ } else trap = (2+1);
+ break;
+ }
+ default: trap = (2+1);
+ }
+ break;
+ }
+ case 0xFD: {
+ // LDR/STR(immediate) 64 bits floats
+ int base = ir >> 5 & 0x1F;
+ int imm = (ir >> 10) & 0xFFF;
+
+ imm *= 4;
+ is_f = true;
+ rval = regs[base] + imm;
+ int opc = (ir >> 22) & 0x3;
+ // full opcode
+ opcode1 = (ir >> 22) & 0x3FF;
+ switch(opc) {
+ case 0x0: {
+ // store
+ if(opcode1 == arm.OPF_STORE_IMM_64) {
+ st8((int)rval, regs[rdid]);
+ } else trap = (2+1);
+
+ rdid = -1;
+ break;
+ }
+ case 0x1: {
+ // LDR (immediate)
+ if(opcode1 == arm.OPF_LOAD_IMM_64) {
+ frval = ld8((int)rval);
+ } else trap = (2+1);
+ break;
+ }
+ default: trap = (2+1);
+ }
+ break;
+ }
+ case 0x9b: {
+ // mul
+ int rn = (ir >> 5) & 0x1F;
+ int rm = (ir >> 16) & 0x1F;
+ rval = regs[rn] * regs[rm];
+ break;
+ }
+ case 0x54: {
+ // b.cond
+ int imm19 = ir << 8 >> 13;
+ imm19 = pc + (imm19 * 4) - 4;
+ rdid = -1;
+
+ switch(ir & 0xF) {
+ case 0x0: if(Z) pc = imm19; break; // eq
+ case 0x1: if(!Z) pc = imm19; break; // ne
+ case 0x2: if(C) pc = imm19; break; // cs
+ case 0x3: if(!C) pc = imm19; break;
+ case 0x4: if(N) pc = imm19; break; // mi
+ case 0x5: if(!N) pc = imm19; break; // pl
+ case 0x6: if(V) pc = imm19; break; // vs
+ case 0x7: if(!V) pc = imm19; break; // vc
+ case 0x8: if(C && !Z) pc = imm19; break; // hi
+ case 0x9: if(!C || Z) pc = imm19; break; // ls
+ case 0xA: if(N == V) pc = imm19; break; // ge
+ case 0xB: if(N != V) pc = imm19; break; // lt
+ case 0xC: if(!Z && N == V) pc = imm19; break; // gt
+ case 0xD: if(Z || N != V) pc = imm19; break; // le
+ case 0xE: pc = imm19; break; // always executed(al)
+ default: trap = (2+1);
+ }
+ break;
+ }
+ case 0x9a: {
+ // conditional select(csel)
+ int rm = (ir >> 16) & 0x1F;
+ switch((ir >> 12) & 0xF) {
+ case 0x0: if(Z) rval = regs[rm]; break; // eq
+ case 0x1: if(!Z) rval = regs[rm]; break; // ne
+ case 0x2: if(C) rval = regs[rm]; break; // cs
+ case 0x3: if(!C) rval = regs[rm]; break;
+ case 0x4: if(N) rval = regs[rm]; break; // mi
+ case 0x5: if(!N) rval = regs[rm]; break; // pl
+ case 0x6: if(V) rval = regs[rm]; break; // vs
+ case 0x7: if(!V) rval = regs[rm]; break; // vc
+ case 0x8: if(C && !Z) rval = regs[rm]; break; // hi
+ case 0x9: if(!C || Z) rval = regs[rm]; break; // ls
+ case 0xA: if(N == V) rval = regs[rm]; break; // ge
+ case 0xB: if(N != V) rval = regs[rm]; break; // lt
+ case 0xC: if(!Z && N == V) rval = regs[rm]; break; // gt
+ case 0xD: if(Z || N != V) rval = regs[rm]; break; // le
+ case 0xE: rval = regs[rm]; break; // always executed(al)
+ default: {
+ rval = regs[(ir >> 5) & 0x1F];
+ }
+ }
+ break;
+ }
+ case 0xeb: {
+ // subs(shifted register)
+ int rn = (ir >> 5) & 0x1F;
+ int rm = (ir >> 16) & 0x1F;
+ long lhs = regs[rn];
+ long rhs = regs[rm];
+ rval = lhs - rhs;
+
+ // set flags as a side effect
+ Z = (rval == 0);
+ N = (rval < 0);
+ C = lhs >= rhs;
+
+ boolean sign_lhs = (lhs >>> 63) != 0;
+ boolean sign_rhs = (rhs >>> 63) != 0;
+ boolean sign_res = (rval >>> 63) != 0;
+ V = (sign_lhs != sign_rhs) && (sign_res != sign_lhs);
+ rdid = -1;
+ break;
+ }
+ case 0xF1: {
+ // subs(immediate)
+ int rn = (ir >> 5) & 0x1F;
+ int immediate = ir << 10 >> 20;
+ long lhs = regs[rn];
+
+ rval = lhs - immediate;
+
+ // set flags as a side effect
+ Z = (rval == 0);
+ N = (rval < 0);
+ C = lhs >= immediate;
+
+ boolean sign_lhs = (lhs >>> 63) != 0;
+ boolean sign_rhs = (immediate >>> 63) != 0;
+ boolean sign_res = (rval >>> 63) != 0;
+ V = (sign_lhs != sign_rhs) && (sign_res != sign_lhs);
+ rdid = -1;
+ break;
+ }
+ case 0x8B: {
+ // add(shifted register)
+ int rn = (ir >> 5) & 0x1F;
+ int rm = (ir >> 16) & 0x1F;
+ long lhs = regs[rn];
+ long rhs = regs[rm];
+ rval = lhs + rhs;
+ break;
+ }
+ case 0x91: {
+ // add(immediate)
+ int rn = (ir >> 5) & 0x1F;
+ int immediate = ir << 10 >> 20;
+ // only hit it twice
+ rval = regs[rn] + immediate;
+ break;
+ }
+ case 0xD3: {
+ // lsl(immediate)
+ int imm = (ir >> 16) & 0x3F;
+ int rn = (ir >> 5) & 0x1F;
+ rval = regs[rn] << (64 - imm);
+ break;
+ }
+ case 0x93: {
+ // asr(immediate)
+ int imm = (ir >> 16) & 0x3F;
+ int rn = (ir >> 5) & 0x1F;
+ rval = regs[rn] >> (imm);
+ break;
+ }
+ case 0x92: {
+ // and(immediate)
+ int imm12 = (ir >> 10) & 0x1FFF;
+ long immediate = arm.decodeImm12(imm12);
+ int rn = (ir >> 5) & 0x1F;
+ rval = regs[rn] & immediate;
+ break;
+ }
+ case 0xAA: {
+ // MOV(register)
+ int rm = (ir >> 16) & 0x1F;
+ rval = regs[rm];
+ break;
+ }
+ case 0xD6: {
+ rdid = -1;
+ if(ld4s(pc + 4) == 0) {
+ break outer;
+ }
+ }
+ // bitwise
+ case 0x8A: {
+ // AND
+ int rn = (ir >> 5) & 0x1F;
+ int rm = (ir >> 16) & 0x1F;
+ rval = regs[rn] & regs[rm];
+ break;
+ }
+ // floats
+ case 0x1E: {
+ is_f = true;
+ int rs1 = (ir >> 5) & 0x1F;
+ int rs2 = (ir >> 16) & 0x1F;
+
+ int op = (ir >> 10) & 0x3F;
+ switch(op) {
+ case 16: frval = fregs[rs1]; break; // fmov
+ case 10:frval = fregs[rs1] + fregs[rs2]; break; // fadd
+
+ case 6: if (fregs[rs2] == 0) frval = 0; else frval = fregs[rs1] / fregs[rs2]; break; // fdiv
+ case 8: {
+ // fcmp
+ double lhs = fregs[rs1];
+ double rhs = fregs[rs2];
+
+ Z = (lhs == rhs);
+ N = (lhs < rhs);
+ C = lhs >= rhs;
+ V = Double.isNaN(lhs) || Double.isNaN(rhs);
+ rdid = -1;
+
+ break;
+ }
+ case 2: frval = fregs[rs1] * fregs[rs2]; break; // fmul
+ case 14: frval = fregs[rs1] - fregs[rs2]; break; // fsub
+ default: trap = (2+1);
+ }
+ break;
+ }
+ case 0x9e: {
+ // scvtf
+ is_f = true;
+ int rs1 = (ir >> 5) & 0x1F;
+ frval = (double)regs[rs1];
+ break;
+ }
+ default: trap = (2+1);
+ }
+ if(trap != 0) {
+ _pc = pc;
+ break;
+ }
+ if(rdid != -1) {
+ if(is_f) {fregs[rdid] = frval; is_f = false;}
+ else regs[rdid] = rval;
+ }
+
+ pc += 4;
+ }
+ // Handle traps and interrupts.
+ if(trap != 0) {
+ return trap;
+ }
+ _cycle = cycle;
+ _pc = pc;
+ return 0;
+ }
+
+
+}
diff --git a/src/test/java/com/seaofnodes/simple/EvalRisc5.java b/src/test/java/com/seaofnodes/simple/EvalRisc5.java
new file mode 100644
index 00000000..fd953342
--- /dev/null
+++ b/src/test/java/com/seaofnodes/simple/EvalRisc5.java
@@ -0,0 +1,353 @@
+ package com.seaofnodes.simple;
+
+/** Simple RISC5 CPU Emulator
+ *
+ * Original works credit Charles Lohr.
+ * This is a complete rewrite in Java.
+ *
+ * Load code into memory and go!
+ */
+
+import com.seaofnodes.simple.codegen.Encoding;
+import com.seaofnodes.simple.node.cpus.riscv.riscv;
+
+import java.util.Arrays;
+
+ public class EvalRisc5 {
+
+ // Memory image, always 0-based
+ public final byte[] _buf;
+
+ // GPRs
+ final long[] regs;
+ // FRs
+ final double[] fregs;
+ // PC
+ int _pc;
+
+ // Start of free memory for allocation
+ int _heap;
+
+ // Cycle counters
+ int _cycle;
+
+ EvalRisc5( byte[] buf, int stackSize ) {
+ _buf = buf;
+ regs = new long[32];
+ fregs = new double[32];
+ // Stack grows down, heap grows up
+ regs[riscv.RSP] = _heap = stackSize;
+ _pc = 0;
+ }
+
+ // Little endian
+ public int ld1s(int x) { return _buf[x ] ; }
+ public int ld1z(int x) { return _buf[x ]&0xFF; }
+ public int ld2s(int x) { return ld1z(x) | ld1s(x+1)<< 8; }
+ public int ld2z(int x) { return ld1z(x) | ld1z(x+1)<< 8; }
+ public int ld4s(int x) { return ld2z(x) | ld2s(x+2)<<16; }
+ public long ld8 (int x) { return (long)ld4s(x)&0xFFFFFFFFL | ((long)ld4s(x+4))<<32; }
+
+ public float ld4f(int x) { return Float.intBitsToFloat(ld4s(x)); }
+ public double ld8f(int x) { return Double.longBitsToDouble(ld8(x)); }
+
+
+ public void st1(int x, int val) { _buf[x] = (byte)val; }
+ public void st2(int x, int val) { st1(x,val); st1(x+1,val>> 8); }
+ public void st4(int x, int val) {st2(x,val); st2(x+2,val>>16); }
+ public void st8(int x, long val) { st4(x,(int)val); st2(x+4,(int)(val>>32)); }
+
+ // Note: only a few bits are used. (Machine = 3, User = 0)
+ // Bits 0..1 = privilege.
+ // Bit 2 = WFI (Wait for interrupt)
+ // Bit 3+ = Load/Store reservation LSBs.
+ int _extraflags;
+
+ public int step( int maxops ) {
+ int trap = 0;
+ long rval = 0;
+ double frval = 0;
+ int pc = _pc;
+ int cycle = _cycle;
+ boolean is_f = false;
+ outer:
+ for( int icount = 0; icount < maxops; icount++ ) {
+ int ir = 0;
+ rval = 0;
+ cycle++;
+
+ if( pc >= _buf.length ) {
+ trap = 1 + 1; // Handle access violation on instruction read.
+ break;
+ }
+ if( (pc & 3)!=0 ) {
+ trap = 1; // Handle PC-misaligned access
+ break;
+ }
+ // Load instruction from image buffer at PC
+ ir = ld4s( pc );
+ int rdid = (ir >> 7) & 0x1f;
+
+ switch( ir & 0x7f ) {
+ case 0x37: // LUI (0b0110111)
+ rval = ( ir & 0xfffff000 );
+ break;
+ case 0x17: // AUIPC (0b0010111)
+ rval = pc + ( ir & 0xfffff000 );
+ break;
+ case 0x6F: { // JAL (0b1101111)
+ int reladdy = ((ir & 0x80000000)>>11) | ((ir & 0x7fe00000)>>20) | ((ir & 0x00100000)>>9) | ((ir&0x000ff000));
+ if( (reladdy & 0x00100000)!=0 ) reladdy |= 0xffe00000; // Sign extension.
+ rval = pc + 4;
+ pc = pc + reladdy - 4;
+ break;
+ }
+ case 0x67: { // JALR (0b1100111)
+ int imm_se = ir >> 20; // Sign extend 12 bits
+ rval = pc + 4;
+ pc = ((int)regs[ (ir >> 15) & 0x1f ] + imm_se) & ~1;
+ // Return from top-level ; exit sim
+ if( pc==0 ) {
+ if( rdid!=0 ) regs[rdid] = rval; // Write PC into register before exit
+ break outer;
+ }
+ // Inline CALLOC effect
+ if( pc == Encoding.SENTINAL_CALLOC ) {
+ int size = (int)(regs[10]*regs[11]);
+ regs[10] = _heap; // Return next free address
+ // Pre-zeroed; epsilon (null) collector, never recycles memory so always zero
+ // Arrays.fill(_buf,_heap,_heap+size, (byte) 0 );
+ _heap += size;
+ pc = (int)(rval - 4); // Unwind PC, as-if returned from calloc
+ } else
+ pc -= 4;
+ break;
+ }
+ case 0x63: { // Branch (0b1100011)
+ int immm4 = ((ir & 0xf00)>>7) | ((ir & 0x7e000000)>>20) | ((ir & 0x80) << 4) | ((ir >> 31)<<12);
+ if( (immm4 & 0x1000)!=0 ) immm4 |= 0xffffe000;
+ long rs1 = regs[(ir >> 15) & 0x1f];
+ long rs2 = regs[(ir >> 20) & 0x1f];
+ immm4 = pc + immm4 - 4;
+ rdid = 0;
+ if( switch( (ir >> 12) & 0x7 ) {
+ case 0 -> rs1 == rs2;
+ case 1 -> rs1 != rs2;
+ case 4 -> rs1 < rs2;
+ case 5 -> rs1 >= rs2;
+ case 6 -> Long.compareUnsigned(rs1,rs2) < 0; //BLTU
+ case 7 -> Long.compareUnsigned(rs1,rs2) >= 0; //BGEU
+ default -> { trap = (2+1); yield false; }
+ } ) pc = immm4;
+ break;
+ }
+ case 0x03: { // Load (0b0000011)
+ int rs1 = (int)regs[(ir >> 15) & 0x1f]; // Address chop to 32b
+ int imm = ir >> 20;
+ int imm_se = imm | (( (imm & 0x800)!=0 ) ? 0xfffff000 : 0 );
+ int rsval = rs1 + imm_se;
+
+ if( rsval >= _buf.length-3 ) {
+ trap = (5+1);
+ rval = rsval;
+ } else {
+ switch( ( ir >> 12 ) & 0x7 ) {
+ //LB, LH, LW, LBU, LHU
+ case 0: rval = ld1s( rsval ); break;
+ case 1: rval = ld2s( rsval ); break;
+ case 2: rval = ld4s( rsval ); break;
+ case 3: rval = ld8 ( rsval ); break;
+ case 4: rval = ld1z( rsval ); break;
+ case 5: rval = ld2z( rsval ); break;
+ case 6: rval = ld4s( rsval ); break;
+ default: trap = (2+1);
+ }
+ }
+ break;
+ }
+ // floats
+ case 0x7: {
+ is_f = true;
+ int rs1 = (int)regs[(ir >> 15) & 0x1f]; // Address chop to 32b
+ int imm = ir >> 20;
+ int imm_se = imm | (( (imm & 0x800)!=0 ) ? 0xfffff000 : 0 );
+ int rsval = rs1 + imm_se;
+
+ if( rsval >= _buf.length-3 ) {
+ trap = (5+1);
+ rval = rsval;
+ } else {
+ switch( ( ir >> 12 ) & 0x7 ) {
+ case 2: frval = ld4f( rsval ); break;
+ case 3: frval = ld8f( rsval ); break;
+ default: trap = (2+1);
+ }
+ }
+ break;
+ }
+
+ case 0x23: { // Store 0b0100011
+ int rs1 = (int)regs[(ir >> 15) & 0x1f]; // Address chop to 32b
+ long rs2 = regs[(ir >> 20) & 0x1f];
+ int addy = ( ( ir >> 7 ) & 0x1f ) | ( ( ir & 0xfe000000 ) >> 20 );
+ if( (addy & 0x800)!=0 ) addy |= 0xfffff000;
+ addy += rs1;
+ rdid = 0;
+ if( addy >= _buf.length-3 ) {
+ trap = (7+1); // Store access fault.
+ rval = addy;
+ } else {
+ switch( ( ir >> 12 ) & 0x7 ) { //SB, SH, SW
+ case 0: st1( addy, (int)rs2 ); break;
+ case 1: st2( addy, (int)rs2 ); break;
+ case 2: st4( addy, (int)rs2 ); break;
+ case 3: st8( addy, rs2 ); break;
+ default: trap = (2+1);
+ }
+ }
+ break;
+ }
+ case 0x13: // Op-immediate 0b0010011
+ case 0x33: { // Op 0b0110011
+ int imm = ir >> 20;
+ imm = imm | (( (imm & 0x800)!=0 ) ? 0xfffff000 : 0);
+ long rs1 = regs[(ir >> 15) & 0x1f];
+ boolean is_reg = (ir & 0x20)!=0;
+ long rs2 = is_reg ? regs[imm & 0x1f] : imm;
+
+ // Insert the detection here
+ int rs1id = (ir >> 15) & 0x1f;
+ int funct3 = (ir >> 12) & 0x7;
+
+ if( is_reg && ( ir & 0x02000000 )!=0 ) {
+ switch( (ir>>12)&7 ) { //0x02000000 = RV32M
+ case 0: rval = rs1 * rs2; break; // MUL
+ case 1: rval = (int)((long)((int)rs1) * (long)((int)rs2)) >> 32; break; // MULH
+ case 2: rval = (int)((long)((int)rs1) * (long)rs2) >> 32; break; // MULHSU
+ case 3: rval = (int)((long)rs1 * (long)rs2) >> 32; break; // MULHU
+ case 4: if( rs2 == 0 ) rval = -1; else rval = ((int)rs1 == Integer.MIN_VALUE && (int)rs2 == -1) ? rs1 : ((int)rs1 / (int)rs2); break; // DIV
+ case 5: if( rs2 == 0 ) rval = 0xffffffff; else rval = rs1 / rs2; break; // DIVU
+ case 6: if( rs2 == 0 ) rval = rs1; else rval = ((int)rs1 == Integer.MIN_VALUE && (int)rs2 == -1) ? 0 : ((int)((int)rs1 % (int)rs2)); break; // REM
+ case 7: if( rs2 == 0 ) rval = rs1; else rval = rs1 % rs2; break; // REMU
+ }
+ } else {
+ rval = switch( (ir >> 12) & 7 ) { // These could be either op-immediate or op commands. Be careful.
+ case 0 -> (is_reg && (ir & 0x40000000) != 0) ? (rs1 - rs2) : (rs1 + rs2);
+ case 1 -> rs1 << (rs2 & 0x1F);
+ case 2 -> (rs1 < rs2) ? 1 : 0;
+ case 3 -> (rs1 < rs2) ? 1 : 0;
+ case 4 -> rs1 ^ rs2;
+ case 5 -> (ir & 0x40000000) != 0 ? (((int) rs1) >> (rs2 & 0x1F)) : (rs1 >> (rs2 & 0x1F));
+ case 6 -> rs1 | rs2;
+ case 7 -> rs1 & rs2;
+ default -> rval;
+ };
+ }
+ break;
+ }
+ case 0x0f: // 0b0001111
+ rdid = 0; // fencetype = (ir >> 12) & 0b111; We ignore fences in this impl.
+ break;
+ case 0x73: // Zifencei+Zicsr (0b1110011)
+ trap = (2+1); // Note micrrop 0b100 == undefined.
+ break;
+
+ case 0x2f: { // RV32A (0b00101111)
+ long rs1 = regs[(ir >> 15) & 0x1f];
+ long rs2 = regs[(ir >> 20) & 0x1f];
+ int irmid = ( ir>>27 ) & 0x1f;
+
+ // We don't implement load/store from UART or CLNT with RV32A here.
+ if( rs1 >= _buf.length-3 ) {
+ trap = (7+1); //Store/AMO access fault
+ rval = rs1;
+ } else {
+ rval = ld4s( (int)rs1 );
+
+ // Referenced a little bit of https://github.com/franzflasch/riscv_em/blob/master/src/core/core.c
+ boolean dowrite = true;
+ switch( irmid ) {
+ case 2: //LR.W (0b00010)
+ dowrite = false;
+ _extraflags = (_extraflags & 0x07) | (int)(rs1<<3);
+ break;
+ case 3: { //SC.W (0b00011) (Make sure we have a slot, and, it's valid)
+ boolean xflag = _extraflags >> 3 != ( rs1 & 0x1fffffff );
+ rval = xflag ? 1 : 0; // Validate that our reservation slot is OK.
+ dowrite = !xflag; // Only write if slot is valid.
+ break;
+ }
+ case 0: rs2 += rval; break; // AMOADD.W (0b00000)
+ case 1: break; // AMOSWAP.W (0b00001)
+ case 4: rs2 ^= rval; break; // AMOXOR.W (0b00100)
+ case 8: rs2 |= rval; break; // AMOOR.W (0b01000)
+ case 12: rs2 &= rval; break; // AMOAND.W (0b01100)
+ case 16: rs2 = Math.min( rs2, rval ); break; //AMOMIN.W (0b10000)
+ case 20: rs2 = Math.max( rs2, rval ); break; //AMOMAX.W (0b10100)
+ case 24: rs2 = Math.min( rs2, rval ); break; //AMOMINU.W (0b11000)
+ case 28: rs2 = Math.max( rs2, rval ); break; //AMOMAXU.W (0b11100)
+ default: trap = (2+1); dowrite = false; break; //Not supported.
+ }
+ if( dowrite ) st4( (int)rs1, (int)rs2 );
+ }
+ break;
+ }
+
+ case 0x53: {
+ is_f = true;
+ int rs1 = (ir >> 15) & 0x1F;
+ int rs2 = (ir >> 20) & 0x1F;
+ double lhs = fregs[rs1];
+ double rhs = fregs[rs2];
+ int func5 = (ir >> 27) & 0x1F;
+ int func2 = (ir >> 25) & 0x3;
+ if( func2 != 1 ) throw Utils.TODO(); // 1==double
+ switch( func5 ) {
+ case 0: frval = lhs + rhs; break; // fadd.d
+ case 1: frval = lhs - rhs; break; // fsub.d
+ case 2: frval = lhs * rhs; break; // fmul.d
+ case 3: frval = lhs / rhs; break; // fdiv.d
+ case 5: frval = Math.min(lhs,rhs); break; // fmin.d, also used for move
+ case 20:
+ is_f = false; // Output into a GPR
+ rval = switch( (ir >> 12) & 7 ) {
+ case 0 -> lhs <= rhs ? 1 : 0;
+ case 1 -> lhs == rhs ? 1 : 0;
+ case 2 -> lhs < rhs ? 1 : 0;
+ default -> throw Utils.TODO();
+ };
+ break;
+ case 26: frval = (double)regs[rs1]; break; // fcvt.w.d
+ default:
+ trap = (2+1); // Fault: Invalid opcode.
+ throw Utils.TODO();
+ }
+ break;
+ }
+
+ default: trap = (2+1); // Fault: Invalid opcode.
+ }
+
+ // If there was a trap, do NOT allow register writeback.
+ if( trap!=0 ) {
+ _pc = pc;
+ break;
+ }
+
+ if( rdid!=0 || is_f) {
+ if(is_f) {fregs[rdid] = frval; is_f = false;} // Write back register.
+ else regs[rdid] = rval; // Write back register.
+ }
+
+ pc += 4;
+ }
+
+ // Handle traps and interrupts.
+ if( trap!=0 )
+ return trap;
+
+ _cycle = cycle;
+ _pc = pc;
+ return 0;
+ }
+}
diff --git a/src/test/java/com/seaofnodes/simple/Main.java b/src/test/java/com/seaofnodes/simple/Main.java
index f3a6784c..589c043b 100644
--- a/src/test/java/com/seaofnodes/simple/Main.java
+++ b/src/test/java/com/seaofnodes/simple/Main.java
@@ -5,7 +5,6 @@ import java.nio.file.Files;
import java.nio.file.Path;
public class Main {
- public static final String PORTS = "com.seaofnodes.simple.node.cpus";
// Compile and run a simple program
public static void main( String[] args ) throws Exception {
// First arg is file, 2nd+ args are program args
diff --git a/src/test/java/com/seaofnodes/simple/MergeSortTest.java b/src/test/java/com/seaofnodes/simple/MergeSortTest.java
index c83fe089..a1004fb5 100644
--- a/src/test/java/com/seaofnodes/simple/MergeSortTest.java
+++ b/src/test/java/com/seaofnodes/simple/MergeSortTest.java
@@ -1,67 +1,44 @@
package com.seaofnodes.simple;
import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.node.cpus.riscv.riscv;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
public class MergeSortTest {
- @Test
- public void testMergeSort() {
- String src =
-"""
-// based on the top-down version from https://en.wikipedia.org/wiki/Merge_sort
-
-val merge_sort = { int[] a, int[] b, int n ->
- copy_array(a, 0, n, b);
- split_merge(a, 0, n, b);
-};
-
-val split_merge = { int[] b, int begin, int end, int[] a ->
- if (end - begin <= 1)
- return 0;
- int middle = (end + begin) / 2;
- split_merge(a, begin, middle, b);
- split_merge(a, middle, end, b);
- merge(b, begin, middle, end, a);
- return 0;
-};
-
-val merge = { int[] b, int begin, int middle, int end, int[] a ->
- int i = begin, j = middle;
-
- for (int k = begin; k < end; k++) {
- // && and ||
- bool cond = false;
- if (i < middle) {
- if (j >= end) cond = true;
- else if (a[i] <= a[j]) cond = true;
- }
- if (cond) b[k] = a[i++];
- else b[k] = a[j++];
+ @Test public void testMergeSort0() throws IOException {
+ int[] primes = new int[] { 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, };
+ SB sb = new SB().p(primes.length).p("[");
+ for( int prime : primes )
+ sb.p(prime).p(", ");
+ String sprimes = sb.p("]").toString();
+ TestC.run("sort", sprimes, 40);
+
+
+ EvalRisc5 R5 = TestRisc5.build("src/test/java/com/seaofnodes/simple/progs", "sort", "merge_sort", 0, 36, false);
+ // Allocate an array of primes, filled in reverse order
+ int ps = R5._heap;
+ R5._heap += (primes.length+1)*8; // 25 primes, and length
+ R5.st4(ps,primes.length);
+ for( int i=0; i<primes.length; i++ )
+ R5.st8(ps+(primes.length-1-i+1)*8,primes[i]);
+ // Pass array base when calling `merge_sort`
+ R5.regs[riscv.A0] = ps; // Array base to be sorted
+
+ int trap = R5.step(100000);
+ assertEquals(0,trap);
+
+ assertEquals(primes.length,R5.ld4s(ps));
+ for( int i=0; i<primes.length; i++ )
+ assertEquals(primes[i], R5.ld8(ps+(i+1)*8));
+
+ // TODO: replace with ARM Eval
+ String src = Files.readString(Path.of("src/test/java/com/seaofnodes/simple/progs/sort.smp"));
+ Chapter21Test.testCPU(src,"arm", "SystemV",36,null);
}
-};
-
-val copy_array = { int[] a, int begin, int end, int[] b ->
- for (int k = begin; k < end; k++)
- b[k] = a[k];
-};
-
-int[] !a = new int[arg];
-int[] !b = new int[a#];
-
-for (int i = 0; i < a#; i++)
- a[i] = a# - i;
-
-merge_sort(a, b, a#);
-
-return a;
-""";
- Chapter20Test.testCPU(src,"x86_64_v2", "SystemV",46,"Stop[ return mov(mov([int])); return 0; ]");
- Chapter20Test.testCPU(src,"riscv" , "SystemV",36,"Stop[ return mov(mov([int])); return 0; ]");
- Chapter20Test.testCPU(src,"arm" , "SystemV",37,"Stop[ return mov(mov([int])); return 0; ]");
-//assertEquals("int[ 1,2,3,4,5,6,7,8,9,10,11]", Eval2.eval(code, 11));
- }
-
}
diff --git a/src/test/java/com/seaofnodes/simple/Simple.java b/src/test/java/com/seaofnodes/simple/Simple.java
new file mode 100644
index 00000000..9b1e2827
--- /dev/null
+++ b/src/test/java/com/seaofnodes/simple/Simple.java
@@ -0,0 +1,313 @@
+package com.seaofnodes.simple;
+
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.print.*;
+
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.io.InputStreamReader;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.util.stream.Collectors;
+
+public class Simple {
+ public static final String PORTS = "com.seaofnodes.simple.node.cpus";
+
+ static final int DUMP_AFTER_PARSE = 1<<0;
+ static final int DUMP_AFTER_OPTO = 1<<1;
+ static final int DUMP_AFTER_TYPE_CHECK = 1<<2;
+ static final int DUMP_AFTER_LOOP_TREE = 1<<3;
+ static final int DUMP_AFTER_INSTR_SELECT = 1<<4;
+ static final int DUMP_AFTER_GCM = 1<<5;
+ static final int DUMP_AFTER_LOCAL_SCHED = 1<<6;
+ static final int DUMP_AFTER_REG_ALLOC = 1<<7;
+ static final int DUMP_AFTER_ENCODE = 1<<8;
+
+ static final int DUMP_FINAL = 1<<16;
+
+ static final int DUMP_DOT = 1<<29;
+ static final int DUMP_PASS_NAME = 1<<30;
+ static final int DUMP_AFTER_ALL = 0xff | DUMP_PASS_NAME;
+ static final int DUMP_DOT_AFTER_ALL = 0xff | DUMP_DOT;
+
+ static final int DUMP_DISASSEMBLE = 1<<31;
+
+ static String system_cpu = null;
+ static String system_abi = null;
+
+ static void usage() {
+ System.out.println(
+"""
+simple <input-file> [options] [--eval|--run ...]
+Options:
+ --dump - dump final intermediate representation
+ --dump-after-parse - dump intermediate representation after parse
+ --dump-after-opto - dump intermediate representation after opto pass
+ --dump-after-type-check - dump intermediate representation after type check pass
+ --dump-after-instr-select - dump intermediate representation after instrution selection pass
+ --dump-after-gcm - dump intermediate representation after GCM pass
+ --dump-after-local-sched - dump intermediate representation after local scheduling pass
+ --dump-after-reg-alloc - dump intermediate representation after register allocation pass
+ --dump-after-encode - dump intermediate representation after encoding pass
+ --dump-after-all - dump intermediate representation after all passes
+ --dot - dump grapical representation of intermediate code into *.dot file(s)
+ -S - dump generated assembler code
+ --eval ... - evaluate the compiled code in emulator
+ --run ... - run the compiled code natively
+ --dump-size - print the size of generated code
+ --dump-time - print compilation and execution times
+ --cpu <cpu-name> - use specific CPU (x86_64_v2, riscv, arm)
+ --abi <abi-name> - use speific ABI variant (SystemV)
+ --target - print native CPU and ABI
+ --version
+ --help
+"""
+);
+ System.exit(0);
+ }
+
+ static void bad_usage() {
+ System.err.println("ERROR: Invalid usage (use --help)");
+ System.exit(1);
+ }
+
+ static void target() {
+ if (system_cpu == null || system_abi == null) {
+ System.err.println("ERROR: Unknown target");
+ System.exit(1);
+ }
+ System.out.print(system_cpu);
+ System.out.print("-");
+ System.out.println(system_abi);
+ System.exit(0);
+ }
+
+ static void dump(CodeGen code, int dump, int pass) {
+ if ((dump & pass) != 0) {
+ if ((dump & DUMP_DOT) != 0) {
+ String fn = switch (pass) {
+ case DUMP_AFTER_PARSE -> "01-parse.dot";
+ case DUMP_AFTER_OPTO -> "02-opto.dot";
+ case DUMP_AFTER_TYPE_CHECK -> "03-type_check.dot";
+ case DUMP_AFTER_LOOP_TREE -> "04-loop_tree.dot";
+ case DUMP_AFTER_INSTR_SELECT -> "05-instr_select.dot";
+ case DUMP_AFTER_GCM -> "06-gcm.dot";
+ case DUMP_AFTER_LOCAL_SCHED -> "07-local_sched.dot";
+ case DUMP_AFTER_REG_ALLOC -> "08-reg_allos.dot";
+ case DUMP_AFTER_ENCODE -> "09-local_sched.dot";
+ case DUMP_FINAL -> "10-final.dot";
+ default -> throw Utils.TODO();
+ };
+
+ try {
+ Files.writeString(Path.of(fn),
+ new GraphVisualizer().generateDotOutput(code._stop, null, null));
+ } catch(IOException e) {
+ System.err.println("ERROR: Cannot write DOT file");
+ System.exit(1);
+ }
+ } else {
+ if ((dump & DUMP_PASS_NAME) != 0) {
+ System.err.println(switch (pass) {
+ case DUMP_AFTER_PARSE -> "After Parse:";
+ case DUMP_AFTER_OPTO -> "After OPTO:";
+ case DUMP_AFTER_TYPE_CHECK -> "After Type Check:";
+ case DUMP_AFTER_LOOP_TREE -> "After Loop Tree:";
+ case DUMP_AFTER_INSTR_SELECT -> "After Instruction Selection:";
+ case DUMP_AFTER_GCM -> "After GCM:";
+ case DUMP_AFTER_LOCAL_SCHED -> "After Local Scheduling:";
+ case DUMP_AFTER_REG_ALLOC -> "After Register Allocation:";
+ case DUMP_AFTER_ENCODE -> "After Instruction Encoding:";
+ case DUMP_FINAL -> "Final:";
+ default -> throw Utils.TODO();
+ });
+ }
+
+ System.err.println(IRPrinter.prettyPrint(code._stop, 9999));
+ }
+ }
+ }
+
+ static void print_compilation_times(CodeGen code) {
+ double t, total = 0;
+
+ total += t = code._tParse / 1e3;
+ System.out.println(String.format("Parsing Time: %.3f sec", t));
+ total += t = code._tOpto / 1e3;
+ System.out.println(String.format("Optimization Time: %.3f sec", t));
+ total += t = code._tTypeCheck / 1e3;
+ System.out.println(String.format("Type Checking Time: %.3f sec", t));
+ total += t = code._tLoopTree / 1e3;
+ System.out.println(String.format("Loop Tree Time: %.3f sec", t));
+ total += t = code._tInsSel / 1e3;
+ System.out.println(String.format("Instruction Selection Time: %.3f sec", t));
+ total += t = code._tGCM / 1e3;
+ System.out.println(String.format("GCM Time: %.3f sec", t));
+ total += t = code._tLocal / 1e3;
+ System.out.println(String.format("Local Scheduling Time: %.3f sec", t));
+ total += t = code._tRegAlloc / 1e3;
+ System.out.println(String.format("Register Allocation Time: %.3f sec", t));
+ total += t = code._tEncode / 1e3;
+ System.out.println(String.format("Instruction Encoding Time: %.3f sec", t));
+ System.out.println(String.format("TOTAL COMPILATION TIME: %.3f sec", total));
+ }
+
+ static String getInput() {
+ BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
+ return reader.lines().collect(Collectors.joining("\n"));
+ }
+
+ public static void main(String[] args) throws Exception {
+ String input_filename = null;
+ boolean use_stdin = false;
+ boolean do_eval = false;
+ boolean do_run = false;
+ boolean do_codegen = false;
+ boolean do_print_size = false;
+ boolean do_print_time = false;
+ int dump = 0;
+ int first_arg = 0;
+ String src = null;
+ String cpu = null;
+ String abi = null;
+
+ // TODO: autodetect
+ system_cpu = "x86_64_v2";
+ system_abi = "SystemV";
+
+ // Parse command line
+loop: for (int i = 0; i < args.length; i++) {
+ String arg = args[i];
+ if (arg.charAt(0) == '-') {
+ switch (arg) {
+ case "-": use_stdin = true; break;
+ case "--dump": dump |= DUMP_FINAL; break;
+ case "--dump-after-parse": dump |= DUMP_AFTER_PARSE; break;
+ case "--dump-after-opto": dump |= DUMP_AFTER_OPTO; break;
+ case "--dump-after-type-check": dump |= DUMP_AFTER_TYPE_CHECK; break;
+ case "--dump-after-loop-tree": dump |= DUMP_AFTER_LOOP_TREE; break;
+ case "--dump-after-instr-select": dump |= DUMP_AFTER_INSTR_SELECT; break;
+ case "--dump-after-gcm": dump |= DUMP_AFTER_GCM; break;
+ case "--dump-after-local-sched": dump |= DUMP_AFTER_LOCAL_SCHED; break;
+ case "--dump-after-reg-alloc": dump |= DUMP_AFTER_REG_ALLOC; break;
+ case "--dump-after-encode": dump |= DUMP_AFTER_ENCODE; break;
+ case "--dump-after-all": dump |= DUMP_AFTER_ALL; break;
+ case "--dot": dump |= DUMP_DOT; break;
+ case "-S": dump |= DUMP_DISASSEMBLE; break;
+ case "--eval": do_eval = true; first_arg = i + 1; break loop;
+ case "--run": do_run = true; first_arg = i + 1; break loop;
+ case "--dump-size": do_print_size = true; break;
+ case "--dump-time": do_print_time = true; break;
+ case "--cpu": if (cpu != null
+ || i + 1 >= args.length
+ || args[i + 1].charAt(0) == '-') bad_usage();
+ cpu = args[i + 1];
+ i++;
+ break;
+ case "--abi": if (abi != null
+ || i + 1 >= args.length
+ || args[i + 1].charAt(0) == '-') bad_usage();
+ abi = args[i + 1];
+ i++;
+ break;
+ case "--target": target();
+ case "--version": throw Utils.TODO();
+ case "--help": usage();
+ default: bad_usage();
+ }
+ } else {
+ if (input_filename != null || use_stdin) bad_usage();
+ input_filename = arg;
+ }
+ }
+
+ if (input_filename == null && !use_stdin) {
+ System.err.println("ERROR: no input file' (use --help)");
+ System.exit(1);
+ }
+
+ if (do_run || (dump & DUMP_DISASSEMBLE) != 0 || do_print_size) {
+ if (cpu == null) cpu = system_cpu;
+ if (abi == null) abi = system_abi;
+ if (cpu == null || abi == null) {
+ System.err.println("ERROR: Cannot compile for unknown target");
+ System.exit(1);
+ }
+ do_codegen = true;
+ }
+
+ // Read input file
+ try {
+ src = use_stdin ? getInput() : Files.readString(Path.of(input_filename));
+ } catch(IOException e) {
+ System.err.println("ERROR: Cannot read input file");
+ System.exit(1);
+ }
+
+ // Compilation pipeline
+ CodeGen code = new CodeGen(src);
+
+ code.parse();
+ dump(code, dump, DUMP_AFTER_PARSE);
+
+ code.opto();
+ dump(code, dump, DUMP_AFTER_OPTO);
+
+ code.typeCheck();
+ dump(code, dump, DUMP_AFTER_TYPE_CHECK);
+
+ code.loopTree();
+ dump(code, dump, DUMP_AFTER_LOOP_TREE);
+
+ if (do_codegen) {
+ code.instSelect(PORTS, cpu, abi);
+ dump(code, dump, DUMP_AFTER_INSTR_SELECT);
+ }
+
+ code.GCM();
+ dump(code, dump, DUMP_AFTER_GCM);
+
+ code.localSched();
+ dump(code, dump, DUMP_AFTER_LOCAL_SCHED);
+
+ if (do_codegen) {
+ code.regAlloc();
+ dump(code, dump, DUMP_AFTER_REG_ALLOC);
+
+ code.encode();
+ dump(code, dump, DUMP_AFTER_ENCODE);
+ }
+
+ dump(code, dump, DUMP_FINAL);
+
+ if (do_codegen && (dump & DUMP_DISASSEMBLE) != 0) {
+ System.out.println(code.asm());
+ }
+
+ if (do_print_size) {
+ System.out.println(String.format("Code Size: %d", code._encoding._bits.size()));
+ }
+
+ if (do_print_time) {
+ print_compilation_times(code);
+ }
+
+ if (do_eval) {
+ // TODO: Support for evaluation of functions with different argument numbers and types
+ long t = System.currentTimeMillis();
+ long arg = (first_arg < args.length) ? Integer.valueOf(args[first_arg]) : 0;
+ System.out.println(Eval2.eval(code, arg, 100000));
+ if (do_print_time) {
+ System.out.println(String.format("EXECUTION TIME: %.3f sec",
+ (System.currentTimeMillis() - t) / 1e3));
+ }
+ } else if (do_run) {
+ if (cpu != system_cpu || abi != system_abi) {
+ System.err.println("ERROR: cannot run code on not native target");
+ System.exit(1);
+ }
+
+ throw Utils.TODO();
+ }
+ }
+}
diff --git a/src/test/java/com/seaofnodes/simple/TestArm64.java b/src/test/java/com/seaofnodes/simple/TestArm64.java
new file mode 100644
index 00000000..5d7d1361
--- /dev/null
+++ b/src/test/java/com/seaofnodes/simple/TestArm64.java
@@ -0,0 +1,42 @@
+package com.seaofnodes.simple;
+
+
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.node.cpus.arm.arm;
+import junit.framework.Assert;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.io.IOException;
+
+import static org.junit.Assert.assertEquals;
+
+// Runs 32-bit ARM code in an emulator
+
+public class TestArm64 {
+ public static EvalArm64 build( String file, int arg, int spills, boolean print ) throws IOException {
+ return build("src/test/java/com/seaofnodes/simple/progs",file, arg, spills, print);
+ }
+
+ // Compile and run a simple program
+ public static EvalArm64 build( String dir, String file, int arg, int spills, boolean print ) throws IOException {
+ // Compile and export Simple
+ String src = Files.readString(Path.of(dir+"/"+file+".smp"));
+ CodeGen code = new CodeGen(src).driver("arm", "SystemV",null);
+ if( print ) { code.print_as_hex(); System.out.print(code.asm()); }
+
+ // Allocation quality not degraded
+ int delta = spills>>3;
+ if( delta == 0 ) delta =1;
+ if( spills != -1 ) assertEquals("Expect spills:", spills, code._regAlloc._spillScaled, delta);
+
+ // Image
+ byte[] image = new byte[1<<20]; // A megabyte (1024*1024 bytes)
+ EvalArm64 ARM = new EvalArm64(image, 1<<16);
+ // Code at offset 0
+ System.arraycopy(code._encoding.bits(), 0, image, 0, code._encoding.bits().length);
+ // Initial incoming int arg
+ ARM.regs[arm.X0] = arg;
+ // malloc memory starts at 64K and goes up
+ return ARM;
+ }
+}
diff --git a/src/test/java/com/seaofnodes/simple/TestC.java b/src/test/java/com/seaofnodes/simple/TestC.java
new file mode 100644
index 00000000..5609758b
--- /dev/null
+++ b/src/test/java/com/seaofnodes/simple/TestC.java
@@ -0,0 +1,89 @@
+package com.seaofnodes.simple;
+
+import com.seaofnodes.simple.codegen.CodeGen;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.io.InputStream;
+import java.io.IOException;
+import static org.junit.Assert.*;
+
+// Call a C driver program, which calls a Simple-generated .o, and self-checks.
+// Expects GCC from the default CLI, compiles C, compiles Simple, links and runs.
+public abstract class TestC {
+
+ public static final String OS = System.getProperty("os.name");
+ public static final String CPU = System.getProperty("os.arch");
+
+ public static final String CALL_CONVENTION = OS.startsWith("Windows") ? "Win64" : "SystemV";
+ public static final String CPU_PORT = switch( CPU ) {
+ case "amd64" -> "x86_64_v2";
+ default -> throw Utils.TODO("Map Yer CPU Port Here");
+ };
+
+ public static void run( String file, int spills ) throws IOException { run(file,"",spills); }
+
+ public static void run( String file, String expected, int spills ) throws IOException {
+ run("src/test/java/com/seaofnodes/simple/progs",file,expected,spills);
+ }
+
+ // Compile and run a simple program
+ public static void run( String dir, String file, String expected, int spills ) throws IOException {
+ // Files
+ String cfile = dir+"/"+file+".c" ;
+ String sfile = dir+"/"+file+".smp";
+ String efile = "build/objs/"+file;
+
+ // Compile and export Simple
+ String src = Files.readString(Path.of(sfile));
+ _run(src,CALL_CONVENTION,"",cfile,efile,"S",expected,spills);
+ }
+
+ static void _run( String src, String simple_conv, String c_conv, String cfile, String efile, String xtn, String expected, int spills ) throws IOException {
+ String bin = efile+xtn;
+ String obj = bin+".o";
+ // Compile simple, emit ELF
+ CodeGen code = new CodeGen(src).driver( CPU_PORT, simple_conv, obj);
+
+ // Compile the C program
+ var params = new String[] {
+ //if (USE_WSL) "wsl.exe";
+ "gcc",
+ cfile,
+ obj,
+ "-lm", // Picks up 'sqrt' for newtonFloat tests to compare
+ "-g",
+ "-o",
+ bin,
+ "-D",
+ "CALL_CONV="+c_conv,
+ };
+ Process gcc = new ProcessBuilder(params).redirectErrorStream(true).start();
+ byte error;
+ try { error = (byte)gcc.waitFor(); } catch( InterruptedException e ) { throw new IOException("interrupted"); }
+ String result = new String(gcc.getInputStream().readAllBytes());
+ if( error!=0 ) {
+ System.err.println("gcc error code: "+error);
+ System.err.println(result);
+ }
+ assertEquals( 0, error );
+ //assertTrue(result.isEmpty()); // No data in error stream
+
+ // Execute results
+ Process smp = new ProcessBuilder(bin).redirectErrorStream(true).start();
+ try { error = (byte)smp.waitFor(); } catch( InterruptedException e ) { throw new IOException("interrupted"); }
+ result = new String(smp.getInputStream().readAllBytes());
+ if( error!=0 ) {
+ System.err.println("exec error code: "+error);
+ System.err.println(result);
+ }
+ assertEquals( 0, error );
+ assertEquals(expected,result);
+
+ // Allocation quality not degraded
+ int delta = spills>>3;
+ if( delta==0 ) delta = 1;
+ if( spills != -1 )
+ assertEquals("Expect spills:",spills,code._regAlloc._spillScaled,delta);
+
+ }
+}
diff --git a/src/test/java/com/seaofnodes/simple/TestRisc5.java b/src/test/java/com/seaofnodes/simple/TestRisc5.java
new file mode 100644
index 00000000..041ed833
--- /dev/null
+++ b/src/test/java/com/seaofnodes/simple/TestRisc5.java
@@ -0,0 +1,50 @@
+package com.seaofnodes.simple;
+
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.node.FunNode;
+import com.seaofnodes.simple.node.Node;
+import com.seaofnodes.simple.node.cpus.riscv.riscv;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import static org.junit.Assert.*;
+
+// Runs 32-bit R5 code in an emulator
+public abstract class TestRisc5 {
+
+ public static EvalRisc5 build( String file, int arg, int spills, boolean print ) throws IOException {
+ return build("src/test/java/com/seaofnodes/simple/progs",file, null, arg, spills, print);
+ }
+
+ // Compile and run a simple program
+ public static EvalRisc5 build( String dir, String file, String main, int arg, int spills, boolean print ) throws IOException {
+ // Compile and export Simple
+ String src = Files.readString(Path.of(dir+"/"+file+".smp"));
+ CodeGen code = new CodeGen(src).driver("riscv", "SystemV",null);
+ if( print ) { code.print_as_hex(); System.out.print(code.asm()); }
+
+ // Allocation quality not degraded
+ int delta = spills>>3;
+ if( delta==0 ) delta = 1;
+ if( spills != -1 )
+ assertEquals("Expect spills:",spills,code._regAlloc._spillScaled,delta);
+
+ // Image
+ byte[] image = new byte[1<<20]; // A megabyte (1024*1024 bytes)
+ EvalRisc5 R5 = new EvalRisc5(image, 1<<16);
+ // Code at offset 0
+ System.arraycopy(code._encoding.bits(), 0, image, 0, code._encoding.bits().length);
+ // Initial incoming int arg
+ R5.regs[riscv.A0] = arg;
+ // malloc memory starts at 64K and goes up
+
+ // Look up starting PC or zero if not given
+ if( main!=null )
+ for( Node use : code._start._outputs )
+ if( use instanceof FunNode fun && main.equals(fun._name) )
+ R5._pc = code._encoding._opStart[fun._nid];
+
+ return R5;
+ }
+
+}
diff --git a/src/test/java/com/seaofnodes/simple/evaluator/Scheduler.java b/src/test/java/com/seaofnodes/simple/evaluator/Scheduler.java
index 4bfa7628..7ba9272f 100644
--- a/src/test/java/com/seaofnodes/simple/evaluator/Scheduler.java
+++ b/src/test/java/com/seaofnodes/simple/evaluator/Scheduler.java
@@ -248,20 +248,20 @@ public class Scheduler {
assert data.block != null;
assert data.users == 0;
- if (data.node instanceof LoadNode l) {
+ if (data.node instanceof MemOpNode l && l._isLoad) {
assert isValid(data);
// Handle anti-deps of load nodes.
// At this point all anti-dep nodes are scheduled,
// but they did not refine the placement
// so do that now.
- var mem = l.in(1);
+ var mem = data.node.in(1);
for(var out : mem._outputs) {
if (out instanceof PhiNode p) {
var r = p.in(0);
for (int i = 1; i < p.nIns(); i++) {
if (p.in(i) == mem) optionalRefinePlacement(data, r.in(i));
}
- } else if (!(out instanceof LoadNode)) {
+ } else if (!(out instanceof MemOpNode ld && ld._isLoad)) {
optionalRefinePlacement(data, out);
}
}
@@ -273,11 +273,11 @@ public class Scheduler {
for(var in:data.node._inputs) {
if (in!=null) update(d(in), data.block);
}
- if (data.node instanceof StoreNode s) {
+ if (data.node instanceof MemOpNode s && !s._isLoad) {
// Store nodes have anti-deps to load nodes.
// So decrease the uses of these loads when the store is placed.
- for (var out: s.in(1)._outputs) {
- if (out instanceof LoadNode) od(out).ifPresent(this::decUsers);
+ for (var out: ((Node)s).in(1)._outputs) {
+ if (out instanceof MemOpNode ld && ld._isLoad) od(out).ifPresent(this::decUsers);
}
}
}
@@ -471,14 +471,14 @@ public class Scheduler {
} else {
for (var in : node._inputs) if (in != null && !(in instanceof CFGNode)) markAlive(dataQueue, in, false);
}
- if (node instanceof StoreNode) mem.push(data);
+ if (node instanceof MemOpNode s && !s._isLoad) mem.push(data);
}
// Handle store nodes and increase load with an anti-dep to the store.
while (!mem.isEmpty()) {
var data = mem.pop();
node = data.node;
for(var out:node.in(1)._outputs) {
- if (out instanceof LoadNode) od(out).ifPresent(d->d.users++);
+ if (out instanceof MemOpNode ld && ld._isLoad) od(out).ifPresent(d->d.users++);
}
}
}
diff --git a/src/test/java/com/seaofnodes/simple/fuzzer/FuzzerUtils.java b/src/test/java/com/seaofnodes/simple/fuzzer/FuzzerUtils.java
index 669a1e6a..b90b663d 100644
--- a/src/test/java/com/seaofnodes/simple/fuzzer/FuzzerUtils.java
+++ b/src/test/java/com/seaofnodes/simple/fuzzer/FuzzerUtils.java
@@ -101,7 +101,7 @@ class FuzzerUtils {
System.setErr(NULL_PRINT_STREAM);
System.setOut(NULL_PRINT_STREAM);
var code = new CodeGen(script, TypeInteger.BOT, workListSeed);
- return code.parse().opto().typeCheck().GCM().localSched();
+ return code.driver(CodeGen.Phase.LocalSched);
} finally {
System.setErr(err);
System.setOut(out);
diff --git a/src/test/java/com/seaofnodes/simple/progs/antiDep1.smp b/src/test/java/com/seaofnodes/simple/progs/antiDep1.smp
new file mode 100644
index 00000000..b18dfd16
--- /dev/null
+++ b/src/test/java/com/seaofnodes/simple/progs/antiDep1.smp
@@ -0,0 +1,11 @@
+// -*- mode: java; -*-
+struct S { int f; };
+var v0 = new S;
+S? v1;
+if (arg) v1 = new S;
+if (v1) {
+ v0.f = v1.f;
+} else {
+ v0.f = 2;
+}
+return v0;
diff --git a/src/test/java/com/seaofnodes/simple/progs/arg_count.c b/src/test/java/com/seaofnodes/simple/progs/arg_count.c
new file mode 100644
index 00000000..e247e28a
--- /dev/null
+++ b/src/test/java/com/seaofnodes/simple/progs/arg_count.c
@@ -0,0 +1,10 @@
+#include <stdio.h>
+
+__attribute__ ((CALL_CONV))
+extern double addAll( long long i0, double f1, long long i2, double f3, long long i4, double f5, long long i6, double f7, long long i8, double f9, long long i10, double f11, long long i12, double f13, long long i14, double f15, long long i16, double f17, long long i18, double f19 );
+
+int main() {
+ double result = addAll( 0, 1.1, 2, 3.1, 4, 5.1, 6, 7.1, 8, 9.1, 10, 11.1, 12, 13.1, 14, 15.1, 16, 17.1, 18, 19.1 );
+ printf("%f\n", result);
+ return 0;
+}
diff --git a/src/test/java/com/seaofnodes/simple/progs/arg_count.smp b/src/test/java/com/seaofnodes/simple/progs/arg_count.smp
new file mode 100644
index 00000000..6b6bc0ae
--- /dev/null
+++ b/src/test/java/com/seaofnodes/simple/progs/arg_count.smp
@@ -0,0 +1,6 @@
+// -*- mode: java; -*-
+val addAll = { int i0, flt f1, int i2, flt f3, int i4, flt f5, int i6, flt f7, int x8, flt f9, int i10, flt f11, int i12, flt f13, int i14, flt f15, int x16, flt f17 int x18, flt f19 ->
+ return
+ i0 + f1+ i2+ f3+ i4+ f5+ i6+ f7+ x8 +f9 +
+ i10+f11+i12+f13+i14+f15+x16+f17+x18+f19 ;
+};
diff --git a/src/test/java/com/seaofnodes/simple/progs/array1.smp b/src/test/java/com/seaofnodes/simple/progs/array1.smp
new file mode 100644
index 00000000..6b10f92c
--- /dev/null
+++ b/src/test/java/com/seaofnodes/simple/progs/array1.smp
@@ -0,0 +1,9 @@
+// -*- mode: java; -*-
+int[] !ary = new int[arg];
+// Fill [0,1,2,3,4,...]
+for( int i=0; i<ary#; i++ )
+ ary[i] = i;
+// Fill [0,1,3,6,10,...]
+for( int i=0; i<ary#-1; i++ )
+ ary[i+1] += ary[i];
+return ary[1] * 1000 + ary[3]; // 1 * 1000 + 6
diff --git a/src/test/java/com/seaofnodes/simple/progs/brain_fuck.c b/src/test/java/com/seaofnodes/simple/progs/brain_fuck.c
new file mode 100644
index 00000000..127cbe73
--- /dev/null
+++ b/src/test/java/com/seaofnodes/simple/progs/brain_fuck.c
@@ -0,0 +1,18 @@
+#include <stdio.h>
+#include <stdbool.h>
+
+typedef struct AryInt {
+ unsigned int len;
+ unsigned char data[];
+} AryInt;
+
+__attribute__ ((CALL_CONV))
+extern AryInt* brain_fuck();
+
+int main() {
+ AryInt* ps = brain_fuck();
+
+ printf("%.*s", ps->len, ps->data);
+
+ return 0;
+}
diff --git a/src/test/java/com/seaofnodes/simple/progs/brain_fuck.smp b/src/test/java/com/seaofnodes/simple/progs/brain_fuck.smp
new file mode 100644
index 00000000..ed90cb84
--- /dev/null
+++ b/src/test/java/com/seaofnodes/simple/progs/brain_fuck.smp
@@ -0,0 +1,46 @@
+// -*- mode: java; -*-
+val brain_fuck = { ->
+ u8[] !program = new u8[106];program[0] = 43;program[1] = 43;program[2] = 43;program[3] = 43;program[4] = 43;program[5] = 43;program[6] = 43;program[7] = 43;program[8] = 91;program[9] = 62;program[10] = 43;program[11] = 43;program[12] = 43;program[13] = 43;program[14] = 91;program[15] = 62;program[16] = 43;program[17] = 43;program[18] = 62;program[19] = 43;program[20] = 43;program[21] = 43;program[22] = 62;program[23] = 43;program[24] = 43;program[25] = 43;program[26] = 62;program[27] = 43;program[28] = 60;program[29] = 60;program[30] = 60;program[31] = 60;program[32] = 45;program[33] = 93;program[34] = 62;program[35] = 43;program[36] = 62;program[37] = 43;program[38] = 62;program[39] = 45;program[40] = 62;program[41] = 62;program[42] = 43;program[43] = 91;program[44] = 60;program[45] = 93;program[46] = 60;program[47] = 45;program[48] = 93;program[49] = 62;program[50] = 62;program[51] = 46;program[52] = 62;program[53] = 45;program[54] = 45;program[55] = 45;program[56] = 46;program[57] = 43;program[58] = 43;program[59] = 43;program[60] = 43;program[61] = 43;program[62] = 43;program[63] = 43;program[64] = 46;program[65] = 46;program[66] = 43;program[67] = 43;program[68] = 43;program[69] = 46;program[70] = 62;program[71] = 62;program[72] = 46;program[73] = 60;program[74] = 45;program[75] = 46;program[76] = 60;program[77] = 46;program[78] = 43;program[79] = 43;program[80] = 43;program[81] = 46;program[82] = 45;program[83] = 45;program[84] = 45;program[85] = 45;program[86] = 45;program[87] = 45;program[88] = 46;program[89] = 45;program[90] = 45;program[91] = 45;program[92] = 45;program[93] = 45;program[94] = 45;program[95] = 45;program[96] = 45;program[97] = 46;program[98] = 62;program[99] = 62;program[100] = 43;program[101] = 46;program[102] = 62;program[103] = 43;program[104] = 43;program[105] = 46;
+ int d = 0;
+ u8[] !output = new u8[0];
+ u8[] !data = new u8[100];
+
+ for( int pc = 0; pc < program#; pc++ ) {
+ var command = program[pc];
+ if (command == 62) {
+ d++;
+ } else if (command == 60) {
+ d--;
+ } else if (command == 43) {
+ data[d]++;
+ } else if (command == 45) {
+ data[d]--;
+ } else if (command == 46) {
+ // Output a byte; increase the output array size
+ var old = output;
+ output = new u8[output# + 1];
+ for( int i = 0; i < old#; i++ )
+ output[i] = old[i];
+ output[old#] = data[d]; // Add the extra byte on the end
+ } else if (command == 44) {
+ data[d] = 42;
+ } else if (command == 91) {
+ if (data[d] == 0) {
+ for( int d = 1; d > 0; ) {
+ command = program[++pc];
+ if (command == 91) d++;
+ if (command == 93) d--;
+ }
+ }
+ } else if (command == 93) {
+ if (data[d]) {
+ for( int d = 1; d > 0; ) {
+ command = program[--pc];
+ if (command == 93) d++;
+ if (command == 91) d--;
+ }
+ }
+ }
+ }
+ return output;
+};
diff --git a/src/test/java/com/seaofnodes/simple/progs/fcn.smp b/src/test/java/com/seaofnodes/simple/progs/fcn.smp
deleted file mode 100644
index 188e0a9f..00000000
--- a/src/test/java/com/seaofnodes/simple/progs/fcn.smp
+++ /dev/null
@@ -1,15 +0,0 @@
-
-struct Person {
- int age;
-};
-
-val fcn = { Person?[] ps, int x ->
- val tmp = ps[x];
- if( ps[x] )
- ps[x].age++;
-};
-
-Person?[] !ps = new Person?[2];
-ps[0] = new Person;
-ps[1] = new Person;
-fcn(ps,1);
diff --git a/src/test/java/com/seaofnodes/simple/progs/fcn2.smp b/src/test/java/com/seaofnodes/simple/progs/fcn2.smp
deleted file mode 100644
index 049132d9..00000000
--- a/src/test/java/com/seaofnodes/simple/progs/fcn2.smp
+++ /dev/null
@@ -1,5 +0,0 @@
-
-val fcn = (arg&1)
- ? { int x -> x*x; }
- : { int x -> x+x; };
-fcn(arg);
diff --git a/src/test/java/com/seaofnodes/simple/progs/fcn3.smp b/src/test/java/com/seaofnodes/simple/progs/fcn3.smp
deleted file mode 100644
index a64564f0..00000000
--- a/src/test/java/com/seaofnodes/simple/progs/fcn3.smp
+++ /dev/null
@@ -1,42 +0,0 @@
-
-//val fact = { int n -> n <= 1 ? 1 : fact(n-1)*n; }
-struct LL { LL? next; int payload; };
-
-
-struct String {
-
- val _hashCodeString = { ->
- int hash=0;
- for( int i=0; i< cs#; i++ )
- hash = hash*31 + cs[i];
- if( !hash ) hash = 123456789;
- return hash;
- };
-
- val equals = { String s ->
- if( self == s ) return true;
- if( cs# != s.cs# ) return false;
- for( int i=0; i< #; i++ )
- if( [i] != s.cs[i] )
- return false;
- return true;
- };
-
- int _hashCode = hash();
- u8[] cs;
-};
-
-
-
-val hashCode= { String self ->
- int hash = self._hashCode
- ? self._hashCode
- : self._hashCode = _hashCodeString(self);
- return hash;
-};
-
-
-String s = new String { cs = new u8[17]; };
-s.cs[0] = 67; // C
-s.cs[1] = 108; // l
-hashCode(s);
diff --git a/src/test/java/com/seaofnodes/simple/progs/fib.c b/src/test/java/com/seaofnodes/simple/progs/fib.c
new file mode 100644
index 00000000..263d2f13
--- /dev/null
+++ b/src/test/java/com/seaofnodes/simple/progs/fib.c
@@ -0,0 +1,16 @@
+#include <stdio.h>
+#include <stdbool.h>
+
+
+__attribute__ ((CALL_CONV))
+extern int fib(int x);
+
+int main() {
+ printf("[");
+ for (int i = 0; i < 10; i++) {
+ printf("%d", fib(i));
+ if (i < 9) printf(", ");
+ }
+ printf("]");
+ return 0;
+} \ No newline at end of file
diff --git a/src/test/java/com/seaofnodes/simple/progs/fib.smp b/src/test/java/com/seaofnodes/simple/progs/fib.smp
new file mode 100644
index 00000000..1512541b
--- /dev/null
+++ b/src/test/java/com/seaofnodes/simple/progs/fib.smp
@@ -0,0 +1,11 @@
+// -*- mode: java; -*-
+val fib = {int n ->
+ int f1=1;
+ int f2=1;
+ while( n-- > 1 ){
+ int temp = f1+f2;
+ f1=f2;
+ f2=temp;
+ }
+ return f2;
+}; \ No newline at end of file
diff --git a/src/test/java/com/seaofnodes/simple/progs/jig.smp b/src/test/java/com/seaofnodes/simple/progs/jig.smp
new file mode 100644
index 00000000..1daeee09
--- /dev/null
+++ b/src/test/java/com/seaofnodes/simple/progs/jig.smp
@@ -0,0 +1,2 @@
+// -*- mode: java; -*-
+return 0;
diff --git a/src/test/java/com/seaofnodes/simple/progs/loop2.c b/src/test/java/com/seaofnodes/simple/progs/loop2.c
new file mode 100644
index 00000000..934f2d89
--- /dev/null
+++ b/src/test/java/com/seaofnodes/simple/progs/loop2.c
@@ -0,0 +1,7 @@
+
+__attribute__ ((CALL_CONV))
+extern int loop2();
+
+int main() {
+ return loop2();
+}
diff --git a/src/test/java/com/seaofnodes/simple/progs/loop2.smp b/src/test/java/com/seaofnodes/simple/progs/loop2.smp
new file mode 100644
index 00000000..01ec827e
--- /dev/null
+++ b/src/test/java/com/seaofnodes/simple/progs/loop2.smp
@@ -0,0 +1,10 @@
+// -*- mode: java; -*-
+int i = 0;
+while(true) {
+ i += 1;
+ if( i==0 ) continue;
+ if( i==1 ) continue;
+ if( i==2 ) break;
+ continue;
+}
+return i;
diff --git a/src/test/java/com/seaofnodes/simple/progs/newtonFloat.c b/src/test/java/com/seaofnodes/simple/progs/newtonFloat.c
new file mode 100644
index 00000000..7e7e796d
--- /dev/null
+++ b/src/test/java/com/seaofnodes/simple/progs/newtonFloat.c
@@ -0,0 +1,16 @@
+#include <stdio.h>
+#include <math.h>
+#include <stdlib.h>
+
+extern double test_sqrt(double);
+
+int main( int argc, char** argv ) {
+ for( int i=0; i<10; i++ ) {
+ double d = test_sqrt((double)i), expect = sqrt((double)i);
+ double delta = fabs(d-expect);
+ printf("%d %f (%g)\n",i,d,delta);
+ if( delta > 1e-15 )
+ return 1;
+ }
+ return 0;
+}
diff --git a/src/test/java/com/seaofnodes/simple/progs/newtonFloat.smp b/src/test/java/com/seaofnodes/simple/progs/newtonFloat.smp
new file mode 100644
index 00000000..be2dffec
--- /dev/null
+++ b/src/test/java/com/seaofnodes/simple/progs/newtonFloat.smp
@@ -0,0 +1,12 @@
+// -*- mode: java; -*-
+// Newtons approximation to the square root
+val test_sqrt = { flt x ->
+ flt epsilon = 1e-15;
+ flt guess = x;
+ while( 1 ) {
+ flt next = (x/guess + guess)/2;
+ if( guess-epsilon <= next & next <= guess+epsilon ) return guess;
+ //if( guess==next ) return guess;
+ guess = next;
+ }
+};
diff --git a/src/test/java/com/seaofnodes/simple/progs/no_stack_arg_count.smp b/src/test/java/com/seaofnodes/simple/progs/no_stack_arg_count.smp
new file mode 100644
index 00000000..20a05445
--- /dev/null
+++ b/src/test/java/com/seaofnodes/simple/progs/no_stack_arg_count.smp
@@ -0,0 +1,6 @@
+// -*- mode: java; -*-
+val addAll = { int i0, flt f1, int i2, flt f3, int i4, flt f5, int i6, flt f7, int x8, flt f9, int i10, flt f11, int i12, flt f13, int i14, flt f15 ->
+ return
+ i0 + f1+ i2+ f3+ i4+ f5+ i6+ f7+ x8 +f9 +
+ i10+f11+i12+f13+i14+f15;
+};
diff --git a/src/test/java/com/seaofnodes/simple/progs/person.c b/src/test/java/com/seaofnodes/simple/progs/person.c
new file mode 100644
index 00000000..df95e1ca
--- /dev/null
+++ b/src/test/java/com/seaofnodes/simple/progs/person.c
@@ -0,0 +1,34 @@
+#include <stdio.h>
+#include <stdbool.h>
+#include <stdlib.h>
+
+typedef struct Person {
+ int age;
+} Person;
+
+typedef struct PersonArray {
+ unsigned int length;
+ int pad;
+ Person* data[];
+} PersonArray;
+
+__attribute__ ((CALL_CONV))
+extern void fcn(PersonArray* people, int index);
+
+int main() {
+ Person base[5] = {{25},{1},{18},{45},{60}};
+ unsigned char buf[8+8*5];
+ PersonArray* people_array = (PersonArray*)buf;
+ people_array->length=5;
+ for (int i=0; i<people_array->length; i++) people_array->data[i] = &base[i];
+
+ fcn(people_array, 1);
+ fcn(people_array, 1);
+ fcn(people_array, 1);
+ fcn(people_array, 1);
+ fcn(people_array, 1);
+
+ printf("%d\n", people_array->data[1]->age); // expected: 6
+
+ return 0;
+} \ No newline at end of file
diff --git a/src/test/java/com/seaofnodes/simple/progs/person.smp b/src/test/java/com/seaofnodes/simple/progs/person.smp
new file mode 100644
index 00000000..9f0f5bea
--- /dev/null
+++ b/src/test/java/com/seaofnodes/simple/progs/person.smp
@@ -0,0 +1,9 @@
+// -*-java-*-
+struct Person {
+ int age;
+};
+
+val fcn = { Person?[] ps, int x ->
+ if( ps[x] )
+ ps[x].age++;
+}; \ No newline at end of file
diff --git a/src/test/java/com/seaofnodes/simple/progs/sieve.c b/src/test/java/com/seaofnodes/simple/progs/sieve.c
new file mode 100644
index 00000000..cac4cfa3
--- /dev/null
+++ b/src/test/java/com/seaofnodes/simple/progs/sieve.c
@@ -0,0 +1,19 @@
+#include <stdio.h>
+#include <stdbool.h>
+
+typedef struct AryInt {
+ unsigned int len;
+ unsigned int data[];
+} AryInt;
+
+__attribute__ ((CALL_CONV))
+extern AryInt *sieve(int N);
+
+int main() {
+ AryInt* ps = sieve(100);
+ printf("%d[",ps->len);
+ for( int i=0; i<ps->len; i++ )
+ printf("%d, ",ps->data[i]);
+ printf("]");
+ return 0;
+}
diff --git a/src/test/java/com/seaofnodes/simple/progs/sieve.smp b/src/test/java/com/seaofnodes/simple/progs/sieve.smp
index 0788e6c5..569dbb96 100644
--- a/src/test/java/com/seaofnodes/simple/progs/sieve.smp
+++ b/src/test/java/com/seaofnodes/simple/progs/sieve.smp
@@ -1,32 +1,31 @@
-
-int[] !ary = new int[arg], !primes = new int[arg];
-int nprimes = 0, p=2;
-// Find primes while p^2 < arg
-while( p*p < arg ) {
- // skip marked non-primes
- while( ary[p]==1 ) p = p + 1;
- // p is now a prime
- primes[nprimes] = p; nprimes = nprimes + 1;
- // Mark out the rest non-primes
- int i = p + p;
- while( i < ary# ) {
- ary[i] = 1;
- i = i + p;
- }
- p = p + 1;
-}
-// Now just collect the remaining primes, no more marking
-while( p < arg ) {
- if( ary[p] == 0 ) {
- primes[nprimes] = p; nprimes = nprimes + 1;
+// -*- mode: java; -*-
+val sieve = { int N ->
+ // The main Sieve array
+ bool[] !ary = new bool[N];
+ // The primes less than N
+ u32[] !primes = new u32[N>>1];
+ // Number of primes so far, searching at index p
+ int nprimes = 0, p=2;
+ // Find primes while p^2 < N
+ while( p*p < N ) {
+ // skip marked non-primes
+ while( ary[p] ) p++;
+ // p is now a prime
+ primes[nprimes++] = p;
+ // Mark out the rest non-primes
+ for( int i = p + p; i < ary#; i+= p )
+ ary[i] = true;
+ p++;
}
- p = p + 1;
-}
-// Copy/shrink the result array
-int[] !rez = new int[nprimes];
-int j = 0;
-while( j < nprimes ) {
- rez[j] = primes[j];
- j = j + 1;
-}
-return rez;
+
+ // Now just collect the remaining primes, no more marking
+ for( ; p < N; p++ )
+ if( !ary[p] )
+ primes[nprimes++] = p;
+
+ // Copy/shrink the result array
+ u32[] !rez = new u32[nprimes];
+ for( int j=0; j < nprimes; j++ )
+ rez[j] = primes[j];
+ return rez;
+};
diff --git a/src/test/java/com/seaofnodes/simple/progs/sort.c b/src/test/java/com/seaofnodes/simple/progs/sort.c
new file mode 100644
index 00000000..ad76cc7f
--- /dev/null
+++ b/src/test/java/com/seaofnodes/simple/progs/sort.c
@@ -0,0 +1,28 @@
+#include <stdio.h>
+#include <stdlib.h>
+
+// Test wrapper to call `sort.smp`
+
+typedef struct AryInt {
+ unsigned int len;
+ long long data[];
+} AryInt;
+
+__attribute__ ((CALL_CONV))
+extern AryInt* merge_sort(AryInt*);
+
+
+long long primes[] = { 25, 97, 89, 83, 79, 73, 71, 67, 61, 59, 53, 47, 43, 41, 37, 31, 29, 23, 19, 17, 13, 11, 7, 5, 3, 2 };
+
+int main() {
+ AryInt *as = (AryInt*)primes;
+
+ merge_sort(as);
+
+ printf("%d[",as->len);
+ for( int i=0; i<as->len; i++ )
+ printf("%d, ",as->data[i]);
+ printf("]");
+
+ return 0;
+} \ No newline at end of file
diff --git a/src/test/java/com/seaofnodes/simple/progs/sort.smp b/src/test/java/com/seaofnodes/simple/progs/sort.smp
new file mode 100644
index 00000000..ae3f4f23
--- /dev/null
+++ b/src/test/java/com/seaofnodes/simple/progs/sort.smp
@@ -0,0 +1,38 @@
+// -*- mode: java; -*-
+// based on the top-down version from https://en.wikipedia.org/wiki/Merge_sort
+
+val merge_sort = { int[] a ->
+ int[] !b = new int[a#];
+ copy_array (a, 0, a#, b);
+ split_merge(a, 0, a#, b);
+};
+
+val split_merge = { int[] b, int begin, int end, int[] a ->
+ if (end - begin <= 1)
+ return 0;
+ int middle = (end + begin) / 2;
+ split_merge(a, begin, middle, b);
+ split_merge(a, middle, end, b);
+ merge(b, begin, middle, end, a);
+ return 0;
+};
+
+val merge = { int[] b, int begin, int middle, int end, int[] a ->
+ int i = begin, j = middle;
+
+ for (int k = begin; k < end; k++) {
+ // && and ||
+ bool cond = false;
+ if (i < middle) {
+ if (j >= end) cond = true;
+ else if (a[i] <= a[j]) cond = true;
+ }
+ if (cond) b[k] = a[i++];
+ else b[k] = a[j++];
+ }
+};
+
+val copy_array = { int[] a, int begin, int end, int[] b ->
+ for (int k = begin; k < end; k++)
+ b[k] = a[k];
+};
diff --git a/src/test/java/com/seaofnodes/simple/progs/stringHash.c b/src/test/java/com/seaofnodes/simple/progs/stringHash.c
new file mode 100644
index 00000000..2c6d0fa9
--- /dev/null
+++ b/src/test/java/com/seaofnodes/simple/progs/stringHash.c
@@ -0,0 +1,30 @@
+#include <stdio.h>
+#include <stdbool.h>
+
+typedef struct ArrayU8 {
+ unsigned int length;
+ unsigned char data[];
+} ArrayU8;
+typedef struct String {
+ ArrayU8 * cs;
+ long long _hashCode;
+} String;
+
+
+__attribute__ ((CALL_CONV))
+extern bool equals(String*st1, String*st2);
+
+int main() {
+ String test1 = {.cs=(ArrayU8*)"\x04\x00\x00\x00test"};
+ String test2 = {.cs=(ArrayU8*)"\x05\x00\x00\x00test1"};
+
+ if( equals(&test1, &test2) ) {
+ printf("Expected test1 and test2 unequal");
+ return 1;
+ }
+ if( !equals(&test2, &test2) ) {
+ printf("Expected test2 and test2 equal");
+ return 1;
+ }
+ return 0;
+}
diff --git a/src/test/java/com/seaofnodes/simple/progs/stringHash.smp b/src/test/java/com/seaofnodes/simple/progs/stringHash.smp
new file mode 100644
index 00000000..dafa7456
--- /dev/null
+++ b/src/test/java/com/seaofnodes/simple/progs/stringHash.smp
@@ -0,0 +1,31 @@
+// -*- mode: java; -*-
+struct String {
+ u8[] cs;
+ int _hashCode;
+};
+
+// Compare two Strings
+val equals = { String self, String s ->
+ if( self == s ) return true;
+ if( self.cs# != s.cs# ) return false;
+ for( int i=0; i< self.cs#; i++ )
+ if( self.cs[i] != s.cs[i] )
+ return false;
+ return true;
+};
+
+// Return the String hashCode (cached, and never 0)
+val hashCode = { String self ->
+ self._hashCode
+ ? self._hashCode
+ : (self._hashCode = _hashCodeString(self));
+};
+
+val _hashCodeString = { String self ->
+ int hash=0;
+ if( self.cs )
+ for( int i=0; i< self.cs#; i++ )
+ hash = hash*31 + self.cs[i];
+ if( !hash ) hash = 123456789;
+ return hash;
+};