From patchwork Thu Nov 4 03:57:25 2010 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ian Lance Taylor X-Patchwork-Id: 70097 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from sourceware.org (server1.sourceware.org [209.132.180.131]) by ozlabs.org (Postfix) with SMTP id A7B02B6F10 for ; Thu, 4 Nov 2010 14:57:46 +1100 (EST) Received: (qmail 2959 invoked by alias); 4 Nov 2010 03:57:43 -0000 Received: (qmail 2947 invoked by uid 22791); 4 Nov 2010 03:57:41 -0000 X-SWARE-Spam-Status: No, hits=-2.2 required=5.0 tests=AWL, BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, SPF_HELO_PASS, TW_CC, TW_FN, TW_PL, T_RP_MATCHES_RCVD, T_TVD_MIME_NO_HEADERS X-Spam-Check-By: sourceware.org Received: from smtp-out.google.com (HELO smtp-out.google.com) (216.239.44.51) by sourceware.org (qpsmtpd/0.43rc1) with ESMTP; Thu, 04 Nov 2010 03:57:34 +0000 Received: from hpaq14.eem.corp.google.com (hpaq14.eem.corp.google.com [172.25.149.14]) by smtp-out.google.com with ESMTP id oA43vW9J004095 for ; Wed, 3 Nov 2010 20:57:32 -0700 Received: from pxi12 (pxi12.prod.google.com [10.243.27.12]) by hpaq14.eem.corp.google.com with ESMTP id oA43vUX9018271 for ; Wed, 3 Nov 2010 20:57:30 -0700 Received: by pxi12 with SMTP id 12so97724pxi.28 for ; Wed, 03 Nov 2010 20:57:29 -0700 (PDT) Received: by 10.142.147.13 with SMTP id u13mr99980wfd.417.1288843049704; Wed, 03 Nov 2010 20:57:29 -0700 (PDT) Received: from coign.google.com ([216.239.45.130]) by mx.google.com with ESMTPS id q13sm14709882wfc.17.2010.11.03.20.57.27 (version=TLSv1/SSLv3 cipher=RC4-MD5); Wed, 03 Nov 2010 20:57:28 -0700 (PDT) From: Ian Lance Taylor To: gcc-patches@gcc.gnu.org, gofrontend-dev@googlegroups.com Subject: [gccgo] Add append builtin function Date: Wed, 03 Nov 2010 20:57:25 -0700 Message-ID: User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/23.1 (gnu/linux) MIME-Version: 1.0 X-System-Of-Record: true X-IsSubscribed: yes Mailing-List: contact gcc-patches-help@gcc.gnu.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Archive: List-Post: List-Help: Sender: gcc-patches-owner@gcc.gnu.org Delivered-To: mailing list gcc-patches@gcc.gnu.org A new builtin function, append, was recently added to the Go language. This patch implements it for gccgo. Committed to gccgo branch. Ian 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(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; +}