From 1fec5f5274d7e232b75fc400321ed64be46766c3 Mon Sep 17 00:00:00 2001 From: Ian Lance Taylor Date: Tue, 4 Nov 2014 22:39:30 +0000 Subject: libgo: add s390 support From Dominik Vogt. * libgo/go/syscall/libcall_linux_s390.go: New file for s390 support. * libgo/go/syscall/syscall_linux_s390.go: Ditto. * libgo/go/syscall/libcall_linux_s390x.go: New file for s390x support. * libgo/go/syscall/syscall_linux_s390x.go: Ditto. * libgo/go/runtime/pprof/pprof.go (printStackRecord): Support s390 and s390x. * libgo/runtime/runtime.c (runtime_cputicks): Add support for s390 and s390x * libgo/mksysinfo.sh: Ditto. (upcase_fields): New helper function * libgo/go/debug/elf/file.go (applyRelocations): Implement relocations on s390x. (applyRelocationsS390x): Ditto. (DWARF): Ditto. * libgo/go/debug/elf/elf.go (R_390): New constants for S390 relocations. (r390Strings): Ditto. (String): Helper function for S390 relocations. (GoString): Ditto. * libgo/go/reflect/makefuncgo_s390.go: New file. (S390MakeFuncStubGo): Implementation of s390 abi. * libgo/go/reflect/makefuncgo_s390x.go: New file. (S390xMakeFuncStubGo): Implementation of s390x abi. * libgo/go/reflect/makefunc_s390.c: New file. (makeFuncStub): s390 and s390x specific implementation of function. * libgo/go/reflect/makefunc.go (MakeFunc): Add support for s390 and s390x. (makeMethodValue): Ditto. (makeValueMethod): Ditto. * libgo/Makefile.am (go_reflect_makefunc_s_file): Ditto. (go_reflect_makefunc_file): Ditto. * libgo/go/reflect/makefunc_dummy.c: Ditto. * libgo/runtime/runtime.h (__go_makefunc_can_recover): Export prototype for use in makefunc_s390.c. (__go_makefunc_returning): Ditto. * libgo/go/syscall/exec_linux.go (forkAndExecInChild): Fix order of the arguments of the clone system call for s390[x]. * libgo/configure.ac (is_s390): New variable. (is_s390x): Ditto (LIBGO_IS_S390): Ditto. (LIBGO_IS_S390X): Ditto. (GOARCH): Support s390 and s390x. * libgo/go/go/build/build.go (cgoEnabled): Ditto. * libgo/go/go/build/syslist.go (goarchList): Ditto. From-SVN: r217106 --- libgo/go/reflect/makefunc.go | 4 +- libgo/go/reflect/makefunc_s390.c | 86 +++++++ libgo/go/reflect/makefuncgo_s390.go | 453 +++++++++++++++++++++++++++++++++++ libgo/go/reflect/makefuncgo_s390x.go | 435 +++++++++++++++++++++++++++++++++ 4 files changed, 976 insertions(+), 2 deletions(-) create mode 100644 libgo/go/reflect/makefunc_s390.c create mode 100644 libgo/go/reflect/makefuncgo_s390.go create mode 100644 libgo/go/reflect/makefuncgo_s390x.go (limited to 'libgo/go/reflect') diff --git a/libgo/go/reflect/makefunc.go b/libgo/go/reflect/makefunc.go index 977aacfd438..eb4589c6ce9 100644 --- a/libgo/go/reflect/makefunc.go +++ b/libgo/go/reflect/makefunc.go @@ -61,7 +61,7 @@ func MakeFunc(typ Type, fn func(args []Value) (results []Value)) Value { var code uintptr var ffi *ffiData switch runtime.GOARCH { - case "amd64", "386": + case "amd64", "386", "s390", "s390x": // Indirect Go func value (dummy) to obtain actual // code address. (A Go func value is a pointer to a C // function pointer. http://golang.org/s/go11func.) @@ -159,7 +159,7 @@ func makeValueMethod(v Value) Value { } switch runtime.GOARCH { - case "amd64", "386": + case "amd64", "386", "s390", "s390x": // Indirect Go func value (dummy) to obtain actual // code address. (A Go func value is a pointer to a C // function pointer. http://golang.org/s/go11func.) diff --git a/libgo/go/reflect/makefunc_s390.c b/libgo/go/reflect/makefunc_s390.c new file mode 100644 index 00000000000..78a960ca2f6 --- /dev/null +++ b/libgo/go/reflect/makefunc_s390.c @@ -0,0 +1,86 @@ +// Copyright 2014 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. + +#include "runtime.h" +#include "go-panic.h" + +#ifdef __s390x__ +# define S390_GO_USE_64_BIT_ABI 1 +# define S390_GO_S390X_ARGS , double f4, double f6 +# define S390_GO_S390X_FIELDS double f4; double f6; + extern void S390xMakeFuncStubGo(void *, void *) + asm ("reflect.S390xMakeFuncStubGo"); +# define S390_GO_MakeFuncStubGo(r, c) S390xMakeFuncStubGo((r), (c)) +#else +# define S390_GO_USE_64_BIT_ABI 0 +# define S390_GO_S390X_ARGS +# define S390_GO_S390X_FIELDS + extern void S390MakeFuncStubGo(void *, void *) + asm ("reflect.S390MakeFuncStubGo"); +# define S390_GO_MakeFuncStubGo(r, c) S390MakeFuncStubGo((r), (c)) + /* Needed to make the unused 64 bit abi conditional code compile. */ +# define f4 f0 +# define f6 f2 +#endif + +/* Structure to store all registers used for parameter passing. */ +typedef struct +{ + long r2; + long r3; + long r4; + long r5; + long r6; + /* Pointer to non-register arguments on the stack. */ + long stack_args; + double f0; + double f2; + S390_GO_S390X_FIELDS +} s390Regs; + +void +makeFuncStub(long r2, long r3, long r4, long r5, long r6, + unsigned long stack_args, double f0, double f2 + S390_GO_S390X_ARGS) + asm ("reflect.makeFuncStub"); + +void +makeFuncStub(long r2, long r3, long r4, long r5, long r6, + unsigned long stack_args, double f0, double f2 + S390_GO_S390X_ARGS) +{ + s390Regs regs; + void *closure; + + /* Store the registers in a structure that is passed on to the Go stub + function. */ + regs.r2 = r2; + regs.r3 = r3; + regs.r4 = r4; + regs.r5 = r5; + regs.r6 = r6; + regs.stack_args = (long)&stack_args; + regs.f0 = f0; + regs.f2 = f2; + if (S390_GO_USE_64_BIT_ABI) { + regs.f4 = f4; + regs.f6 = f6; + } + /* For MakeFunc functions that call recover. */ + __go_makefunc_can_recover(__builtin_return_address(0)); + /* Call the Go stub function. */ + closure = __go_get_closure(); + S390_GO_MakeFuncStubGo(®s, closure); + /* MakeFunc functions can no longer call recover. */ + __go_makefunc_returning(); + /* Restore all possible return registers. */ + if (S390_GO_USE_64_BIT_ABI) { + asm volatile ("lg\t%%r2,0(%0)" : : "a" (®s.r2) : "r2" ); + asm volatile ("ld\t%%f0,0(%0)" : : "a" (®s.f0) : "f0" ); + } else { + asm volatile ("l\t%%r2,0(%0)" : : "a" (®s.r2) : "r2" ); + asm volatile ("l\t%%r3,0(%0)" : : "a" (®s.r3) : "r3" ); + asm volatile ("ld\t%%f0,0(%0)" : : "a" (®s.f0) : "f0" ); + } +} diff --git a/libgo/go/reflect/makefuncgo_s390.go b/libgo/go/reflect/makefuncgo_s390.go new file mode 100644 index 00000000000..ff22add81a1 --- /dev/null +++ b/libgo/go/reflect/makefuncgo_s390.go @@ -0,0 +1,453 @@ +// Copyright 2014 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 s390 implementation. + +package reflect + +import "unsafe" + +// Convenience types and constants. +const s390_arch_stack_slot_align uintptr = 4 +const s390_num_gr = 5 +const s390_num_fr = 2 + +type s390_arch_gr_t uint32 +type s390_arch_fr_t uint64 + +// 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 s390_regs struct { + r2 s390_arch_gr_t + r3 s390_arch_gr_t + r4 s390_arch_gr_t + r5 s390_arch_gr_t + r6 s390_arch_gr_t + stack_args s390_arch_gr_t + f0 s390_arch_fr_t + f2 s390_arch_fr_t +} + +// Argument classifications that arise for Go types. +type s390_arg_t int + +const ( + s390_general_reg s390_arg_t = iota + s390_general_reg_pair + s390_float_reg + // Argument passed as a pointer to an in-memory value. + s390_mem_ptr + s390_empty +) + +// s390ClassifyParameter returns the register class needed to +// pass the value of type TYP. s390_empty means the register is +// not used. The second and third return values are the offset of +// an rtype parameter passed in a register (second) or stack slot +// (third). +func s390ClassifyParameter(typ *rtype) (s390_arg_t, uintptr, uintptr) { + offset := s390_arch_stack_slot_align - typ.Size() + if typ.Size() > s390_arch_stack_slot_align { + offset = 0 + } + switch typ.Kind() { + default: + panic("internal error--unknown kind in s390ClassifyParameter") + case Bool, Int, Int8, Int16, Int32, Uint, Uint8, Uint16, Uint32: + return s390_general_reg, offset, offset + case Int64, Uint64: + return s390_general_reg_pair, 0, 0 + case Uintptr, Chan, Func, Map, Ptr, UnsafePointer: + return s390_general_reg, 0, 0 + case Float32, Float64: + return s390_float_reg, 0, offset + case Complex64, Complex128: + // Complex numbers are passed by reference. + return s390_mem_ptr, 0, 0 + case Array, Struct: + var ityp *rtype + var length int + + if typ.Size() == 0 { + return s390_empty, 0, 0 + } + switch typ.Size() { + default: + // Pointer to memory. + return s390_mem_ptr, 0, 0 + case 1, 2: + // Pass in an integer register. + return s390_general_reg, offset, offset + + case 4, 8: + // See below. + } + if typ.Kind() == Array { + atyp := (*arrayType)(unsafe.Pointer(typ)) + length = atyp.Len() + ityp = atyp.elem + } else { + styp := (*structType)(unsafe.Pointer(typ)) + length = len(styp.fields) + ityp = styp.fields[0].typ + } + if length == 1 { + class, off_reg, off_slot := s390ClassifyParameter(ityp) + if class == s390_float_reg { + // The array (stored in a structure) or struct + // is "equivalent to a floating point type" as + // defined in the S390 Abi. Note that this + // can only be the case in the case 4 of the + // switch above. + return s390_float_reg, off_reg, off_slot + } + } + switch typ.Size() { + case 4: + return s390_general_reg, offset, offset + case 8: + return s390_general_reg_pair, 0, 0 + default: + return s390_general_reg, 0, 0 + } + case Interface, String: + // Structure of size 8. + return s390_general_reg_pair, 0, 0 + + case Slice: + return s390_mem_ptr, 0, 0 + } +} + +// s390ClassifyReturn returns the register classes needed to +// return the value of type TYP. s390_empty means the register is +// not used. The second value is the offset of an rtype return +// parameter if stored in a register. +func s390ClassifyReturn(typ *rtype) (s390_arg_t, uintptr) { + offset := s390_arch_stack_slot_align - typ.Size() + if typ.Size() > s390_arch_stack_slot_align { + offset = 0 + } + switch typ.Kind() { + default: + panic("internal error--unknown kind in s390ClassifyReturn") + case Bool, Int, Int8, Int16, Int32, + Uint, Uint8, Uint16, Uint32, Uintptr: + + return s390_general_reg, offset + case Int64, Uint64: + return s390_general_reg_pair, 0 + case Chan, Func, Map, Ptr, UnsafePointer: + return s390_general_reg, 0 + case Float32, Float64: + return s390_float_reg, 0 + case Complex64, Complex128: + return s390_mem_ptr, 0 + case Interface, Slice, String: + return s390_mem_ptr, 0 + case Array, Struct: + if typ.size == 0 { + return s390_empty, 0 + } + // No optimization is done for returned structures and arrays. + return s390_mem_ptr, 0 + } +} + +// Given a value of type *rtype left aligned in an unsafe.Pointer, +// reload the value so that it can be stored in a general or +// floating point register. For general registers the value is +// sign extend and right aligned. +func s390ReloadForRegister(typ *rtype, w uintptr, offset uintptr) uintptr { + var do_sign_extend bool = false + var gr s390_arch_gr_t + + switch typ.Kind() { + case Int, Int8, Int16, Int32: + do_sign_extend = true + default: + // Handle all other cases in the next switch. + } + switch typ.size { + case 1: + if do_sign_extend == true { + se := int32(*(*int8)(unsafe.Pointer(&w))) + gr = *(*s390_arch_gr_t)(unsafe.Pointer(&se)) + } else { + e := int32(*(*uint8)(unsafe.Pointer(&w))) + gr = *(*s390_arch_gr_t)(unsafe.Pointer(&e)) + } + case 2: + if do_sign_extend == true { + se := int32(*(*int16)(unsafe.Pointer(&w))) + gr = *(*s390_arch_gr_t)(unsafe.Pointer(&se)) + } else { + e := int32(*(*uint16)(unsafe.Pointer(&w))) + gr = *(*s390_arch_gr_t)(unsafe.Pointer(&e)) + } + default: + panic("reflect: bad size in s390ReloadForRegister") + } + + return *(*uintptr)(unsafe.Pointer(&gr)) +} + +// MakeFuncStubGo implements the s390 calling convention for +// MakeFunc. This should not be called. It is exported so that +// assembly code can call it. +func S390MakeFuncStubGo(regs *s390_regs, c *makeFuncImpl) { + ftyp := c.typ + gr := 0 + fr := 0 + ap := uintptr(regs.stack_args) + + // See if the result requires a struct. If it does, the first + // parameter is a pointer to the struct. + var ret_class s390_arg_t + var ret_off_reg uintptr + var ret_type *rtype + + switch len(ftyp.out) { + case 0: + ret_type = nil + ret_class, ret_off_reg = s390_empty, 0 + case 1: + ret_type = ftyp.out[0] + ret_class, ret_off_reg = s390ClassifyReturn(ret_type) + default: + ret_type = nil + ret_class, ret_off_reg = s390_mem_ptr, 0 + } + in := make([]Value, 0, len(ftyp.in)) + if ret_class == s390_mem_ptr { + // We are returning a value in memory, which means + // that the first argument is a hidden parameter + // pointing to that return area. + gr++ + } + +argloop: + for _, rt := range ftyp.in { + class, off_reg, off_slot := s390ClassifyParameter(rt) + fl := flag(rt.Kind()) << flagKindShift + switch class { + case s390_empty: + v := Value{rt, nil, fl | flagIndir} + in = append(in, v) + continue argloop + case s390_general_reg: + // Values stored in a general register are right + // aligned. + if gr < s390_num_gr { + val := s390_general_reg_val(regs, gr) + iw := unsafe.Pointer(&val) + k := rt.Kind() + if k != Ptr && k != UnsafePointer { + ix := uintptr(unsafe.Pointer(&val)) + ix += off_reg + iw = unsafe.Pointer(ix) + fl |= flagIndir + } + v := Value{rt, iw, fl} + in = append(in, v) + gr++ + } else { + in, ap = s390_add_stackreg( + in, ap, rt, off_slot) + } + continue argloop + case s390_general_reg_pair: + // 64-bit integers and structs are passed in a register + // pair. + if gr+1 < s390_num_gr { + val := uint64(s390_general_reg_val(regs, gr))<<32 + uint64(s390_general_reg_val(regs, gr+1)) + iw := unsafe.Pointer(&val) + v := Value{rt, iw, fl | flagIndir} + in = append(in, v) + gr += 2 + } else { + in, ap = s390_add_stackreg(in, ap, rt, off_slot) + gr = s390_num_gr + } + continue argloop + case s390_float_reg: + // In a register, floats are left aligned, but in a + // stack slot they are right aligned. + if fr < s390_num_fr { + val := s390_float_reg_val(regs, fr) + ix := uintptr(unsafe.Pointer(&val)) + v := Value{ + rt, unsafe.Pointer(unsafe.Pointer(ix)), + fl | flagIndir, + } + in = append(in, v) + fr++ + } else { + in, ap = s390_add_stackreg( + in, ap, rt, off_slot) + } + continue argloop + case s390_mem_ptr: + if gr < s390_num_gr { + // Register holding a pointer to memory. + val := s390_general_reg_val(regs, gr) + v := Value{ + rt, unsafe.Pointer(uintptr(val)), + fl | flagIndir} + in = append(in, v) + gr++ + } else { + // Stack slot holding a pointer to memory. + in, ap = s390_add_memarg(in, ap, rt) + } + continue argloop + } + panic("reflect: argtype not handled in MakeFunc:argloop") + } + + // All the real arguments have been found and turned into + // Values. Call the real function. + + out := c.call(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") + } + } + + switch ret_class { + case s390_general_reg, s390_float_reg, s390_general_reg_pair: + // Single return value in a general or floating point register. + v := out[0] + var w uintptr + if v.Kind() == Ptr || v.Kind() == UnsafePointer { + w = uintptr(v.pointer()) + } else { + w = uintptr(loadScalar(v.ptr, v.typ.size)) + if ret_off_reg != 0 { + w = s390ReloadForRegister( + ret_type, w, ret_off_reg) + } + } + if ret_class == s390_float_reg { + regs.f0 = s390_arch_fr_t(uintptr(w)) + } else if ret_class == s390_general_reg { + regs.r2 = s390_arch_gr_t(uintptr(w)) + } else { + regs.r2 = s390_arch_gr_t(uintptr(w) >> 32) + regs.r3 = s390_arch_gr_t(uintptr(w) & 0xffffffff) + } + + case s390_mem_ptr: + // The address of the memory area was passed as a hidden + // parameter in %r2. Multiple return values are always returned + // in an in-memory structure. + ptr := unsafe.Pointer(uintptr(regs.r2)) + 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) { + *(*unsafe.Pointer)(addr) = v.ptr + } else { + memmove(addr, v.ptr, typ.size) + } + off += typ.size + } + + case s390_empty: + } + + return +} + +// The s390_add_stackreg function adds an argument passed on the +// stack that could be passed in a register. +func s390_add_stackreg(in []Value, ap uintptr, rt *rtype, offset uintptr) ([]Value, uintptr) { + // If we're not already at the beginning of a stack slot, round up to + // the beginning of the next one. + ap = align(ap, s390_arch_stack_slot_align) + // If offset is > 0, the data is right aligned on the stack slot. + ap += offset + + // 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()<