diff mbox

[gccgo] Add append builtin function

Message ID mcrk4ktivve.fsf@google.com
State New
Headers show

Commit Message

Ian Lance Taylor Nov. 4, 2010, 3:57 a.m. UTC
A new builtin function, append, was recently added to the Go language.
This patch implements it for gccgo.  Committed to gccgo branch.

Ian
diff mbox

Patch

diff -r 425b90986649 go/expressions.cc
--- a/go/expressions.cc	Wed Nov 03 13:03:58 2010 -0700
+++ b/go/expressions.cc	Wed Nov 03 20:55:37 2010 -0700
@@ -6075,7 +6075,7 @@ 
 {
  public:
   Builtin_call_expression(Gogo* gogo, Expression* fn, Expression_list* args,
-			  source_location location);
+			  bool is_varargs, source_location location);
 
  protected:
   // This overrides Call_expression::do_lower.
@@ -6108,6 +6108,7 @@ 
   {
     return new Builtin_call_expression(this->gogo_, this->fn()->copy(),
 				       this->args()->copy(),
+				       this->is_varargs(),
 				       this->location());
   }
 
@@ -6130,6 +6131,7 @@ 
       BUILTIN_INVALID,
 
       // Predeclared builtin functions.
+      BUILTIN_APPEND,
       BUILTIN_CAP,
       BUILTIN_CLOSE,
       BUILTIN_CLOSED,
@@ -6173,14 +6175,17 @@ 
 Builtin_call_expression::Builtin_call_expression(Gogo* gogo,
 						 Expression* fn,
 						 Expression_list* args,
+						 bool is_varargs,
 						 source_location location)
-  : Call_expression(fn, args, false, location),
+  : Call_expression(fn, args, is_varargs, location),
     gogo_(gogo), code_(BUILTIN_INVALID)
 {
   Func_expression* fnexp = this->fn()->func_expression();
   gcc_assert(fnexp != NULL);
   const std::string& name(fnexp->named_object()->name());
-  if (name == "cap")
+  if (name == "append")
+    this->code_ = BUILTIN_APPEND;
+  else if (name == "cap")
     this->code_ = BUILTIN_CAP;
   else if (name == "close")
     this->code_ = BUILTIN_CLOSE;
@@ -6277,7 +6282,7 @@ 
 // specific expressions.  We also convert to a constant if we can.
 
 Expression*
-Builtin_call_expression::do_lower(Gogo*, Named_object* function, int)
+Builtin_call_expression::do_lower(Gogo* gogo, Named_object* function, int)
 {
   if (this->code_ == BUILTIN_NEW)
     {
@@ -6394,6 +6399,22 @@ 
 				       this->location());
 	}
     }
+  else if (this->code_ == BUILTIN_APPEND)
+    {
+      // Lower the varargs.
+      const Expression_list* args = this->args();
+      if (args == NULL || args->empty())
+	return this;
+      Type* slice_type = args->front()->type();
+      if (!slice_type->is_open_array_type())
+	{
+	  error_at(args->front()->location(),
+		   _("argument 1 must be a slice"));
+	  this->set_is_error();
+	  return this;
+	}
+      return this->lower_varargs(gogo, function, slice_type, 2);
+    }
 
   return this;
 }
@@ -6768,6 +6789,14 @@ 
     case BUILTIN_RECOVER:
       return Type::make_interface_type(NULL, BUILTINS_LOCATION);
 
+    case BUILTIN_APPEND:
+      {
+	const Expression_list* args = this->args();
+	if (args == NULL || args->empty())
+	  return Type::make_error_type();
+	return args->front()->type();
+      }
+
     case BUILTIN_REAL:
     case BUILTIN_IMAG:
       {
@@ -7093,6 +7122,33 @@ 
       }
       break;
 
+    case BUILTIN_APPEND:
+      {
+	const Expression_list* args = this->args();
+	if (args == NULL || args->empty())
+	  {
+	    this->report_error(_("not enough arguments"));
+	    break;
+	  }
+	/* Lowering varargs should have left us with 2 arguments.  */
+	gcc_assert(args->size() == 2);
+	std::string reason;
+	if (!Type::are_assignable(args->front()->type(), args->back()->type(),
+				  &reason))
+	  {
+	    if (reason.empty())
+	      this->report_error(_("arguments 1 and 2 have different types"));
+	    else
+	      {
+		error_at(this->location(),
+			 "arguments 1 and 2 have different types (%s)",
+			 reason.c_str());
+		this->set_is_error();
+	      }
+	  }
+	break;
+      }
+
     case BUILTIN_REAL:
     case BUILTIN_IMAG:
       if (this->check_one_arg())
@@ -7559,6 +7615,36 @@ 
 			       call, len);
       }
 
+    case BUILTIN_APPEND:
+      {
+	const Expression_list* args = this->args();
+	gcc_assert(args != NULL && args->size() == 2);
+	Expression* arg1 = args->front();
+	Expression* arg2 = args->back();
+
+	tree arg1_tree = arg1->get_tree(context);
+	tree arg2_tree = arg2->get_tree(context);
+	if (arg1_tree == error_mark_node || arg2_tree == error_mark_node)
+	  return error_mark_node;
+
+	tree descriptor_tree = arg1->type()->type_descriptor_pointer(gogo);
+
+	// We rebuild the decl each time since the slice types may
+	// change.
+	tree append_fndecl = NULL_TREE;
+	return Gogo::call_builtin(&append_fndecl,
+				  location,
+				  "__go_append",
+				  3,
+				  TREE_TYPE(arg1_tree),
+				  TREE_TYPE(descriptor_tree),
+				  descriptor_tree,
+				  TREE_TYPE(arg1_tree),
+				  arg1_tree,
+				  TREE_TYPE(arg2_tree),
+				  arg2_tree);
+      }
+
     case BUILTIN_REAL:
     case BUILTIN_IMAG:
       {
@@ -7690,7 +7776,7 @@ 
       && fne->named_object()->is_function_declaration()
       && fne->named_object()->func_declaration_value()->type()->is_builtin())
     return new Builtin_call_expression(gogo, this->fn_, this->args_,
-				       this->location());
+				       this->is_varargs_, this->location());
 
   // Handle an argument which is a call to a function which returns
   // multiple results.
@@ -7720,28 +7806,41 @@ 
 
   // Handle a call to a varargs function by packaging up the extra
   // parameters.
-  if (!this->varargs_are_lowered_
-      && this->fn_->type()->function_type() != NULL
+  if (this->fn_->type()->function_type() != NULL
       && this->fn_->type()->function_type()->is_varargs())
-    return this->lower_varargs(gogo, function);
+    {
+      Function_type* fntype = this->fn_->type()->function_type();
+      const Typed_identifier_list* parameters = fntype->parameters();
+      gcc_assert(parameters != NULL && !parameters->empty());
+      Type* varargs_type = parameters->back().type();
+      return this->lower_varargs(gogo, function, varargs_type,
+				 parameters->size());
+    }
 
   return this;
 }
 
-// Lower a call to a varargs function.
+// Lower a call to a varargs function.  FUNCTION is the function in
+// which the call occurs--it's not the function we are calling.
+// VARARGS_TYPE is the type of the varargs parameter, a slice type.
+// PARAM_COUNT is the number of parameters of the function we are
+// calling; the last of these parameters will be the varargs
+// parameter.
 
 Expression*
-Call_expression::lower_varargs(Gogo* gogo, Named_object* function)
-{
+Call_expression::lower_varargs(Gogo* gogo, Named_object* function,
+			       Type* varargs_type, size_t param_count)
+{
+  if (this->varargs_are_lowered_)
+    return this;
+
   source_location loc = this->location();
 
-  Function_type* fntype = this->fn_->type()->function_type();
-  const Typed_identifier_list* parameters = fntype->parameters();
-  gcc_assert(parameters != NULL && !parameters->empty());
-  Type* varargs_type = parameters->back().type();
+  gcc_assert(param_count > 0);
+  gcc_assert(varargs_type->is_open_array_type());
 
   size_t arg_count = this->args_ == NULL ? 0 : this->args_->size();
-  if (arg_count < parameters->size() - 1)
+  if (arg_count < param_count - 1)
     {
       // Not enough arguments; will be caught in check_types.
       return this;
@@ -7752,17 +7851,16 @@ 
   bool push_empty_arg = false;
   if (old_args == NULL || old_args->empty())
     {
-      gcc_assert(parameters->size() == 1);
+      gcc_assert(param_count == 1);
       push_empty_arg = true;
     }
   else
     {
-      Typed_identifier_list::const_iterator pp = parameters->begin();
       Expression_list::const_iterator pa;
       int i = 1;
-      for (pa = old_args->begin(); pa != old_args->end(); ++pa, ++pp, ++i)
-	{
-	  if (pp + 1 == parameters->end())
+      for (pa = old_args->begin(); pa != old_args->end(); ++pa, ++i)
+	{
+	  if (static_cast<size_t>(i) == param_count)
 	    break;
 	  new_args->push_back(*pa);
 	}
@@ -7770,15 +7868,20 @@ 
       // We have reached the varargs parameter.
 
       bool issued_error = false;
-      if (pa != old_args->end()
-	  && pa + 1 == old_args->end()
-	  && (this->is_varargs_
-	      || this->is_compatible_varargs_argument(function, *pa,
-						      varargs_type,
-						      &issued_error)))
+      if (pa == old_args->end())
+	push_empty_arg = true;
+      else if (pa + 1 == old_args->end() && this->is_varargs_)
 	new_args->push_back(*pa);
-      else if (pa == old_args->end())
-	push_empty_arg = true;
+      else if (this->is_varargs_)
+	{
+	  this->report_error("too many arguments");
+	  return this;
+	}
+      else if (pa + 1 == old_args->end()
+	       && this->is_compatible_varargs_argument(function, *pa,
+						       varargs_type,
+						       &issued_error))
+	new_args->push_back(*pa);
       else
 	{
 	  Type* element_type = varargs_type->array_type()->element_type();
diff -r 425b90986649 go/expressions.h
--- a/go/expressions.h	Wed Nov 03 13:03:58 2010 -0700
+++ b/go/expressions.h	Wed Nov 03 20:55:37 2010 -0700
@@ -1197,6 +1197,11 @@ 
   void
   set_recover_arg(Expression*);
 
+  // Whether the last argument is a varargs argument (f(a...)).
+  bool
+  is_varargs() const
+  { return this->is_varargs_; }
+
   // Whether this call is being deferred.
   bool
   is_deferred() const
@@ -1251,10 +1256,12 @@ 
   set_args(Expression_list* args)
   { this->args_ = args; }
 
+  // Let a builtin expression lower varargs.
+  Expression*
+  lower_varargs(Gogo*, Named_object* function, Type* varargs_type,
+		size_t param_count);
+
  private:
-  Expression*
-  lower_varargs(Gogo*, Named_object*);
-
   bool
   is_compatible_varargs_argument(Named_object*, Expression*, Type*, bool*);
 
diff -r 425b90986649 go/gogo.cc
--- a/go/gogo.cc	Wed Nov 03 13:03:58 2010 -0700
+++ b/go/gogo.cc	Wed Nov 03 20:55:37 2010 -0700
@@ -195,6 +195,11 @@ 
   copy_type->set_is_builtin();
   this->globals_->add_function_declaration("copy", NULL, copy_type, loc);
 
+  Function_type* append_type = Type::make_function_type(NULL, NULL, NULL, loc);
+  append_type->set_is_varargs();
+  append_type->set_is_builtin();
+  this->globals_->add_function_declaration("append", NULL, append_type, loc);
+
   Function_type* cmplx_type = Type::make_function_type(NULL, NULL, NULL, loc);
   cmplx_type->set_is_varargs();
   cmplx_type->set_is_builtin();
diff -r 425b90986649 libgo/Makefile.am
--- a/libgo/Makefile.am	Wed Nov 03 13:03:58 2010 -0700
+++ b/libgo/Makefile.am	Wed Nov 03 20:55:37 2010 -0700
@@ -292,6 +292,7 @@ 
 endif
 
 runtime_files = \
+	runtime/go-append.c \
 	runtime/go-assert.c \
 	runtime/go-assert-interface.c \
 	runtime/go-byte-array-to-string.c \
diff -r 425b90986649 libgo/runtime/go-append.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libgo/runtime/go-append.c	Wed Nov 03 20:55:37 2010 -0700
@@ -0,0 +1,62 @@ 
+/* go-append.c -- the go builtin append function.
+
+   Copyright 2010 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 "go-assert.h"
+#include "go-type.h"
+#include "array.h"
+#include "runtime.h"
+#include "malloc.h"
+
+struct __go_open_array
+__go_append (const struct __go_slice_type *type,
+	     struct __go_open_array a, struct __go_open_array b)
+{
+  size_t element_size;
+  unsigned int ucount;
+  int count;
+
+  if (b.__values == NULL || b.__count == 0)
+    return a;
+
+  __go_assert (type->__common.__code == GO_SLICE);
+  element_size = type->__element_type->__size;
+
+  ucount = (unsigned int) a.__count + (unsigned int) b.__count;
+  count = (int) ucount;
+  __go_assert (ucount == (unsigned int) count && count >= a.__count);
+  if (count > a.__capacity)
+    {
+      int m;
+      struct __go_open_array n;
+
+      m = a.__capacity;
+      if (m == 0)
+	m = b.__count;
+      else
+	{
+	  do
+	    {
+	      if (a.__count < 1024)
+		m += m;
+	      else
+		m += m / 4;
+	    }
+	  while (m < count);
+	}
+
+      n.__values = __go_alloc (m * element_size);
+      n.__count = a.__count;
+      n.__capacity = m;
+      __builtin_memcpy (n.__values, a.__values, n.__count * element_size);
+
+      a = n;
+    }
+
+  __builtin_memmove ((char *) a.__values + a.__count * element_size,
+		     b.__values, b.__count * element_size);
+  a.__count = count;
+  return a;
+}