From: Ian Lance Taylor Date: Tue, 4 Nov 2014 22:39:30 +0000 (+0000) Subject: libgo: add s390 support X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=1fec5f5274d7e232b75fc400321ed64be46766c3;p=gcc.git 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 --- diff --git a/libgo/Makefile.am b/libgo/Makefile.am index 3a42250eee7..79cfdd85fb3 100644 --- a/libgo/Makefile.am +++ b/libgo/Makefile.am @@ -943,11 +943,26 @@ go_reflect_makefunc_file = \ go_reflect_makefunc_s_file = \ go/reflect/makefunc_386.S else +if LIBGO_IS_S390 +go_reflect_makefunc_file = \ + go/reflect/makefuncgo_s390.go +go_reflect_makefunc_s_file = \ + go/reflect/makefunc_s390.c +else +if LIBGO_IS_S390X +go_reflect_makefunc_file = \ + go/reflect/makefuncgo_s390x.go \ + go/reflect/makefuncgo_s390.go +go_reflect_makefunc_s_file = \ + go/reflect/makefunc_s390.c +else go_reflect_makefunc_file = go_reflect_makefunc_s_file = \ go/reflect/makefunc_dummy.c endif endif +endif +endif go_reflect_files = \ go/reflect/deepequal.go \ diff --git a/libgo/Makefile.in b/libgo/Makefile.in index 97bc9387f71..f5b5e70c007 100644 --- a/libgo/Makefile.in +++ b/libgo/Makefile.in @@ -1104,15 +1104,28 @@ go_path_files = \ go/path/match.go \ go/path/path.go -@LIBGO_IS_386_FALSE@@LIBGO_IS_X86_64_FALSE@go_reflect_makefunc_file = +@LIBGO_IS_386_FALSE@@LIBGO_IS_S390X_FALSE@@LIBGO_IS_S390_FALSE@@LIBGO_IS_X86_64_FALSE@go_reflect_makefunc_file = +@LIBGO_IS_386_FALSE@@LIBGO_IS_S390X_TRUE@@LIBGO_IS_S390_FALSE@@LIBGO_IS_X86_64_FALSE@go_reflect_makefunc_file = \ +@LIBGO_IS_386_FALSE@@LIBGO_IS_S390X_TRUE@@LIBGO_IS_S390_FALSE@@LIBGO_IS_X86_64_FALSE@ go/reflect/makefuncgo_s390x.go \ +@LIBGO_IS_386_FALSE@@LIBGO_IS_S390X_TRUE@@LIBGO_IS_S390_FALSE@@LIBGO_IS_X86_64_FALSE@ go/reflect/makefuncgo_s390.go + +@LIBGO_IS_386_FALSE@@LIBGO_IS_S390_TRUE@@LIBGO_IS_X86_64_FALSE@go_reflect_makefunc_file = \ +@LIBGO_IS_386_FALSE@@LIBGO_IS_S390_TRUE@@LIBGO_IS_X86_64_FALSE@ go/reflect/makefuncgo_s390.go + @LIBGO_IS_386_TRUE@@LIBGO_IS_X86_64_FALSE@go_reflect_makefunc_file = \ @LIBGO_IS_386_TRUE@@LIBGO_IS_X86_64_FALSE@ go/reflect/makefuncgo_386.go @LIBGO_IS_X86_64_TRUE@go_reflect_makefunc_file = \ @LIBGO_IS_X86_64_TRUE@ go/reflect/makefuncgo_amd64.go -@LIBGO_IS_386_FALSE@@LIBGO_IS_X86_64_FALSE@go_reflect_makefunc_s_file = \ -@LIBGO_IS_386_FALSE@@LIBGO_IS_X86_64_FALSE@ go/reflect/makefunc_dummy.c +@LIBGO_IS_386_FALSE@@LIBGO_IS_S390X_FALSE@@LIBGO_IS_S390_FALSE@@LIBGO_IS_X86_64_FALSE@go_reflect_makefunc_s_file = \ +@LIBGO_IS_386_FALSE@@LIBGO_IS_S390X_FALSE@@LIBGO_IS_S390_FALSE@@LIBGO_IS_X86_64_FALSE@ go/reflect/makefunc_dummy.c + +@LIBGO_IS_386_FALSE@@LIBGO_IS_S390X_TRUE@@LIBGO_IS_S390_FALSE@@LIBGO_IS_X86_64_FALSE@go_reflect_makefunc_s_file = \ +@LIBGO_IS_386_FALSE@@LIBGO_IS_S390X_TRUE@@LIBGO_IS_S390_FALSE@@LIBGO_IS_X86_64_FALSE@ go/reflect/makefunc_s390.c + +@LIBGO_IS_386_FALSE@@LIBGO_IS_S390_TRUE@@LIBGO_IS_X86_64_FALSE@go_reflect_makefunc_s_file = \ +@LIBGO_IS_386_FALSE@@LIBGO_IS_S390_TRUE@@LIBGO_IS_X86_64_FALSE@ go/reflect/makefunc_s390.c @LIBGO_IS_386_TRUE@@LIBGO_IS_X86_64_FALSE@go_reflect_makefunc_s_file = \ @LIBGO_IS_386_TRUE@@LIBGO_IS_X86_64_FALSE@ go/reflect/makefunc_386.S diff --git a/libgo/configure b/libgo/configure index 7984e862e64..68161f65902 100755 --- a/libgo/configure +++ b/libgo/configure @@ -631,6 +631,10 @@ LIBGO_IS_SPARC64_FALSE LIBGO_IS_SPARC64_TRUE LIBGO_IS_SPARC_FALSE LIBGO_IS_SPARC_TRUE +LIBGO_IS_S390X_FALSE +LIBGO_IS_S390X_TRUE +LIBGO_IS_S390_FALSE +LIBGO_IS_S390_TRUE LIBGO_IS_PPC64_FALSE LIBGO_IS_PPC64_TRUE LIBGO_IS_PPC_FALSE @@ -11118,7 +11122,7 @@ else lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext <<_LT_EOF -#line 11121 "configure" +#line 11125 "configure" #include "confdefs.h" #if HAVE_DLFCN_H @@ -11224,7 +11228,7 @@ else lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext <<_LT_EOF -#line 11227 "configure" +#line 11231 "configure" #include "confdefs.h" #if HAVE_DLFCN_H @@ -13620,6 +13624,8 @@ is_m68k=no mips_abi=unknown is_ppc=no is_ppc64=no +is_s390=no +is_s390x=no is_sparc=no is_sparc64=no is_x86_64=no @@ -13739,6 +13745,26 @@ rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext GOARCH=ppc64 fi ;; + s390*-*-*) + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#if defined(__s390x__) +#error 64-bit +#endif +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + is_s390=yes +else + is_s390x=yes +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + if test "$is_s390" = "yes"; then + GOARCH=s390 + else + GOARCH=s390x + fi + ;; sparc*-*-*) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ @@ -13856,6 +13882,22 @@ else LIBGO_IS_PPC64_FALSE= fi + if test $is_s390 = yes; then + LIBGO_IS_S390_TRUE= + LIBGO_IS_S390_FALSE='#' +else + LIBGO_IS_S390_TRUE='#' + LIBGO_IS_S390_FALSE= +fi + + if test $is_s390x = yes; then + LIBGO_IS_S390X_TRUE= + LIBGO_IS_S390X_FALSE='#' +else + LIBGO_IS_S390X_TRUE='#' + LIBGO_IS_S390X_FALSE= +fi + if test $is_sparc = yes; then LIBGO_IS_SPARC_TRUE= LIBGO_IS_SPARC_FALSE='#' @@ -15630,6 +15672,14 @@ if test -z "${LIBGO_IS_PPC64_TRUE}" && test -z "${LIBGO_IS_PPC64_FALSE}"; then as_fn_error "conditional \"LIBGO_IS_PPC64\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi +if test -z "${LIBGO_IS_S390_TRUE}" && test -z "${LIBGO_IS_S390_FALSE}"; then + as_fn_error "conditional \"LIBGO_IS_S390\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${LIBGO_IS_S390X_TRUE}" && test -z "${LIBGO_IS_S390X_FALSE}"; then + as_fn_error "conditional \"LIBGO_IS_S390X\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi if test -z "${LIBGO_IS_SPARC_TRUE}" && test -z "${LIBGO_IS_SPARC_FALSE}"; then as_fn_error "conditional \"LIBGO_IS_SPARC\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 diff --git a/libgo/configure.ac b/libgo/configure.ac index 0469b89a98a..d651827fee5 100644 --- a/libgo/configure.ac +++ b/libgo/configure.ac @@ -194,6 +194,8 @@ is_m68k=no mips_abi=unknown is_ppc=no is_ppc64=no +is_s390=no +is_s390x=no is_sparc=no is_sparc64=no is_x86_64=no @@ -271,6 +273,18 @@ changequote([,])dnl GOARCH=ppc64 fi ;; + s390*-*-*) + AC_COMPILE_IFELSE([ +#if defined(__s390x__) +#error 64-bit +#endif], +[is_s390=yes], [is_s390x=yes]) + if test "$is_s390" = "yes"; then + GOARCH=s390 + else + GOARCH=s390x + fi + ;; sparc*-*-*) AC_COMPILE_IFELSE([ #if defined(__sparcv9) || defined(__arch64__) @@ -296,6 +310,8 @@ AM_CONDITIONAL(LIBGO_IS_MIPSN64, test $mips_abi = n64) AM_CONDITIONAL(LIBGO_IS_MIPSO64, test $mips_abi = o64) AM_CONDITIONAL(LIBGO_IS_PPC, test $is_ppc = yes) AM_CONDITIONAL(LIBGO_IS_PPC64, test $is_ppc64 = yes) +AM_CONDITIONAL(LIBGO_IS_S390, test $is_s390 = yes) +AM_CONDITIONAL(LIBGO_IS_S390X, test $is_s390x = yes) AM_CONDITIONAL(LIBGO_IS_SPARC, test $is_sparc = yes) AM_CONDITIONAL(LIBGO_IS_SPARC64, test $is_sparc64 = yes) AM_CONDITIONAL(LIBGO_IS_X86_64, test $is_x86_64 = yes) diff --git a/libgo/go/debug/elf/elf.go b/libgo/go/debug/elf/elf.go index 51319c0ddc4..a7986a57535 100644 --- a/libgo/go/debug/elf/elf.go +++ b/libgo/go/debug/elf/elf.go @@ -1340,6 +1340,72 @@ var rppc64Strings = []intName{ func (i R_PPC64) String() string { return stringName(uint32(i), rppc64Strings, false) } func (i R_PPC64) GoString() string { return stringName(uint32(i), rppc64Strings, true) } +// Relocation types for s390 +type R_390 int + +const ( + R_390_NONE R_390 = 0 + R_390_8 R_390 = 1 + R_390_12 R_390 = 2 + R_390_16 R_390 = 3 + R_390_32 R_390 = 4 + R_390_PC32 R_390 = 5 + R_390_GOT12 R_390 = 6 + R_390_GOT32 R_390 = 7 + R_390_PLT32 R_390 = 8 + R_390_COPY R_390 = 9 + R_390_GLOB_DAT R_390 = 10 + R_390_JMP_SLOT R_390 = 11 + R_390_RELATIVE R_390 = 12 + R_390_GOTOFF R_390 = 13 + R_390_GOTPC R_390 = 14 + R_390_GOT16 R_390 = 15 + R_390_PC16 R_390 = 16 + R_390_PC16DBL R_390 = 17 + R_390_PLT16DBL R_390 = 18 + R_390_PC32DBL R_390 = 19 + R_390_PLT32DBL R_390 = 20 + R_390_GOTPCDBL R_390 = 21 + R_390_64 R_390 = 22 + R_390_PC64 R_390 = 23 + R_390_GOT64 R_390 = 24 + R_390_PLT64 R_390 = 25 + R_390_GOTENT R_390 = 26 +) + +var r390Strings = []intName{ + {0, "R_390_NONE"}, + {1, "R_390_8"}, + {2, "R_390_12"}, + {3, "R_390_16"}, + {4, "R_390_32"}, + {5, "R_390_PC32"}, + {6, "R_390_GOT12"}, + {7, "R_390_GOT32"}, + {8, "R_390_PLT32"}, + {9, "R_390_COPY"}, + {10, "R_390_GLOB_DAT"}, + {11, "R_390_JMP_SLOT"}, + {12, "R_390_RELATIVE"}, + {13, "R_390_GOTOFF"}, + {14, "R_390_GOTPC"}, + {15, "R_390_GOT16"}, + {16, "R_390_PC16"}, + {17, "R_390_PC16DBL"}, + {18, "R_390_PLT16DBL"}, + {19, "R_390_PC32DBL"}, + {20, "R_390_PLT32DBL"}, + {21, "R_390_GOTPCDBL"}, + {22, "R_390_64"}, + {23, "R_390_PC64"}, + {24, "R_390_GOT64"}, + {25, "R_390_PLT64"}, + {26, "R_390_GOTENT"}, +} + +func (i R_390) String() string { return stringName(uint32(i), r390Strings, false) } +func (i R_390) GoString() string { return stringName(uint32(i), r390Strings, true) } + // Relocation types for SPARC. type R_SPARC int diff --git a/libgo/go/debug/elf/file.go b/libgo/go/debug/elf/file.go index 81ceb9ef741..0c8dff506f3 100644 --- a/libgo/go/debug/elf/file.go +++ b/libgo/go/debug/elf/file.go @@ -528,6 +528,9 @@ func (f *File) applyRelocations(dst []byte, rels []byte) error { if f.Class == ELFCLASS64 && f.Machine == EM_PPC64 { return f.applyRelocationsPPC64(dst, rels) } + if f.Class == ELFCLASS64 && f.Machine == EM_S390 { + return f.applyRelocationsS390x(dst, rels) + } return errors.New("not implemented") } @@ -659,6 +662,47 @@ func (f *File) applyRelocationsPPC64(dst []byte, rels []byte) error { return nil } +func (f *File) applyRelocationsS390x(dst []byte, rels []byte) error { + // 24 is the size of Rela64. + if len(rels)%24 != 0 { + return errors.New("length of relocation section is not a multiple of 24") + } + + symbols, _, err := f.getSymbols(SHT_SYMTAB) + if err != nil { + return err + } + + b := bytes.NewBuffer(rels) + var rela Rela64 + + for b.Len() > 0 { + binary.Read(b, f.ByteOrder, &rela) + symNo := rela.Info >> 32 + t := R_390(rela.Info & 0xffff) + + if symNo == 0 || symNo > uint64(len(symbols)) { + continue + } + sym := &symbols[symNo-1] + + switch t { + case R_390_64: + if rela.Off+8 >= uint64(len(dst)) || rela.Addend < 0 { + continue + } + f.ByteOrder.PutUint64(dst[rela.Off:rela.Off+8], uint64(rela.Addend)+uint64(sym.Value)) + case R_390_32: + if rela.Off+4 >= uint64(len(dst)) || rela.Addend < 0 { + continue + } + f.ByteOrder.PutUint32(dst[rela.Off:rela.Off+4], uint32(rela.Addend)+uint32(sym.Value)) + } + } + + return nil +} + func (f *File) DWARF() (*dwarf.Data, error) { // There are many other DWARF sections, but these // are the required ones, and the debug/dwarf package @@ -681,7 +725,7 @@ func (f *File) DWARF() (*dwarf.Data, error) { // If there's a relocation table for .debug_info, we have to process it // now otherwise the data in .debug_info is invalid for x86-64 objects. rela := f.Section(".rela.debug_info") - if rela != nil && rela.Type == SHT_RELA && (f.Machine == EM_X86_64 || f.Machine == EM_PPC64) { + if rela != nil && rela.Type == SHT_RELA && (f.Machine == EM_X86_64 || f.Machine == EM_PPC64 || f.Machine == EM_S390) { data, err := rela.Data() if err != nil { return nil, err diff --git a/libgo/go/go/build/build.go b/libgo/go/go/build/build.go index 4df0b8983ac..1032c93c7e2 100644 --- a/libgo/go/go/build/build.go +++ b/libgo/go/go/build/build.go @@ -268,6 +268,8 @@ var cgoEnabled = map[string]bool{ "linux/386": true, "linux/amd64": true, "linux/arm": true, + "linux/s390": true, + "linux/s390x": true, "netbsd/386": true, "netbsd/amd64": true, "netbsd/arm": true, diff --git a/libgo/go/go/build/syslist.go b/libgo/go/go/build/syslist.go index b1d5bbe4f44..84712bd91fc 100644 --- a/libgo/go/go/build/syslist.go +++ b/libgo/go/go/build/syslist.go @@ -5,4 +5,4 @@ package build const goosList = "darwin dragonfly freebsd linux nacl netbsd openbsd plan9 solaris windows " -const goarchList = "386 amd64 amd64p32 arm arm64 alpha m68k mipso32 mipsn32 mipsn64 mipso64 ppc ppc64 sparc sparc64 " +const goarchList = "386 amd64 amd64p32 arm arm64 alpha m68k mipso32 mipsn32 mipsn64 mipso64 ppc ppc64 s390 s390x sparc sparc64 " 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()<