compiler: make value method of direct interface type takes pointer

Currently, a value method of a direct interface type takes the
value of the receiver, which is pointer shaped, as the first
parameter. When this method is called through interface, we
actually pass the interface data as a pointer. On most platforms
this is ok, as the underlying calling convention is the same,
except that on SPARC32, the calling convention is actually
different.

This CL changes the method function actually takes a pointer.
The function will convert the pointer to the pointer-shaped
receiver type (a no-op conversion from machine code's aspect).
For a direct call, in the caller we convert the receiver to a
pointer (also no-op conversion) before invoking the method. For
an interface call, we pass the pointer as before. This way, it is
consistent that we always pass a pointer.

Hopefully this fixes SPARC32 build and https://gcc.gnu.org/PR90482.

Change-Id: I67808afcf847fb2ebe2bbcf74df158e7887f81cd
Reviewed-on: https://go-review.googlesource.com/c/gofrontend/+/177758
Reviewed-by: Ian Lance Taylor <iant@golang.org>
diff --git a/go/expressions.cc b/go/expressions.cc
index dd559b4..ad898b7 100644
--- a/go/expressions.cc
+++ b/go/expressions.cc
@@ -11263,6 +11263,16 @@
   else
     has_closure_arg = true;
 
+  Expression* first_arg = NULL;
+  if (!is_interface_method && fntype->is_method())
+    {
+      first_arg = this->args_->front();
+      if (first_arg->type()->points_to() == NULL
+          && first_arg->type()->is_direct_iface_type())
+        first_arg = Expression::unpack_direct_iface(first_arg,
+                                                    first_arg->location());
+    }
+
   int nargs;
   std::vector<Bexpression*> fn_args;
   if (this->args_ == NULL || this->args_->empty())
@@ -11279,7 +11289,7 @@
 		&& this->args_->size() == 1);
       nargs = 1;
       fn_args.resize(1);
-      fn_args[0] = this->args_->front()->get_backend(context);
+      fn_args[0] = first_arg->get_backend(context);
     }
   else
     {
@@ -11294,7 +11304,7 @@
       Expression_list::const_iterator pe = this->args_->begin();
       if (!is_interface_method && fntype->is_method())
 	{
-          fn_args[i] = (*pe)->get_backend(context);
+          fn_args[i] = first_arg->get_backend(context);
 	  ++pe;
 	  ++i;
 	}
diff --git a/go/expressions.h b/go/expressions.h
index 21a214d..3527d7a 100644
--- a/go/expressions.h
+++ b/go/expressions.h
@@ -1063,6 +1063,11 @@
   static Expression*
   pack_direct_iface(Type*, Expression*, Location);
 
+  // Return an expression of the underlying pointer for a direct interface
+  // type (the opposite of pack_direct_iface).
+  static Expression*
+  unpack_direct_iface(Expression*, Location);
+
   // Dump an expression to a dump constext.
   void
   dump_expression(Ast_dump_context*) const;
@@ -1231,9 +1236,6 @@
   }
 
   static Expression*
-  unpack_direct_iface(Expression*, Location);
-
-  static Expression*
   get_interface_type_descriptor(Expression*);
 
   static Expression*
diff --git a/go/gogo.cc b/go/gogo.cc
index 02120f2..e94c567 100644
--- a/go/gogo.cc
+++ b/go/gogo.cc
@@ -6052,9 +6052,10 @@
 
 	  // We always pass the receiver to a method as a pointer.  If
 	  // the receiver is declared as a non-pointer type, then we
-	  // copy the value into a local variable.
+	  // copy the value into a local variable.  For direct interface
+          // type we pack the pointer into the type.
 	  if ((*p)->var_value()->is_receiver()
-	      && !(*p)->var_value()->type()->is_direct_iface_type())
+              && (*p)->var_value()->type()->points_to() == NULL)
 	    {
 	      std::string name = (*p)->name() + ".pointer";
 	      Type* var_type = (*p)->var_value()->type();
@@ -6066,14 +6067,19 @@
               parm_bvar = parm_no->get_backend_variable(gogo, named_function);
 
               vars.push_back(bvar);
-	      Expression* parm_ref =
+
+              Expression* parm_ref =
                   Expression::make_var_reference(parm_no, loc);
-              parm_ref =
-                  Expression::make_dereference(parm_ref,
-                                               Expression::NIL_CHECK_NEEDED,
-                                               loc);
-	      if ((*p)->var_value()->is_in_heap())
-		parm_ref = Expression::make_heap_expression(parm_ref, loc);
+              Type* recv_type = (*p)->var_value()->type();
+              if (recv_type->is_direct_iface_type())
+                parm_ref = Expression::pack_direct_iface(recv_type, parm_ref, loc);
+              else
+                parm_ref =
+                    Expression::make_dereference(parm_ref,
+                                                 Expression::NIL_CHECK_NEEDED,
+                                                 loc);
+              if ((*p)->var_value()->is_in_heap())
+                parm_ref = Expression::make_heap_expression(parm_ref, loc);
               var_inits.push_back(parm_ref->get_backend(&context));
 	    }
 	  else if ((*p)->var_value()->is_in_heap())
@@ -7531,7 +7537,7 @@
       else
 	{
 	  bool is_parameter = this->is_parameter_;
-	  if (this->is_receiver_ && !type->is_direct_iface_type())
+	  if (this->is_receiver_ && type->points_to() == NULL)
 	    is_parameter = false;
 	  if (this->is_in_heap())
 	    {
diff --git a/go/types.cc b/go/types.cc
index 371b65f..5260626 100644
--- a/go/types.cc
+++ b/go/types.cc
@@ -4760,10 +4760,15 @@
           // We always pass the address of the receiver parameter, in
           // order to make interface calls work with unknown types,
           // except for direct interface types where the interface call
-          // actually passes value.
+          // actually passes the underlying pointer of the value.
           Type* rtype = this->receiver_->type();
-          if (!rtype->is_direct_iface_type())
-            rtype = Type::make_pointer_type(rtype);
+          if (rtype->points_to() == NULL)
+            {
+              if (rtype->is_direct_iface_type())
+                rtype = Type::make_pointer_type(Type::make_void_type());
+              else
+                rtype = Type::make_pointer_type(rtype);
+            }
           breceiver.btype = rtype->get_backend(gogo);
           breceiver.location = this->receiver_->location();
         }