diff options
author | mrs <mrs@138bc75d-0d04-0410-961f-82ee72b054a4> | 2013-10-20 23:47:35 +0000 |
---|---|---|
committer | mrs <mrs@138bc75d-0d04-0410-961f-82ee72b054a4> | 2013-10-20 23:47:35 +0000 |
commit | 6b40961666f073231ed8a76e6e33deeda063cde7 (patch) | |
tree | 8247eb4232e8be98b7f61bd68bab2fd1a9f06ca3 /libgo/go/reflect/makefuncgo_amd64.go | |
parent | e6b1b76450af5f98696ecedd4bd9a0ed18cdb2a6 (diff) | |
parent | fc1ce0cf396bf638746d546a557158d87f13849b (diff) | |
download | gcc-6b40961666f073231ed8a76e6e33deeda063cde7.tar.gz |
Merge in trunk.
git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/branches/wide-int@203881 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'libgo/go/reflect/makefuncgo_amd64.go')
-rw-r--r-- | libgo/go/reflect/makefuncgo_amd64.go | 493 |
1 files changed, 493 insertions, 0 deletions
diff --git a/libgo/go/reflect/makefuncgo_amd64.go b/libgo/go/reflect/makefuncgo_amd64.go new file mode 100644 index 00000000000..ecc50a42520 --- /dev/null +++ b/libgo/go/reflect/makefuncgo_amd64.go @@ -0,0 +1,493 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// MakeFunc amd64 implementation. + +package reflect + +import "unsafe" + +// The assembler stub will pass a pointer to this structure. +// This will come in holding all the registers that might hold +// function parameters. On return we will set the registers that +// might hold result values. +type amd64Regs struct { + rax uint64 + rdi uint64 + rsi uint64 + rdx uint64 + rcx uint64 + r8 uint64 + r9 uint64 + rsp uint64 + xmm0 [2]uint64 + xmm1 [2]uint64 + xmm2 [2]uint64 + xmm3 [2]uint64 + xmm4 [2]uint64 + xmm5 [2]uint64 + xmm6 [2]uint64 + xmm7 [2]uint64 +} + +// Argument classifications. The amd64 ELF ABI uses several more, but +// these are the only ones that arise for Go types. +type amd64Class int + +const ( + amd64Integer amd64Class = iota + amd64SSE + amd64NoClass + amd64Memory +) + +// amd64Classify returns the one or two register classes needed to +// pass the value of type. Go types never need more than two +// registers. amd64Memory means the value is stored in memory. +// amd64NoClass means the register is not used. +func amd64Classify(typ *rtype) (amd64Class, amd64Class) { + switch typ.Kind() { + default: + panic("internal error--unknown kind in amd64Classify") + + case Bool, Int, Int8, Int16, Int32, Int64, + Uint, Uint8, Uint16, Uint32, Uint64, + Uintptr, Chan, Func, Map, Ptr, UnsafePointer: + + return amd64Integer, amd64NoClass + + case Float32, Float64, Complex64: + return amd64SSE, amd64NoClass + + case Complex128: + return amd64SSE, amd64SSE + + case Array: + if typ.size == 0 { + return amd64NoClass, amd64NoClass + } else if typ.size > 16 { + return amd64Memory, amd64NoClass + } + atyp := (*arrayType)(unsafe.Pointer(typ)) + eclass1, eclass2 := amd64Classify(atyp.elem) + if eclass1 == amd64Memory { + return amd64Memory, amd64NoClass + } + if eclass2 == amd64NoClass && typ.size > 8 { + eclass2 = eclass1 + } + return eclass1, eclass2 + + case Interface: + return amd64Integer, amd64Integer + + case Slice: + return amd64Memory, amd64NoClass + + case String: + return amd64Integer, amd64Integer + + case Struct: + if typ.size == 0 { + return amd64NoClass, amd64NoClass + } else if typ.size > 16 { + return amd64Memory, amd64NoClass + } + var first, second amd64Class + f := amd64NoClass + onFirst := true + styp := (*structType)(unsafe.Pointer(typ)) + for _, field := range styp.fields { + if onFirst && field.offset >= 8 { + first = f + f = amd64NoClass + onFirst = false + } + fclass1, fclass2 := amd64Classify(field.typ) + f = amd64MergeClasses(f, fclass1) + if fclass2 != amd64NoClass { + if !onFirst { + panic("amd64Classify inconsistent") + } + first = f + f = fclass2 + onFirst = false + } + } + if onFirst { + first = f + second = amd64NoClass + } else { + second = f + } + if first == amd64Memory || second == amd64Memory { + return amd64Memory, amd64NoClass + } + return first, second + } +} + +// amd64MergeClasses merges two register classes as described in the +// amd64 ELF ABI. +func amd64MergeClasses(c1, c2 amd64Class) amd64Class { + switch { + case c1 == c2: + return c1 + case c1 == amd64NoClass: + return c2 + case c2 == amd64NoClass: + return c1 + case c1 == amd64Memory || c2 == amd64Memory: + return amd64Memory + case c1 == amd64Integer || c2 == amd64Integer: + return amd64Integer + default: + return amd64SSE + } +} + +// MakeFuncStubGo implements the amd64 calling convention for +// MakeFunc. This should not be called. It is exported so that +// assembly code can call it. + +func MakeFuncStubGo(regs *amd64Regs, c *makeFuncImpl) { + ftyp := c.typ + + // See if the result requires a struct. If it does, the first + // parameter is a pointer to the struct. + var ret1, ret2 amd64Class + switch len(ftyp.out) { + case 0: + ret1, ret2 = amd64NoClass, amd64NoClass + case 1: + ret1, ret2 = amd64Classify(ftyp.out[0]) + default: + off := uintptr(0) + f := amd64NoClass + onFirst := true + for _, rt := range ftyp.out { + off = align(off, uintptr(rt.fieldAlign)) + + if onFirst && off >= 8 { + ret1 = f + f = amd64NoClass + onFirst = false + } + + off += rt.size + if off > 16 { + break + } + + fclass1, fclass2 := amd64Classify(rt) + f = amd64MergeClasses(f, fclass1) + if fclass2 != amd64NoClass { + if !onFirst { + panic("amd64Classify inconsistent") + } + ret1 = f + f = fclass2 + onFirst = false + } + } + if off > 16 { + ret1, ret2 = amd64Memory, amd64NoClass + } else { + if onFirst { + ret1, ret2 = f, amd64NoClass + } else { + ret2 = f + } + } + if ret1 == amd64Memory || ret2 == amd64Memory { + ret1, ret2 = amd64Memory, amd64NoClass + } + } + + in := make([]Value, 0, len(ftyp.in)) + intreg := 0 + ssereg := 0 + ap := uintptr(regs.rsp) + + maxIntregs := 6 // When we support Windows, this would be 4. + maxSSEregs := 8 + + if ret1 == amd64Memory { + // We are returning a value in memory, which means + // that the first argument is a hidden parameter + // pointing to that return area. + intreg++ + } + +argloop: + for _, rt := range ftyp.in { + c1, c2 := amd64Classify(rt) + + fl := flag(rt.Kind()) << flagKindShift + if c2 == amd64NoClass { + + // Argument is passed in a single register or + // in memory. + + switch c1 { + case amd64NoClass: + v := Value{rt, nil, fl | flagIndir} + in = append(in, v) + continue argloop + case amd64Integer: + if intreg < maxIntregs { + reg := amd64IntregVal(regs, intreg) + iw := unsafe.Pointer(reg) + if k := rt.Kind(); k != Ptr && k != UnsafePointer { + iw = unsafe.Pointer(®) + fl |= flagIndir + } + v := Value{rt, iw, fl} + in = append(in, v) + intreg++ + continue argloop + } + case amd64SSE: + if ssereg < maxSSEregs { + reg := amd64SSEregVal(regs, ssereg) + v := Value{rt, unsafe.Pointer(®), fl | flagIndir} + in = append(in, v) + ssereg++ + continue argloop + } + } + + in, ap = amd64Memarg(in, ap, rt) + continue argloop + } + + // Argument is passed in two registers. + + nintregs := 0 + nsseregs := 0 + switch c1 { + case amd64Integer: + nintregs++ + case amd64SSE: + nsseregs++ + default: + panic("inconsistent") + } + switch c2 { + case amd64Integer: + nintregs++ + case amd64SSE: + nsseregs++ + default: + panic("inconsistent") + } + + // If the whole argument does not fit in registers, it + // is passed in memory. + + if intreg+nintregs > maxIntregs || ssereg+nsseregs > maxSSEregs { + in, ap = amd64Memarg(in, ap, rt) + continue argloop + } + + var word1, word2 uintptr + switch c1 { + case amd64Integer: + word1 = amd64IntregVal(regs, intreg) + intreg++ + case amd64SSE: + word1 = amd64SSEregVal(regs, ssereg) + ssereg++ + } + switch c2 { + case amd64Integer: + word2 = amd64IntregVal(regs, intreg) + intreg++ + case amd64SSE: + word2 = amd64SSEregVal(regs, ssereg) + ssereg++ + } + + p := unsafe_New(rt) + *(*uintptr)(p) = word1 + *(*uintptr)(unsafe.Pointer(uintptr(p) + ptrSize)) = word2 + v := Value{rt, p, fl | flagIndir} + in = append(in, v) + } + + // All the real arguments have been found and turned into + // Value's. Call the real function. + + out := c.fn(in) + + if len(out) != len(ftyp.out) { + panic("reflect: wrong return count from function created by MakeFunc") + } + + for i, typ := range ftyp.out { + v := out[i] + if v.typ != typ { + panic("reflect: function created by MakeFunc using " + funcName(c.fn) + + " returned wrong type: have " + + out[i].typ.String() + " for " + typ.String()) + } + if v.flag&flagRO != 0 { + panic("reflect: function created by MakeFunc using " + funcName(c.fn) + + " returned value obtained from unexported field") + } + } + + if ret1 == amd64NoClass { + return + } + + if ret1 == amd64Memory { + // The address of the memory area was passed as a + // hidden parameter in %rdi. + ptr := unsafe.Pointer(uintptr(regs.rdi)) + off := uintptr(0) + for i, typ := range ftyp.out { + v := out[i] + off = align(off, uintptr(typ.fieldAlign)) + addr := unsafe.Pointer(uintptr(ptr) + off) + if v.flag&flagIndir == 0 && (v.kind() == Ptr || v.kind() == UnsafePointer) { + storeIword(addr, iword(v.val), typ.size) + } else { + memmove(addr, v.val, typ.size) + } + off += typ.size + } + return + } + + if len(out) == 1 && ret2 == amd64NoClass { + v := out[0] + w := v.iword() + if v.Kind() != Ptr && v.Kind() != UnsafePointer { + w = loadIword(unsafe.Pointer(w), v.typ.size) + } + switch ret1 { + case amd64Integer: + regs.rax = uint64(uintptr(w)) + case amd64SSE: + regs.xmm0[0] = uint64(uintptr(w)) + regs.xmm0[1] = 0 + default: + panic("inconsistency") + } + return + } + + var buf [2]unsafe.Pointer + ptr := unsafe.Pointer(&buf[0]) + off := uintptr(0) + for i, typ := range ftyp.out { + v := out[i] + off = align(off, uintptr(typ.fieldAlign)) + addr := unsafe.Pointer(uintptr(ptr) + off) + if v.flag&flagIndir == 0 && (v.kind() == Ptr || v.kind() == UnsafePointer) { + storeIword(addr, iword(v.val), typ.size) + } else { + memmove(addr, v.val, typ.size) + } + off += uintptr(typ.size) + } + + switch ret1 { + case amd64Integer: + regs.rax = *(*uint64)(unsafe.Pointer(&buf[0])) + case amd64SSE: + regs.xmm0[0] = *(*uint64)(unsafe.Pointer(&buf[0])) + regs.xmm0[1] = 0 + default: + panic("inconsistency") + } + + switch ret2 { + case amd64Integer: + reg := *(*uint64)(unsafe.Pointer(&buf[1])) + if ret1 == amd64Integer { + regs.rdx = reg + } else { + regs.rax = reg + } + case amd64SSE: + reg := *(*uint64)(unsafe.Pointer(&buf[1])) + if ret1 == amd64Integer { + regs.xmm0[0] = reg + regs.xmm0[1] = 0 + } else { + regs.xmm1[0] = reg + regs.xmm1[1] = 0 + } + case amd64NoClass: + default: + panic("inconsistency") + } +} + +// The amd64Memarg function adds an argument passed in memory. +func amd64Memarg(in []Value, ap uintptr, rt *rtype) ([]Value, uintptr) { + ap = align(ap, ptrSize) + ap = align(ap, uintptr(rt.align)) + + // We have to copy the argument onto the heap in case the + // function hangs onto the reflect.Value we pass it. + p := unsafe_New(rt) + memmove(p, unsafe.Pointer(ap), rt.size) + + v := Value{rt, p, flag(rt.Kind()<<flagKindShift) | flagIndir} + in = append(in, v) + ap += rt.size + return in, ap +} + +// The amd64IntregVal function returns the value of integer register i. +func amd64IntregVal(regs *amd64Regs, i int) uintptr { + var r uint64 + switch i { + case 0: + r = regs.rdi + case 1: + r = regs.rsi + case 2: + r = regs.rdx + case 3: + r = regs.rcx + case 4: + r = regs.r8 + case 5: + r = regs.r9 + default: + panic("amd64IntregVal: bad index") + } + return uintptr(r) +} + +// The amd64SSEregVal function returns the value of SSE register i. +// Note that although SSE registers can hold two uinptr's, for the +// types we use in Go we only ever use the least significant one. The +// most significant one would only be used for 128 bit types. +func amd64SSEregVal(regs *amd64Regs, i int) uintptr { + var r uint64 + switch i { + case 0: + r = regs.xmm0[0] + case 1: + r = regs.xmm1[0] + case 2: + r = regs.xmm2[0] + case 3: + r = regs.xmm3[0] + case 4: + r = regs.xmm4[0] + case 5: + r = regs.xmm5[0] + case 6: + r = regs.xmm6[0] + case 7: + r = regs.xmm7[0] + } + return uintptr(r) +} |