runtime: revert eqtype for AIX
authorClément Chigot <chigot.c@gmail.com>
Fri, 29 May 2020 09:39:42 +0000 (11:39 +0200)
committerIan Lance Taylor <iant@golang.org>
Mon, 10 Aug 2020 23:45:16 +0000 (16:45 -0700)
AIX linker is not able to merge identical type descriptors in a single
symbol if there are coming from different object or shared object files.
This results into several pointers referencing the same type
descriptors.
Thus, eqtype is needed to ensure that these different symbols will be
considered as the same type descriptor.

Fixes golang/go#39276

gcc/go/ChangeLog:

* go-c.h (struct go_create_gogo_args): Add need_eqtype field.
* go-lang.c (go_langhook_init): Set need_eqtype.

Reviewed-on: https://go-review.googlesource.com/c/gofrontend/+/235697

13 files changed:
gcc/go/go-c.h
gcc/go/go-lang.c
gcc/go/gofrontend/MERGE
gcc/go/gofrontend/expressions.cc
gcc/go/gofrontend/expressions.h
gcc/go/gofrontend/go.cc
gcc/go/gofrontend/gogo.cc
gcc/go/gofrontend/gogo.h
gcc/go/gofrontend/runtime.def
libgo/go/runtime/alg.go
libgo/go/runtime/eqtype.go [new file with mode: 0644]
libgo/go/runtime/eqtype_aix_gccgo.go [new file with mode: 0644]
libgo/go/runtime/iface.go

index 7e46f4b0a575b5447d50c5151171cef121cb6343..3a005d5dd69d46bb5d7a23d8a163f5b9a951f1f2 100644 (file)
@@ -50,6 +50,7 @@ struct go_create_gogo_args
   const char* debug_escape_hash;
   int64_t nil_check_size_threshold;
   bool debug_optimization;
+  bool need_eqtype;
 };
 
 extern void go_create_gogo (const struct go_create_gogo_args*);
index 387694e4f56d610aeda03e52059fc3aab83402df..2cfb41042bddfa7bcdc3c33bc92a805e120fdf26 100644 (file)
@@ -119,6 +119,7 @@ go_langhook_init (void)
   args.debug_escape_hash = go_debug_escape_hash;
   args.nil_check_size_threshold = TARGET_AIX ? -1 : 4096;
   args.debug_optimization = go_debug_optimization;
+  args.need_eqtype = TARGET_AIX ? true : false;
   args.linemap = go_get_linemap();
   args.backend = go_get_backend();
   go_create_gogo (&args);
index b6089f3f01d2b692ec0cd3c243b113fe0ca77add..93aa18cec06758b7a013c07fa47e04bd9f14251f 100644 (file)
@@ -1,4 +1,4 @@
-307665073fce992ea8112f74b91954e770afcc70
+c512af85eb8c75a759b5e4fc6b72041fe09b75f1
 
 The first line of this file holds the git revision number of the last
 merge done from the gofrontend repository.
index 7e7fb8c7313562f65f6006557063f93a0159799b..d295fd10136aa2b6da9eb819841d5fa5f56bd0bc 100644 (file)
@@ -208,7 +208,7 @@ Expression::is_same_variable(Expression* a, Expression* b)
 // assignment.
 
 Expression*
-Expression::convert_for_assignment(Gogo*, Type* lhs_type,
+Expression::convert_for_assignment(Gogo* gogo, Type* lhs_type,
                                   Expression* rhs, Location location)
 {
   Type* rhs_type = rhs->type();
@@ -229,7 +229,7 @@ Expression::convert_for_assignment(Gogo*, Type* lhs_type,
                                                         location);
     }
   else if (!are_identical && rhs_type->interface_type() != NULL)
-    return Expression::convert_interface_to_type(lhs_type, rhs, location);
+    return Expression::convert_interface_to_type(gogo, lhs_type, rhs, location);
   else if (lhs_type->is_slice_type() && rhs_type->is_nil_type())
     {
       // Assigning nil to a slice.
@@ -498,7 +498,7 @@ Expression::convert_interface_to_interface(Type *lhs_type, Expression* rhs,
 // non-interface type.
 
 Expression*
-Expression::convert_interface_to_type(Type *lhs_type, Expression* rhs,
+Expression::convert_interface_to_type(Gogo* gogo, Type *lhs_type, Expression* rhs,
                                       Location location)
 {
   // We are going to evaluate RHS multiple times.
@@ -507,8 +507,11 @@ Expression::convert_interface_to_type(Type *lhs_type, Expression* rhs,
   // Build an expression to check that the type is valid.  It will
   // panic with an appropriate runtime type error if the type is not
   // valid.
-  // (lhs_type != rhs_type ? panicdottype(lhs_type, rhs_type, inter_type) :
-  //    nil /*dummy*/)
+  // (lhs_type == rhs_type ? nil /*dummy*/ :
+  //    panicdottype(lhs_type, rhs_type, inter_type))
+  // For some Oses, we need to call runtime.eqtype instead of
+  // lhs_type == rhs_type, as we may have unmerged type descriptors
+  // from shared libraries.
   Expression* lhs_type_expr = Expression::make_type_descriptor(lhs_type,
                                                                 location);
   Expression* rhs_descriptor =
@@ -518,15 +521,23 @@ Expression::convert_interface_to_type(Type *lhs_type, Expression* rhs,
   Expression* rhs_inter_expr = Expression::make_type_descriptor(rhs_type,
                                                                 location);
 
-  Expression* cond = Expression::make_binary(OPERATOR_NOTEQ, lhs_type_expr,
-                                             rhs_descriptor, location);
+  Expression* cond;
+  if (gogo->need_eqtype()) {
+    cond = Runtime::make_call(Runtime::EQTYPE, location,
+                              2, lhs_type_expr,
+                              rhs_descriptor);
+  } else {
+    cond = Expression::make_binary(OPERATOR_EQEQ, lhs_type_expr,
+                                   rhs_descriptor, location);
+  }
+
   rhs_descriptor = Expression::get_interface_type_descriptor(rhs);
   Expression* panic = Runtime::make_call(Runtime::PANICDOTTYPE, location,
                                          3, lhs_type_expr->copy(),
                                          rhs_descriptor,
                                          rhs_inter_expr);
   Expression* nil = Expression::make_nil(location);
-  Expression* check = Expression::make_conditional(cond, panic, nil,
+  Expression* check = Expression::make_conditional(cond, nil, panic,
                                                    location);
 
   // If the conversion succeeds, pull out the value.
index a4f892acaf782dcb68cc3bafd7906a6f0b8fec6e..acb2732bddea4ba6e59e33f3617900a7c1077119 100644 (file)
@@ -1273,7 +1273,7 @@ class Expression
   }
 
   static Expression*
-  convert_interface_to_type(Type*, Expression*, Location);
+  convert_interface_to_type(Gogo*, Type*, Expression*, Location);
 
   static Expression*
   import_identifier(Import_function_body*, Location);
index 12205bd5aa726fbfcce2ebcf7f4d085c678135d4..e026d6592ba0955fee6ec097d627a3467449a858 100644 (file)
@@ -46,6 +46,8 @@ go_create_gogo(const struct go_create_gogo_args* args)
   ::gogo->set_nil_check_size_threshold(args->nil_check_size_threshold);
   if (args->debug_optimization)
     ::gogo->set_debug_optimization(args->debug_optimization);
+  if (args->need_eqtype)
+    ::gogo->set_need_eqtype(args->need_eqtype);
 }
 
 // Parse the input files.
index 4c8c55fcb14f1d3123c141c11c6e31be11e4e3f4..13de74bc8700e263f120c45c159f6f654aec7eef 100644 (file)
@@ -57,6 +57,7 @@ Gogo::Gogo(Backend* backend, Linemap* linemap, int, int pointer_size)
     debug_escape_level_(0),
     debug_optimization_(false),
     nil_check_size_threshold_(4096),
+    need_eqtype_(false),
     verify_types_(),
     interface_types_(),
     specific_type_functions_(),
index 2fb8a3aeb4341400952dd27e505ce779106abea9..45be1732aa0fa97f39323bf1104f7023cbc9f77d 100644 (file)
@@ -360,6 +360,17 @@ class Gogo
   set_nil_check_size_threshold(int64_t bytes)
   { this->nil_check_size_threshold_ = bytes; }
 
+  // Return whether runtime.eqtype calls are needed when comparing
+  // type descriptors.
+  bool
+  need_eqtype() const
+  { return this->need_eqtype_; }
+
+  // Set if calls to runtime.eqtype are needed.
+  void
+  set_need_eqtype(bool b)
+  { this->need_eqtype_ = b; }
+
   // Import a package.  FILENAME is the file name argument, LOCAL_NAME
   // is the local name to give to the package.  If LOCAL_NAME is empty
   // the declarations are added to the global scope.
@@ -1161,6 +1172,9 @@ class Gogo
   bool debug_optimization_;
   // Nil-check size threshhold.
   int64_t nil_check_size_threshold_;
+  // Whether runtime.eqtype calls are needed when comparing type
+  // descriptors.
+  bool need_eqtype_;
   // A list of types to verify.
   std::vector<Type*> verify_types_;
   // A list of interface types defined while parsing.
index a9500797b4d4b196ebcc2e507bed1427433e50db..0796cbaadceadedc779e90171f9920af03c58da5 100644 (file)
@@ -340,6 +340,9 @@ DEF_GO_RUNTIME(PANICDOTTYPE, "runtime.panicdottype", P3(TYPE, TYPE, TYPE),
 // Return whether we can convert a type to an interface type.
 DEF_GO_RUNTIME(IFACET2IP, "runtime.ifaceT2Ip", P2(TYPE, TYPE), R1(BOOL))
 
+// Compare two type descriptors for equality.
+DEF_GO_RUNTIME(EQTYPE, "runtime.eqtype", P2(TYPE, TYPE), R1(BOOL))
+
 // Compare two empty interface values.
 DEF_GO_RUNTIME(EFACEEQ, "runtime.efaceeq", P2(EFACE, EFACE), R1(BOOL))
 
index 95f02aa31c4425d4f705cb0f0e303b38d1412b68..b5b22cfd0f8c33944eefb1426abddc1d579d203c 100644 (file)
@@ -276,7 +276,7 @@ func nilinterequal(p, q unsafe.Pointer) bool {
 }
 func efaceeq(x, y eface) bool {
        t := x._type
-       if t != y._type {
+       if !eqtype(t, y._type) {
                return false
        }
        if t == nil {
@@ -301,7 +301,7 @@ func ifaceeq(x, y iface) bool {
                return false
        }
        t := *(**_type)(xtab)
-       if t != *(**_type)(y.tab) {
+       if !eqtype(t, *(**_type)(y.tab)) {
                return false
        }
        eq := t.equal
@@ -322,7 +322,7 @@ func ifacevaleq(x iface, t *_type, p unsafe.Pointer) bool {
                return false
        }
        xt := *(**_type)(x.tab)
-       if xt != t {
+       if !eqtype(xt, t) {
                return false
        }
        eq := t.equal
@@ -343,7 +343,7 @@ func ifaceefaceeq(x iface, y eface) bool {
                return false
        }
        xt := *(**_type)(x.tab)
-       if xt != y._type {
+       if !eqtype(xt, y._type) {
                return false
        }
        eq := xt.equal
@@ -360,7 +360,7 @@ func efacevaleq(x eface, t *_type, p unsafe.Pointer) bool {
        if x._type == nil {
                return false
        }
-       if x._type != t {
+       if !eqtype(x._type, t) {
                return false
        }
        eq := t.equal
diff --git a/libgo/go/runtime/eqtype.go b/libgo/go/runtime/eqtype.go
new file mode 100644 (file)
index 0000000..71d64ee
--- /dev/null
@@ -0,0 +1,21 @@
+// Copyright 2020 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.
+
+// +build !aix,gccgo
+
+package runtime
+
+import (
+       _ "unsafe"
+)
+
+// go:linkname is required as eqtype is a compiler-called
+// function on some OSes.
+//
+//go:linkname eqtype
+
+// Return whether two type descriptors are equal.
+func eqtype(t1, t2 *_type) bool {
+       return t1 == t2
+}
diff --git a/libgo/go/runtime/eqtype_aix_gccgo.go b/libgo/go/runtime/eqtype_aix_gccgo.go
new file mode 100644 (file)
index 0000000..6d32022
--- /dev/null
@@ -0,0 +1,32 @@
+// Copyright 2020 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.
+
+// +build aix,gccgo
+
+package runtime
+
+import (
+       _ "unsafe"
+)
+
+// eqtype is a compiler-called function.
+//
+//go:linkname eqtype
+
+// Return whether two type descriptors are equal.
+// This is gccgo-specific, as some linkers are not able
+// to merge identical type descriptors coming from
+// different object or shared object files.
+func eqtype(t1, t2 *_type) bool {
+       switch {
+       case t1 == t2:
+               return true
+       case t1 == nil || t2 == nil:
+               return false
+       case t1.kind != t2.kind || t1.hash != t2.hash:
+               return false
+       default:
+               return t1.string() == t2.string()
+       }
+}
index 877e191a8557cf16f510ea3761c5ded809621b55..5667ddb6b95b19c393017ff1da388fffa97490c8 100644 (file)
@@ -232,7 +232,7 @@ func (m *itab) init() string {
                        ri++
                }
 
-               if lhsMethod.typ != rhsMethod.mtyp {
+               if !eqtype(lhsMethod.typ, rhsMethod.mtyp) {
                        m.methods[1] = nil
                        return *lhsMethod.name
                }
@@ -406,7 +406,7 @@ func ifaceI2I2(inter *_type, i iface) (iface, bool) {
 
 // Convert an empty interface to a pointer non-interface type.
 func ifaceE2T2P(t *_type, e eface) (unsafe.Pointer, bool) {
-       if t != e._type {
+       if !eqtype(t, e._type) {
                return nil, false
        } else {
                return e.data, true
@@ -415,7 +415,7 @@ func ifaceE2T2P(t *_type, e eface) (unsafe.Pointer, bool) {
 
 // Convert a non-empty interface to a pointer non-interface type.
 func ifaceI2T2P(t *_type, i iface) (unsafe.Pointer, bool) {
-       if i.tab == nil || t != *(**_type)(i.tab) {
+       if i.tab == nil || !eqtype(t, *(**_type)(i.tab)) {
                return nil, false
        } else {
                return i.data, true
@@ -424,7 +424,7 @@ func ifaceI2T2P(t *_type, i iface) (unsafe.Pointer, bool) {
 
 // Convert an empty interface to a non-pointer non-interface type.
 func ifaceE2T2(t *_type, e eface, ret unsafe.Pointer) bool {
-       if t != e._type {
+       if !eqtype(t, e._type) {
                typedmemclr(t, ret)
                return false
        } else {
@@ -439,7 +439,7 @@ func ifaceE2T2(t *_type, e eface, ret unsafe.Pointer) bool {
 
 // Convert a non-empty interface to a non-pointer non-interface type.
 func ifaceI2T2(t *_type, i iface, ret unsafe.Pointer) bool {
-       if i.tab == nil || t != *(**_type)(i.tab) {
+       if i.tab == nil || !eqtype(t, *(**_type)(i.tab)) {
                typedmemclr(t, ret)
                return false
        } else {
@@ -485,7 +485,7 @@ func ifaceT2Ip(to, from *_type) bool {
                        ri++
                }
 
-               if fromMethod.mtyp != toMethod.typ {
+               if !eqtype(fromMethod.mtyp, toMethod.typ) {
                        return false
                }