summaryrefslogtreecommitdiff
path: root/src/main/java/com/seaofnodes/simple/codegen/Machine.java
blob: 3eee8fc330eacf81d999ff4a15a7c3e9a06e4a04 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
package com.seaofnodes.simple.codegen;

import com.seaofnodes.simple.node.*;
import com.seaofnodes.simple.type.TypeFunPtr;
import com.seaofnodes.simple.type.TypeInteger;

abstract public class Machine {
    // Human readable machine name.  Something like "x86-64" or "arm" or "risc5"
    public abstract String name();

    // Default opcode size for printing; 4 bytes for most 32-bit risc chips
    public int defaultOpSize() { return 4; }

    // Human-readable name for a register number, e.g. "RAX" or "R0"
    public abstract String[] regs();

    // Create a split op; any register to any register, including stack slots.
    // The FunNode holds the stack layout after RegAlloc, which in turn
    // dictates the stack slot (the frame offset) for stack splits.
    public abstract SplitNode split( LRG lrg, String kind, byte round );

    // List of caller-save registers, as a 64bit mask
    public abstract long callerSave();
    public long callerSave(String conv) { return 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);
    public RegMask callArgMask(TypeFunPtr tfp, int arg, int maxArgSlot, String conv) { return callArgMask(tfp,arg,maxArgSlot); }

    // Return register mask, based on signature (GPR vs FPR)
    public abstract RegMask retMask(TypeFunPtr tfp);

    // Return PC register
    public abstract int rpc();

    // Return a MachNode unconditional branch
    public abstract CFGNode jump();

    // Instruction select from ideal nodes
    public abstract Node instSelect( Node n );

    /** Stack Slot Numbering

RA gives all registers a number, starting at 0.  Stack slots continue this
numbering going up from the last register number.  Common register numbers are
0-15 for GPRs, 16-31 for FPRs, 32 for flags, and stack slots starting at
register#33 going up.  These numbers are machine-specific, YMMV, etc; e.g. RPC
is only in stack slot 0 on X86; other cpus start with the rpc in a register
which may spill into any generic spill slot.

For ease of reading and printing, stack slots start again at slot#0 - but
during RA they are actually biased by CPU.MAX_REG.

Stack Layout during Reg Alloc:

         Slot  Value    Description
        ----  -------  -----------
MAXREG+ R+1   argR     maxArgSlot(calleR tfp), can be 0
        ...
MAXREG+ 1     arg0
        ----  -- Caller ------
MAXREG+ 0     RPC
MAXREG+ R+E+N PAD      optional alignment

MAXREG+  +    Spills   Caller Frame Space; N spills
MAXREG+ R+E+1 Spills

MAXREG+ R+E   argE     maxArgSlot(calleE tfp), can be 0
        ...
MAXREG+ R+2   arg0
        ----  -- Callee -------
        ...

Slots defined by maxArgSlot do not have to contain the actual passed argument;
they can be "shadow slots".  If they exist, they are property of the frame /
FunNode where the TFP comes from.

For a given calleR, there are generally many calleE's and they all share the
low-range stack slots.

Example:, Win64, main(int arg) { calloc(1,size); }

                           Post-Warp: SP+56   shadow-main-arg
Pre-alloc               Post-Alloc    SP+48   RPC
--- -------             ----------    -------------
40  spill#3             SP+40         SP+40   spill#3
39  spill#2             SP+32          ""
38  spill#1             SP+24          ""
37  spill#0             SP+16          ""     spill#0
36  shadow-calloc#size  SP+ 8         SP+ 8   shadow-calloc#size
35  shadow-calloc#1     SP+ 0         SP+ 0   shadow-calloc#1
--- --- FRAME BASE ----------         ---- FRAME BASE -------
34  shadow-main-arg     SP+56
33  RPC                 SP+48


    */


    // Maximum stack slot (or 0) for the args in this TFP.  This will include
    // shadow slots if defined in the ABI, even if all arguments are passed in
    // registers.
    public abstract short maxArgSlot(TypeFunPtr tfp);
    public short maxArgSlot(TypeFunPtr tfp, String conv) { return maxArgSlot(tfp); }

    // Calling convention used for external C symbols.
    public String cCallingConv(String callingConv) { return callingConv; }

    // Only the declared C result width describes the physical return register.
    // Simple functions already return canonical full-width integer values.
    protected static TypeInteger cReturn(ProjNode prj) {
        if( prj._idx==2 && prj.in(0) instanceof CallEndNode cend &&
            cend.call().fptr()._type instanceof TypeFunPtr tfp && tfp.isConstant() &&
            CodeGen.CODE.externFunc(tfp.fidx())!=null &&
            tfp.ret() instanceof TypeInteger ret && ret.log_size()<3 )
            return ret;
        return null;
    }

}