summaryrefslogtreecommitdiff
path: root/src/main/java/com
diff options
context:
space:
mode:
authorCliff Click <cliffc@acm.org>2025-04-24 14:38:36 -0700
committerCliff Click <cliffc@acm.org>2026-08-10 11:11:18 -0700
commit1c61d09c8538e894980b3b9580343d5cd2846658 (patch)
tree9a40de36668fcbcdee2e622264d886f58bed574a /src/main/java/com
parent1ded839cb51ae32ad86d37a39d439dfd88f4fb6f (diff)
downloadSimple-1c61d09c8538e894980b3b9580343d5cd2846658.tar.gz
Simple-1c61d09c8538e894980b3b9580343d5cd2846658.zip
Chapter 21: Instruction Encoding and ELF
Co-authored-by: godalming123 <68993177+godalming123@users.noreply.github.com> Co-authored-by: tejasae-afk <tejasae-afk@users.noreply.github.com> Co-authored-by: Tejas Attarde <201814263+tejasae-afk@users.noreply.github.com> Co-authored-by: Tamás Hadházy <85063808+Hels15@users.noreply.github.com> Co-authored-by: Hsiang-Ying Fu <66062782+fuhsnn@users.noreply.github.com>
Diffstat (limited to 'src/main/java/com')
-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
230 files changed, 6485 insertions, 3875 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 e8043854..4ca95cae 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); }
@@ -267,13 +267,24 @@ 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; }
// Shortcuts to stop DCE mid-parse
// Add bogus null use to keep node alive
+ @SuppressWarnings("unchecked")
public <N extends Node> N keep() { addUse(null); return (N)this; }
// Remove bogus null.
+ @SuppressWarnings("unchecked")
public <N extends Node> N unkeep() { delUse(null); return (N)this; }
// Test "keep" status
public boolean iskeep() { return _outputs.find(null) != -1; }
@@ -297,9 +308,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 ) {
@@ -312,19 +322,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
@@ -334,6 +331,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);
@@ -345,7 +344,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 ) {
@@ -484,6 +483,7 @@ public abstract class Node {
return old;
}
+ @SuppressWarnings("unchecked")
public <N extends Node> N init() { _type = compute(); return (N)this; }
/**
@@ -546,17 +546,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.
@@ -582,7 +581,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
@@ -664,7 +663,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;
@@ -676,6 +675,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(); }