1 // Copyright 2013 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
5 // MakeFunc amd64 implementation.
11 // The assembler stub will pass a pointer to this structure.
12 // This will come in holding all the registers that might hold
13 // function parameters. On return we will set the registers that
14 // might hold result values.
15 type amd64Regs
struct {
34 // Argument classifications. The amd64 ELF ABI uses several more, but
35 // these are the only ones that arise for Go types.
39 amd64Integer amd64Class
= iota
45 // amd64Classify returns the one or two register classes needed to
46 // pass the value of type. Go types never need more than two
47 // registers. amd64Memory means the value is stored in memory.
48 // amd64NoClass means the register is not used.
49 func amd64Classify(typ
*rtype
) (amd64Class
, amd64Class
) {
52 panic("internal error--unknown kind in amd64Classify")
54 case Bool
, Int
, Int8
, Int16
, Int32
, Int64
,
55 Uint
, Uint8
, Uint16
, Uint32
, Uint64
,
56 Uintptr
, Chan
, Func
, Map
, Ptr
, UnsafePointer
:
58 return amd64Integer
, amd64NoClass
60 case Float32
, Float64
, Complex64
:
61 return amd64SSE
, amd64NoClass
64 return amd64SSE
, amd64SSE
68 return amd64NoClass
, amd64NoClass
69 } else if typ
.size
> 16 {
70 return amd64Memory
, amd64NoClass
72 atyp
:= (*arrayType
)(unsafe
.Pointer(typ
))
73 eclass1
, eclass2
:= amd64Classify(atyp
.elem
)
74 if eclass1
== amd64Memory
{
75 return amd64Memory
, amd64NoClass
77 if eclass2
== amd64NoClass
&& typ
.size
> 8 {
80 return eclass1
, eclass2
83 return amd64Integer
, amd64Integer
86 return amd64Memory
, amd64NoClass
89 return amd64Integer
, amd64Integer
93 return amd64NoClass
, amd64NoClass
94 } else if typ
.size
> 16 {
95 return amd64Memory
, amd64NoClass
97 var first
, second amd64Class
100 styp
:= (*structType
)(unsafe
.Pointer(typ
))
101 for _
, field
:= range styp
.fields
{
102 if onFirst
&& field
.offset
>= 8 {
107 fclass1
, fclass2
:= amd64Classify(field
.typ
)
108 f
= amd64MergeClasses(f
, fclass1
)
109 if fclass2
!= amd64NoClass
{
111 panic("amd64Classify inconsistent")
120 second
= amd64NoClass
124 if first
== amd64Memory || second
== amd64Memory
{
125 return amd64Memory
, amd64NoClass
131 // amd64MergeClasses merges two register classes as described in the
133 func amd64MergeClasses(c1
, c2 amd64Class
) amd64Class
{
137 case c1
== amd64NoClass
:
139 case c2
== amd64NoClass
:
141 case c1
== amd64Memory || c2
== amd64Memory
:
143 case c1
== amd64Integer || c2
== amd64Integer
:
150 // MakeFuncStubGo implements the amd64 calling convention for
151 // MakeFunc. This should not be called. It is exported so that
152 // assembly code can call it.
154 func MakeFuncStubGo(regs
*amd64Regs
, c
*makeFuncImpl
) {
157 // See if the result requires a struct. If it does, the first
158 // parameter is a pointer to the struct.
159 var ret1
, ret2 amd64Class
160 switch len(ftyp
.out
) {
162 ret1
, ret2
= amd64NoClass
, amd64NoClass
164 ret1
, ret2
= amd64Classify(ftyp
.out
[0])
169 for _
, rt
:= range ftyp
.out
{
170 off
= align(off
, uintptr(rt
.fieldAlign
))
172 if onFirst
&& off
>= 8 {
183 fclass1
, fclass2
:= amd64Classify(rt
)
184 f
= amd64MergeClasses(f
, fclass1
)
185 if fclass2
!= amd64NoClass
{
187 panic("amd64Classify inconsistent")
195 ret1
, ret2
= amd64Memory
, amd64NoClass
198 ret1
, ret2
= f
, amd64NoClass
203 if ret1
== amd64Memory || ret2
== amd64Memory
{
204 ret1
, ret2
= amd64Memory
, amd64NoClass
208 in
:= make([]Value
, 0, len(ftyp
.in
))
211 ap
:= uintptr(regs
.rsp
)
213 maxIntregs
:= 6 // When we support Windows, this would be 4.
216 if ret1
== amd64Memory
{
217 // We are returning a value in memory, which means
218 // that the first argument is a hidden parameter
219 // pointing to that return area.
224 for _
, rt
:= range ftyp
.in
{
225 c1
, c2
:= amd64Classify(rt
)
227 fl
:= flag(rt
.Kind()) << flagKindShift
228 if c2
== amd64NoClass
{
230 // Argument is passed in a single register or
235 v
:= Value
{rt
, nil, fl | flagIndir
}
239 if intreg
< maxIntregs
{
240 reg
:= amd64IntregVal(regs
, intreg
)
241 iw
:= unsafe
.Pointer(reg
)
242 if k
:= rt
.Kind(); k
!= Ptr
&& k
!= UnsafePointer
{
243 iw
= unsafe
.Pointer(®
)
246 v
:= Value
{rt
, iw
, fl
}
252 if ssereg
< maxSSEregs
{
253 reg
:= amd64SSEregVal(regs
, ssereg
)
254 v
:= Value
{rt
, unsafe
.Pointer(®
), fl | flagIndir
}
261 in
, ap
= amd64Memarg(in
, ap
, rt
)
265 // Argument is passed in two registers.
275 panic("inconsistent")
283 panic("inconsistent")
286 // If the whole argument does not fit in registers, it
287 // is passed in memory.
289 if intreg
+nintregs
> maxIntregs || ssereg
+nsseregs
> maxSSEregs
{
290 in
, ap
= amd64Memarg(in
, ap
, rt
)
294 var word1
, word2
uintptr
297 word1
= amd64IntregVal(regs
, intreg
)
300 word1
= amd64SSEregVal(regs
, ssereg
)
305 word2
= amd64IntregVal(regs
, intreg
)
308 word2
= amd64SSEregVal(regs
, ssereg
)
313 *(*uintptr)(p
) = word1
314 *(*uintptr)(unsafe
.Pointer(uintptr(p
) + ptrSize
)) = word2
315 v
:= Value
{rt
, p
, fl | flagIndir
}
319 // All the real arguments have been found and turned into
320 // Value's. Call the real function.
324 if len(out
) != len(ftyp
.out
) {
325 panic("reflect: wrong return count from function created by MakeFunc")
328 for i
, typ
:= range ftyp
.out
{
331 panic("reflect: function created by MakeFunc using " + funcName(c
.fn
) +
332 " returned wrong type: have " +
333 out
[i
].typ
.String() + " for " + typ
.String())
335 if v
.flag
&flagRO
!= 0 {
336 panic("reflect: function created by MakeFunc using " + funcName(c
.fn
) +
337 " returned value obtained from unexported field")
341 if ret1
== amd64NoClass
{
345 if ret1
== amd64Memory
{
346 // The address of the memory area was passed as a
347 // hidden parameter in %rdi.
348 ptr
:= unsafe
.Pointer(uintptr(regs
.rdi
))
350 for i
, typ
:= range ftyp
.out
{
352 off
= align(off
, uintptr(typ
.fieldAlign
))
353 addr
:= unsafe
.Pointer(uintptr(ptr
) + off
)
354 if v
.flag
&flagIndir
== 0 && (v
.kind() == Ptr || v
.kind() == UnsafePointer
) {
355 storeIword(addr
, iword(v
.val
), typ
.size
)
357 memmove(addr
, v
.val
, typ
.size
)
364 if len(out
) == 1 && ret2
== amd64NoClass
{
367 if v
.Kind() != Ptr
&& v
.Kind() != UnsafePointer
{
368 w
= loadIword(unsafe
.Pointer(w
), v
.typ
.size
)
372 regs
.rax
= uint64(uintptr(w
))
374 regs
.xmm0
[0] = uint64(uintptr(w
))
377 panic("inconsistency")
382 var buf
[2]unsafe
.Pointer
383 ptr
:= unsafe
.Pointer(&buf
[0])
385 for i
, typ
:= range ftyp
.out
{
387 off
= align(off
, uintptr(typ
.fieldAlign
))
388 addr
:= unsafe
.Pointer(uintptr(ptr
) + off
)
389 if v
.flag
&flagIndir
== 0 && (v
.kind() == Ptr || v
.kind() == UnsafePointer
) {
390 storeIword(addr
, iword(v
.val
), typ
.size
)
392 memmove(addr
, v
.val
, typ
.size
)
394 off
+= uintptr(typ
.size
)
399 regs
.rax
= *(*uint64)(unsafe
.Pointer(&buf
[0]))
401 regs
.xmm0
[0] = *(*uint64)(unsafe
.Pointer(&buf
[0]))
404 panic("inconsistency")
409 reg
:= *(*uint64)(unsafe
.Pointer(&buf
[1]))
410 if ret1
== amd64Integer
{
416 reg
:= *(*uint64)(unsafe
.Pointer(&buf
[1]))
417 if ret1
== amd64Integer
{
426 panic("inconsistency")
430 // The amd64Memarg function adds an argument passed in memory.
431 func amd64Memarg(in
[]Value
, ap
uintptr, rt
*rtype
) ([]Value
, uintptr) {
432 ap
= align(ap
, ptrSize
)
433 ap
= align(ap
, uintptr(rt
.align
))
435 // We have to copy the argument onto the heap in case the
436 // function hangs onto the reflect.Value we pass it.
438 memmove(p
, unsafe
.Pointer(ap
), rt
.size
)
440 v
:= Value
{rt
, p
, flag(rt
.Kind()<<flagKindShift
) | flagIndir
}
446 // The amd64IntregVal function returns the value of integer register i.
447 func amd64IntregVal(regs
*amd64Regs
, i
int) uintptr {
463 panic("amd64IntregVal: bad index")
468 // The amd64SSEregVal function returns the value of SSE register i.
469 // Note that although SSE registers can hold two uinptr's, for the
470 // types we use in Go we only ever use the least significant one. The
471 // most significant one would only be used for 128 bit types.
472 func amd64SSEregVal(regs
*amd64Regs
, i
int) uintptr {