runtime: revert eqtype for AIX

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

Change-Id: Ia941304d6b3822a78ba5f53f05f9a385029c3f35
Reviewed-on: https://go-review.googlesource.com/c/gofrontend/+/235697
Reviewed-by: Ian Lance Taylor <iant@golang.org>
diff --git a/go/expressions.cc b/go/expressions.cc
index 7e7fb8c..d295fd1 100644
--- a/go/expressions.cc
+++ b/go/expressions.cc
@@ -208,7 +208,7 @@
 // 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 @@
                                                         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 @@
 // 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 @@
   // 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* 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.
diff --git a/go/expressions.h b/go/expressions.h
index a4f892a..acb2732 100644
--- a/go/expressions.h
+++ b/go/expressions.h
@@ -1273,7 +1273,7 @@
   }
 
   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);
diff --git a/go/go.cc b/go/go.cc
index 12205bd..e026d65 100644
--- a/go/go.cc
+++ b/go/go.cc
@@ -46,6 +46,8 @@
   ::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.
diff --git a/go/gogo.cc b/go/gogo.cc
index 4c8c55f..13de74b 100644
--- a/go/gogo.cc
+++ b/go/gogo.cc
@@ -57,6 +57,7 @@
     debug_escape_level_(0),
     debug_optimization_(false),
     nil_check_size_threshold_(4096),
+    need_eqtype_(false),
     verify_types_(),
     interface_types_(),
     specific_type_functions_(),
diff --git a/go/gogo.h b/go/gogo.h
index 2fb8a3a..45be173 100644
--- a/go/gogo.h
+++ b/go/gogo.h
@@ -360,6 +360,17 @@
   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 @@
   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.
diff --git a/go/runtime.def b/go/runtime.def
index a950079..0796cba 100644
--- a/go/runtime.def
+++ b/go/runtime.def
@@ -340,6 +340,9 @@
 // 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))
 
diff --git a/libgo/go/runtime/alg.go b/libgo/go/runtime/alg.go
index 95f02aa..b5b22cf 100644
--- a/libgo/go/runtime/alg.go
+++ b/libgo/go/runtime/alg.go
@@ -276,7 +276,7 @@
 }
 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 @@
 		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 @@
 		return false
 	}
 	xt := *(**_type)(x.tab)
-	if xt != t {
+	if !eqtype(xt, t) {
 		return false
 	}
 	eq := t.equal
@@ -343,7 +343,7 @@
 		return false
 	}
 	xt := *(**_type)(x.tab)
-	if xt != y._type {
+	if !eqtype(xt, y._type) {
 		return false
 	}
 	eq := xt.equal
@@ -360,7 +360,7 @@
 	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
index 0000000..71d64ee
--- /dev/null
+++ b/libgo/go/runtime/eqtype.go
@@ -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
index 0000000..6d32022
--- /dev/null
+++ b/libgo/go/runtime/eqtype_aix_gccgo.go
@@ -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()
+	}
+}
diff --git a/libgo/go/runtime/iface.go b/libgo/go/runtime/iface.go
index 877e191..5667ddb 100644
--- a/libgo/go/runtime/iface.go
+++ b/libgo/go/runtime/iface.go
@@ -232,7 +232,7 @@
 			ri++
 		}
 
-		if lhsMethod.typ != rhsMethod.mtyp {
+		if !eqtype(lhsMethod.typ, rhsMethod.mtyp) {
 			m.methods[1] = nil
 			return *lhsMethod.name
 		}
@@ -406,7 +406,7 @@
 
 // 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 @@
 
 // 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 @@
 
 // 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 @@
 
 // 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 @@
 			ri++
 		}
 
-		if fromMethod.mtyp != toMethod.typ {
+		if !eqtype(fromMethod.mtyp, toMethod.typ) {
 			return false
 		}