summaryrefslogtreecommitdiff
path: root/vendor/github.com/tetratelabs/wazero/internal/engine/wazevo/backend/abi.go
blob: cf91c6b7ad3208a5c8316c8f5cf564436e903bb5 (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
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
package backend

import (
	"fmt"

	"github.com/tetratelabs/wazero/internal/engine/wazevo/backend/regalloc"
	"github.com/tetratelabs/wazero/internal/engine/wazevo/ssa"
)

type (
	// FunctionABI represents the ABI information for a function which corresponds to a ssa.Signature.
	FunctionABI struct {
		Initialized bool

		Args, Rets                 []ABIArg
		ArgStackSize, RetStackSize int64

		ArgIntRealRegs   byte
		ArgFloatRealRegs byte
		RetIntRealRegs   byte
		RetFloatRealRegs byte
	}

	// ABIArg represents either argument or return value's location.
	ABIArg struct {
		// Index is the index of the argument.
		Index int
		// Kind is the kind of the argument.
		Kind ABIArgKind
		// Reg is valid if Kind == ABIArgKindReg.
		// This VReg must be based on RealReg.
		Reg regalloc.VReg
		// Offset is valid if Kind == ABIArgKindStack.
		// This is the offset from the beginning of either arg or ret stack slot.
		Offset int64
		// Type is the type of the argument.
		Type ssa.Type
	}

	// ABIArgKind is the kind of ABI argument.
	ABIArgKind byte
)

const (
	// ABIArgKindReg represents an argument passed in a register.
	ABIArgKindReg = iota
	// ABIArgKindStack represents an argument passed in the stack.
	ABIArgKindStack
)

// String implements fmt.Stringer.
func (a *ABIArg) String() string {
	return fmt.Sprintf("args[%d]: %s", a.Index, a.Kind)
}

// String implements fmt.Stringer.
func (a ABIArgKind) String() string {
	switch a {
	case ABIArgKindReg:
		return "reg"
	case ABIArgKindStack:
		return "stack"
	default:
		panic("BUG")
	}
}

// Init initializes the abiImpl for the given signature.
func (a *FunctionABI) Init(sig *ssa.Signature, argResultInts, argResultFloats []regalloc.RealReg) {
	if len(a.Rets) < len(sig.Results) {
		a.Rets = make([]ABIArg, len(sig.Results))
	}
	a.Rets = a.Rets[:len(sig.Results)]
	a.RetStackSize = a.setABIArgs(a.Rets, sig.Results, argResultInts, argResultFloats)
	if argsNum := len(sig.Params); len(a.Args) < argsNum {
		a.Args = make([]ABIArg, argsNum)
	}
	a.Args = a.Args[:len(sig.Params)]
	a.ArgStackSize = a.setABIArgs(a.Args, sig.Params, argResultInts, argResultFloats)

	// Gather the real registers usages in arg/return.
	a.ArgIntRealRegs, a.ArgFloatRealRegs = 0, 0
	a.RetIntRealRegs, a.RetFloatRealRegs = 0, 0
	for i := range a.Rets {
		r := &a.Rets[i]
		if r.Kind == ABIArgKindReg {
			if r.Type.IsInt() {
				a.RetIntRealRegs++
			} else {
				a.RetFloatRealRegs++
			}
		}
	}
	for i := range a.Args {
		arg := &a.Args[i]
		if arg.Kind == ABIArgKindReg {
			if arg.Type.IsInt() {
				a.ArgIntRealRegs++
			} else {
				a.ArgFloatRealRegs++
			}
		}
	}

	a.Initialized = true
}

// setABIArgs sets the ABI arguments in the given slice. This assumes that len(s) >= len(types)
// where if len(s) > len(types), the last elements of s is for the multi-return slot.
func (a *FunctionABI) setABIArgs(s []ABIArg, types []ssa.Type, ints, floats []regalloc.RealReg) (stackSize int64) {
	il, fl := len(ints), len(floats)

	var stackOffset int64
	intParamIndex, floatParamIndex := 0, 0
	for i, typ := range types {
		arg := &s[i]
		arg.Index = i
		arg.Type = typ
		if typ.IsInt() {
			if intParamIndex >= il {
				arg.Kind = ABIArgKindStack
				const slotSize = 8 // Align 8 bytes.
				arg.Offset = stackOffset
				stackOffset += slotSize
			} else {
				arg.Kind = ABIArgKindReg
				arg.Reg = regalloc.FromRealReg(ints[intParamIndex], regalloc.RegTypeInt)
				intParamIndex++
			}
		} else {
			if floatParamIndex >= fl {
				arg.Kind = ABIArgKindStack
				slotSize := int64(8)   // Align at least 8 bytes.
				if typ.Bits() == 128 { // Vector.
					slotSize = 16
				}
				arg.Offset = stackOffset
				stackOffset += slotSize
			} else {
				arg.Kind = ABIArgKindReg
				arg.Reg = regalloc.FromRealReg(floats[floatParamIndex], regalloc.RegTypeFloat)
				floatParamIndex++
			}
		}
	}
	return stackOffset
}

func (a *FunctionABI) AlignedArgResultStackSlotSize() uint32 {
	stackSlotSize := a.RetStackSize + a.ArgStackSize
	// Align stackSlotSize to 16 bytes.
	stackSlotSize = (stackSlotSize + 15) &^ 15
	// Check overflow 32-bit.
	if stackSlotSize > 0xFFFFFFFF {
		panic("ABI stack slot size overflow")
	}
	return uint32(stackSlotSize)
}

func (a *FunctionABI) ABIInfoAsUint64() uint64 {
	return uint64(a.ArgIntRealRegs)<<56 |
		uint64(a.ArgFloatRealRegs)<<48 |
		uint64(a.RetIntRealRegs)<<40 |
		uint64(a.RetFloatRealRegs)<<32 |
		uint64(a.AlignedArgResultStackSlotSize())
}

func ABIInfoFromUint64(info uint64) (argIntRealRegs, argFloatRealRegs, retIntRealRegs, retFloatRealRegs byte, stackSlotSize uint32) {
	return byte(info >> 56), byte(info >> 48), byte(info >> 40), byte(info >> 32), uint32(info)
}