diff options
Diffstat (limited to 'gcc/go')
52 files changed, 11408 insertions, 5516 deletions
diff --git a/gcc/go/ChangeLog b/gcc/go/ChangeLog index eabd8f3fef..e27cef9a5b 100644 --- a/gcc/go/ChangeLog +++ b/gcc/go/ChangeLog @@ -1,10 +1,196 @@ -2016-08-22 Release Manager +2017-05-02 Release Manager - * GCC 6.2.0 released. + * GCC 7.1.0 released. -2016-04-27 Release Manager +2017-03-28 Than McIntosh <thanm@google.com> - * GCC 6.1.0 released. + PR go/80226 + * go-gcc.cc (Gcc_backend::return_statement): Check for + void_type_node when checking result size. + +2017-02-20 Ian Lance Taylor <iant@golang.org> + + PR go/79642 + * lang.opt (-fgo-relative-import-path): Change space to tab. + +2017-02-07 Richard Biener <rguenther@suse.de> + + PR tree-optimization/79256 + PR middle-end/79278 + * go-backend.c (go_field_alignment): Adjust. + +2017-01-11 Than McIntosh <thanm@google.com> + + * go-gcc.cc (conditional_expression): Add Bfunction parameter. + +2017-01-01 Jakub Jelinek <jakub@redhat.com> + + Update copyright years. + + * gccgo.texi: Bump @copyrights-go year. + +2016-12-16 Than McIntosh <thanm@google.com> + + * go-gcc.cc (Gcc_backend::expression_statement): Add Bfunction* + parameter. + (Gcc_backend::init_statement): Likewise. + (Gcc_backend::assignment_statement): Likewise. + (Gcc_backend::if_statement): Likewise. + +2016-12-06 Than McIntosh <thanm@google.com> + + * go-gcc.cc (Gcc_backend::var_expression): Add Varexpr_context + parameter. + +2016-11-22 Than McIntosh <thanm@google.com> + + * go-gcc.cc (char_needs_encoding): Remove. + (needs_encoding, fetch_utf8_char, encode_id): Remove. + (Gcc_backend::global_variable): Add asm_name parameter. Don't + compute asm_name here. + (Gcc_backend::implicit_variable): Likewise. + (Gcc_backend::implicit_variable_reference): Likewise. + (Gcc_backend::immutable_struct): Likewise. + (Gcc_backend::immutable_struct_reference): Likewise. + * Make-lang.in (GO_OBJS): Add go/go-encode-id.o. + +2016-11-22 Ian Lance Taylor <iant@google.com> + + * go-gcc.cc (Gcc_backend::Gcc_backend): Add builtin function + __builtin_frame_address. + +2016-10-25 David Malcolm <dmalcolm@redhat.com> + + * go-lang.c (go_langhook_type_for_mode): Remove redundant cast + from result of GET_MODE_CLASS. Minor formatting fixes. + +2016-10-13 Thomas Preud'homme <thomas.preudhomme@arm.com> + + * go-backend.c: Include memmodel.h. + +2016-10-10 Than McIntosh <thanm@google.com> + + * go-gcc.h: New file. + * go-c.h (struct go_create_gogo_args): Add backend and linemap + fields. + * go-lang.c: Include "go-gcc.h". + (go_langhook_init): Set linemap and backend fields of args. + * go-gcc.cc: Include "go-gcc.h". + * go-linemap.cc: Include "go-gcc.h". + +2016-10-10 Than McIntosh <thanm@google.com> + + * go-linemap.cc (Gcc_linemap::location_line): New method. + +2016-10-10 Eric Botcazou <ebotcazou@adacore.com> + + * config-lang.in (lang_requires_boot_languages): Delete. + +2016-10-06 Chris Manghane <cmang@google.com> + + * go-gcc.cc (Gcc_backend::stack_allocation_expression): Clear the + returned memory. + +2016-09-27 Than McIntosh <thanm@google.com> + + * go-linemap.cc (Gcc_linemap::to_string): New method. + +2016-09-23 Than McIntosh <thanm@google.com> + + * go-gcc-diagnostics.cc: New file. + * go-location.h (Location): Remove operator source_location. Add + operator==. + * go-system.h: #include <sstream>. + * Make-lang.in (GO_OBJS): Add go/go-diagnostics.o and + go/go-gcc-diagnostics.o. + (CFLAGS-go/go-gcc-diagnostics.o): New variable. + +2016-09-23 Chris Manghane <cmang@google.com> + + PR go/77701 + * go-gcc.cc (Gcc_backend::Gcc_backend): Fix calls to integer_type + to pass arguments in the correct order. + +2016-09-22 Ian Lance Taylor <iant@golang.org> + + * go-gcc.cc (Gcc_backend::Gcc_backend): Declare + __builtin_frame_address. + +2016-09-11 Ian Lance Taylor <iant@golang.org> + + * go-gcc.cc (Gcc_backend::Gcc_backend): Add builtin versions of + ctz, ctzll, bswap32, bswap64. + +2016-09-10 Ian Lance Taylor <iant@golang.org> + + * go-backend.c (go_trampoline_info): Remove. + * go-c.h (go_trampoline_info): Don't declare. + +2016-09-09 Than McIntosh <thanm@google.com> + + * go-sha1.cc: New file. + * Make-lang.in (GO_OBJS): Add go/go-sha1.o. + (CFLAGS-go/go-sha1.o): New variable. + +2016-08-29 Ian Lance Taylor <iant@google.com> + + * lang.opt (fgo-c-header, fgo-compiling-runtime): New options. + * go-c.h (struct go_create_gogo_args): Define. + (go_create_gogo): Change declaration to take struct pointer. + * go-lang.c (go_c_header): New static variable. + (go_langhook_init): Update call to go_create_gogo. + * gccgo.texi (Invoking gccgo): Document -fgo-c-header and + -fgo-compiling-runtime. + +2016-08-09 Ian Lance Taylor <iant@google.com> + + * gccgo.texi (Invoking gccgo): Document -fgo-optimize-allocs and + -fgo-debug-escae. + (Compiler Directives): New chapter. + (Function Names): Describe using //go:linkname. Suggest using + -fgo-pkgpath rather than -fgo-prefix. + +2016-08-08 Ian Lance Taylor <iant@google.com> + + PR go/72814 + * go-gcc.cc (Gcc_backend::function_type): If the return type is + zero bytes, treat the function as returning void. + (return_statement): If the return type is zero bytes, don't + actually return any values. + +2016-08-05 Ian Lance Taylor <iant@google.com> + + PR go/72812 + * go-gcc.cc (char_needs_encoding): New static function. + (needs_encoding, fetch_utf8_char): New static functions. + (encode_id): New static function. + (Gcc_backend::global_variable): Set asm name if the name is not + simple ASCII. + (Gcc_backend::implicit_variable): Likewise. + (Gcc_backend::implicit_variable_reference): Likewise. + (Gcc_backend::immutable_struct): Likewise. + (Gcc_backend::immutable_struct_reference): Likewise. + (Gcc_backend::function): Likewise. + +2016-08-02 Chris Manghane <cmang@google.com> + + * lang.opt: Add -fgo-debug-escape option. + * go-c.h (go_create_gogo): Add debug_escape_level parameter. + * go-lang.c (go_langhook_init): Pass go_debug_escape_level to + go_create_gogo. + +2016-05-06 Chris Manghane <cmang@google.com> + + * Make-lang.in (GO_OBJS): Add go/escape.o (based on an entirely + new escape.cc). + +2016-04-29 Chris Manghane <cmang@google.com> + + * Make-lang.in (GO_OBJS): Remove go/dataflow.o, go/escape.o. + +2016-04-18 Michael Matz <matz@suse.de> + + * go-gcc.cc (Gcc_backend::implicit_variable): Use SET_DECL_ALIGN. 2016-02-12 Jakub Jelinek <jakub@redhat.com> @@ -1185,7 +1371,7 @@ Go frontend added to gcc repository. -Copyright (C) 2010-2016 Free Software Foundation, Inc. +Copyright (C) 2010-2017 Free Software Foundation, Inc. Copying and distribution of this file, with or without modification, are permitted in any medium without royalty provided the copyright diff --git a/gcc/go/Make-lang.in b/gcc/go/Make-lang.in index 3240fe8449..ba88376543 100644 --- a/gcc/go/Make-lang.in +++ b/gcc/go/Make-lang.in @@ -1,6 +1,6 @@ # Make-lang.in -- Top level -*- makefile -*- fragment for gcc Go frontend. -# Copyright (C) 2009-2016 Free Software Foundation, Inc. +# Copyright (C) 2009-2017 Free Software Foundation, Inc. # This file is part of GCC. @@ -50,16 +50,19 @@ go-warn = $(STRICT_WARN) GO_OBJS = \ go/ast-dump.o \ - go/dataflow.o \ go/escape.o \ go/export.o \ go/expressions.o \ go/go-backend.o \ + go/go-diagnostics.o \ + go/go-encode-id.o \ go/go-dump.o \ go/go-gcc.o \ + go/go-gcc-diagnostics.o \ go/go-lang.o \ go/go-linemap.o \ go/go-optimize.o \ + go/go-sha1.o \ go/go.o \ go/gogo.o \ go/import.o \ @@ -226,6 +229,9 @@ GOINCLUDES = -I $(srcdir)/go -I $(srcdir)/go/gofrontend CFLAGS-go/go-gcc.o += $(GOINCLUDES) CFLAGS-go/go-linemap.o += $(GOINCLUDES) +CFLAGS-go/go-sha1.o += $(GOINCLUDES) +CFLAGS-go/go-gcc-diagnostics.o += $(GOINCLUDES) +CFLAGS-go/go-encode-id.o += $(GOINCLUDES) go/%.o: go/gofrontend/%.cc $(COMPILE) $(GOINCLUDES) $< diff --git a/gcc/go/config-lang.in b/gcc/go/config-lang.in index 8e27ade222..1fc7eeccec 100644 --- a/gcc/go/config-lang.in +++ b/gcc/go/config-lang.in @@ -1,6 +1,6 @@ # config-lang.in -- Top level configure fragment for gcc Go frontend. -# Copyright (C) 2009-2016 Free Software Foundation, Inc. +# Copyright (C) 2009-2017 Free Software Foundation, Inc. # This file is part of GCC. @@ -31,10 +31,6 @@ compilers="go1\$(exeext)" target_libs="target-libgo target-libffi target-libbacktrace" lang_dirs="gotools" -# The Go frontend is written in C++, so we need to build the C++ -# compiler during stage 1. -lang_requires_boot_languages=c++ - gtfiles="\$(srcdir)/go/go-lang.c \$(srcdir)/go/go-c.h" # Do not build by default. diff --git a/gcc/go/gccgo.texi b/gcc/go/gccgo.texi index 88eb46ad0b..e268c25fab 100644 --- a/gcc/go/gccgo.texi +++ b/gcc/go/gccgo.texi @@ -12,7 +12,7 @@ @include gcc-common.texi @c Copyright years for this manual. -@set copyrights-go 2010-2016 +@set copyrights-go 2010-2017 @copying @c man begin COPYRIGHT @@ -88,6 +88,7 @@ package documentation, see @uref{http://golang.org/}. How you can share and copy this manual. * Invoking gccgo:: How to run gccgo. * Import and Export:: Importing and exporting package data. +* Compiler Directives:: Comments to control compilation. * C Interoperability:: Calling C from Go and vice-versa. * Index:: Index. @end menu @@ -228,6 +229,27 @@ may be used. Or the checks may be removed via by default, but in the future may be off by default on systems that do not require it. +@item -fgo-optimize-allocs +@cindex @option{-fgo-optimize-allocs} +Use escape analysis to allocate objects on the stack rather than the +heap when possible. In the future this may be the default. + +@item -fgo-debug-escape@var{n} +@cindex @option{-fgo-debug-escape} +Output escape analysis debugging information. Larger values of +@var{n} generate more information. + +@item -fgo-c-header=@var{file} +@cindex @option{-fgo-c-header} +Write top-level named Go struct definitions to @var{file} as C code. +This is used when compiling the runtime package. + +@item -fgo-compiling-runtime +@cindex @option{-fgo-compiling-runtime} +Apply special rules for compiling the runtime package. Implicit +memory allocation is forbidden. Some additional compiler directives +are supported. + @item -g @cindex @option{-g for gccgo} This is the standard @command{gcc} option (@pxref{Debugging Options, , @@ -286,6 +308,50 @@ At link time you must explicitly tell @command{gccgo} which files to link together into the executable, as is usual with @command{gcc}. This is different from the behavior of other Go compilers. +@node Compiler Directives +@chapter Compiler Directives + +The Go compiler supports a few compiler directives. A compiler +directive uses a @code{//} comment at the start of a line. There must +be no space between the @code{//} and the name of the directive. + +@table @code +@item //line @var{file}:@var{line} +The @code{//line} directive specifies that the source line that +follows should be recorded as having come from the given file path and +line number. Successive lines are recorded using increasing line +numbers, until the next directive. This directive typically appears +in machine-generated code, so that compilers and debuggers will show +lines in the original input to the generator. + +@item //extern @var{extern_name} +The @code{extern} directive sets the externally visible name of the +next function declaration. See @ref{Function Names}. + +@item //go:compile @var{go_name} @var{extern_name} +The @code{go:compile} directives sets the externally visible name of a +function definition or declaration. See @ref{Function Names}. + +@item //go:noescape +The @code{//go:noescape} directive specifies that the next declaration +in the file, which must be a func without a body (meaning that it has +an implementation not written in Go) does not allow any of the +pointers passed as arguments to escape into the heap or into the +values returned from the function. This information can be used during +the compiler's escape analysis of Go code calling the function. + +@item //go:nosplit +The @code{//go:nosplit} directive specifies that the next function +declared in the file must not include a stack overflow check. This is +most commonly used by low-level runtime sources invoked at times when +it is unsafe for the calling goroutine to be preempted. + +@item //go:noinline +The @code{//go:noinline} directive specifies that the next function +defined in the file may not be inlined. + +@end table + @node C Interoperability @chapter C Interoperability @@ -376,20 +442,31 @@ function is still using it. @cindex @code{extern} @cindex external names -Go code can call C functions directly using a Go extension implemented -in @command{gccgo}: a function declaration may be preceded by a -comment giving the external name. The comment must be at the -beginning of the line and must start with @code{//extern}. This must -be followed by a space and then the external name of the function. -The function declaration must be on the line immediately after the -comment. For example, here is how the C function @code{open} can be -declared in Go: +Go code can call C functions directly using the @code{//extern} or +@code{//go:linkname} compiler directives. An @code{//extern} +directive must be at the beginning of the line and must start with +@code{//extern}. This must be followed by a space and then the +external name of the function. The function declaration must be on +the line immediately after the comment. For example, here is how the +C function @code{open} can be declared in Go: @smallexample //extern open func c_open(name *byte, mode int, perm int) int @end smallexample +You can do the same thing using the @code{//go:linkname} compiler +directive. The @code{//go:linkname} directive must be at the start of +the line. It is followed by whitespace, the name of the Go function, +more whitespace, and the external name of the function. Unlike +@code{//extern}, @code{//go:linkname} does not need to appear +immediately adjacent to the function definition or declaration. + +@smallexample +//go:linkname c_open open +func c_open(name *byte, mode int, perm int) int +@end smallexample + The C function naturally expects a nul terminated string, which in Go is equivalent to a pointer to an array (not a slice!) of @code{byte} with a terminating zero byte. So a sample call from Go would look @@ -405,14 +482,14 @@ use Go's @code{os.Open} function instead. The name of Go functions accessed from C is subject to change. At present the name of a Go function that does not have a receiver is -@code{prefix.package.Functionname}. The prefix is set by the -@option{-fgo-prefix} option used when the package is compiled; if the -option is not used, the default is simply @code{go}. To call the -function from C you must set the name using the @command{gcc} +@code{pkgpath.Functionname}. The @var{pkgpath} is set by the +@option{-fgo-pkgpath} option used when the package is compiled; if the +option is not used, the default is @code{go.@var{packagename}}. To +call the function from C you must set the name using the @command{gcc} @code{__asm__} extension. @smallexample -extern int go_function(int) __asm__ ("myprefix.mypackage.Function"); +extern int go_function(int) __asm__ ("mypkgpath.Function"); @end smallexample @node Index diff --git a/gcc/go/go-backend.c b/gcc/go/go-backend.c index 1c62495c38..d60a79e296 100644 --- a/gcc/go/go-backend.c +++ b/gcc/go/go-backend.c @@ -1,5 +1,5 @@ /* go-backend.c -- Go frontend interface to gcc backend. - Copyright (C) 2010-2016 Free Software Foundation, Inc. + Copyright (C) 2010-2017 Free Software Foundation, Inc. This file is part of GCC. @@ -22,6 +22,7 @@ along with GCC; see the file COPYING3. If not see #include "coretypes.h" #include "target.h" #include "tree.h" +#include "memmodel.h" #include "tm_p.h" #include "diagnostic.h" #include "simple-object.h" @@ -70,25 +71,12 @@ go_field_alignment (tree t) #endif #ifdef ADJUST_FIELD_ALIGN - { - tree field ATTRIBUTE_UNUSED; - field = build_decl (UNKNOWN_LOCATION, FIELD_DECL, NULL, t); - v = ADJUST_FIELD_ALIGN (field, v); - } + v = ADJUST_FIELD_ALIGN (NULL_TREE, t, v); #endif return v / BITS_PER_UNIT; } -/* Return the size and alignment of a trampoline. */ - -void -go_trampoline_info (unsigned int *size, unsigned int *alignment) -{ - *size = TRAMPOLINE_SIZE; - *alignment = TRAMPOLINE_ALIGNMENT; -} - /* This is called by the Go frontend proper if the unsafe package was imported. When that happens we can not do type-based alias analysis. */ diff --git a/gcc/go/go-c.h b/gcc/go/go-c.h index 98b2850be8..029b5d5f6b 100644 --- a/gcc/go/go-c.h +++ b/gcc/go/go-c.h @@ -1,5 +1,5 @@ /* go-c.h -- Header file for go frontend gcc C interface. - Copyright (C) 2009-2016 Free Software Foundation, Inc. + Copyright (C) 2009-2017 Free Software Foundation, Inc. This file is part of GCC. @@ -22,6 +22,8 @@ along with GCC; see the file COPYING3. If not see #define GO_EXTERN_C +class Linemap; +class Backend; /* Functions defined in the Go frontend proper called by the GCC interface. */ @@ -31,10 +33,23 @@ extern int go_enable_optimize (const char*); extern void go_add_search_path (const char*); -extern void go_create_gogo (int int_type_size, int pointer_size, - const char* pkgpath, const char *prefix, - const char *relative_import_path, - bool check_divide_zero, bool check_divide_overflow); +struct go_create_gogo_args +{ + int int_type_size; + int pointer_size; + const char* pkgpath; + const char* prefix; + const char* relative_import_path; + const char* c_header; + Backend* backend; + Linemap* linemap; + bool check_divide_by_zero; + bool check_divide_overflow; + bool compiling_runtime; + int debug_escape_level; +}; + +extern void go_create_gogo (const struct go_create_gogo_args*); extern void go_parse_input_files (const char**, unsigned int, bool only_check_syntax, @@ -52,8 +67,6 @@ extern const char *go_localize_identifier (const char*); extern unsigned int go_field_alignment (tree); -extern void go_trampoline_info (unsigned int *size, unsigned int *alignment); - extern void go_imported_unsafe (void); extern void go_write_export_data (const char *, unsigned int); diff --git a/gcc/go/go-gcc-diagnostics.cc b/gcc/go/go-gcc-diagnostics.cc new file mode 100644 index 0000000000..b12773f455 --- /dev/null +++ b/gcc/go/go-gcc-diagnostics.cc @@ -0,0 +1,61 @@ +// go-gcc-diagnostics.cc -- GCC implementation of go diagnostics interface. +// Copyright (C) 2016-2017 Free Software Foundation, Inc. +// Contributed by Than McIntosh, Google. + +// This file is part of GCC. + +// GCC is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3, or (at your option) any later +// version. + +// GCC is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. + +// You should have received a copy of the GNU General Public License +// along with GCC; see the file COPYING3. If not see +// <http://www.gnu.org/licenses/>. + +#include "go-system.h" +#include "go-diagnostics.h" + +void +go_be_error_at(const Location location, const std::string& errmsg) +{ + source_location gcc_loc = location.gcc_location(); + error_at(gcc_loc, "%s", errmsg.c_str()); +} + + +void +go_be_warning_at(const Location location, + int opt, const std::string& warningmsg) +{ + source_location gcc_loc = location.gcc_location(); + warning_at(gcc_loc, opt, "%s", warningmsg.c_str()); +} + +void +go_be_fatal_error(const Location location, + const std::string& fatalmsg) +{ + source_location gcc_loc = location.gcc_location(); + fatal_error(gcc_loc, "%s", fatalmsg.c_str()); +} + +void +go_be_inform(const Location location, + const std::string& infomsg) +{ + source_location gcc_loc = location.gcc_location(); + inform(gcc_loc, "%s", infomsg.c_str()); +} + +void +go_be_get_quotechars(const char** open_qu, const char** close_qu) +{ + *open_qu = open_quote; + *close_qu = close_quote; +} diff --git a/gcc/go/go-gcc.cc b/gcc/go/go-gcc.cc index 2793bb6090..62baa91fab 100644 --- a/gcc/go/go-gcc.cc +++ b/gcc/go/go-gcc.cc @@ -1,5 +1,5 @@ // go-gcc.cc -- Go frontend to gcc IR. -// Copyright (C) 2011-2016 Free Software Foundation, Inc. +// Copyright (C) 2011-2017 Free Software Foundation, Inc. // Contributed by Ian Lance Taylor, Google. // This file is part of GCC. @@ -43,6 +43,7 @@ #include "builtins.h" #include "go-c.h" +#include "go-gcc.h" #include "gogo.h" #include "backend.h" @@ -275,7 +276,7 @@ class Gcc_backend : public Backend { return this->make_expression(null_pointer_node); } Bexpression* - var_expression(Bvariable* var, Location); + var_expression(Bvariable* var, Varexpr_context, Location); Bexpression* indirect_expression(Btype*, Bexpression* expr, bool known_valid, Location); @@ -324,8 +325,8 @@ class Gcc_backend : public Backend compound_expression(Bstatement*, Bexpression*, Location); Bexpression* - conditional_expression(Btype*, Bexpression*, Bexpression*, Bexpression*, - Location); + conditional_expression(Bfunction*, Btype*, Bexpression*, Bexpression*, + Bexpression*, Location); Bexpression* unary_expression(Operator, Bexpression*, Location); @@ -360,21 +361,22 @@ class Gcc_backend : public Backend { return this->make_statement(error_mark_node); } Bstatement* - expression_statement(Bexpression*); + expression_statement(Bfunction*, Bexpression*); Bstatement* - init_statement(Bvariable* var, Bexpression* init); + init_statement(Bfunction*, Bvariable* var, Bexpression* init); Bstatement* - assignment_statement(Bexpression* lhs, Bexpression* rhs, Location); + assignment_statement(Bfunction*, Bexpression* lhs, Bexpression* rhs, + Location); Bstatement* return_statement(Bfunction*, const std::vector<Bexpression*>&, Location); Bstatement* - if_statement(Bexpression* condition, Bblock* then_block, Bblock* else_block, - Location); + if_statement(Bfunction*, Bexpression* condition, Bblock* then_block, + Bblock* else_block, Location); Bstatement* switch_statement(Bfunction* function, Bexpression* value, @@ -411,9 +413,8 @@ class Gcc_backend : public Backend { return new Bvariable(error_mark_node); } Bvariable* - global_variable(const std::string& package_name, - const std::string& pkgpath, - const std::string& name, + global_variable(const std::string& var_name, + const std::string& asm_name, Btype* btype, bool is_external, bool is_hidden, @@ -439,25 +440,27 @@ class Gcc_backend : public Backend Location, Bstatement**); Bvariable* - implicit_variable(const std::string&, Btype*, bool, bool, bool, - int64_t); + implicit_variable(const std::string&, const std::string&, Btype*, + bool, bool, bool, int64_t); void implicit_variable_set_init(Bvariable*, const std::string&, Btype*, bool, bool, bool, Bexpression*); Bvariable* - implicit_variable_reference(const std::string&, Btype*); + implicit_variable_reference(const std::string&, const std::string&, Btype*); Bvariable* - immutable_struct(const std::string&, bool, bool, Btype*, Location); + immutable_struct(const std::string&, const std::string&, + bool, bool, Btype*, Location); void immutable_struct_set_init(Bvariable*, const std::string&, bool, bool, Btype*, Location, Bexpression*); Bvariable* - immutable_struct_reference(const std::string&, Btype*, Location); + immutable_struct_reference(const std::string&, const std::string&, + Btype*, Location); // Labels. @@ -541,7 +544,7 @@ private: std::map<std::string, Bfunction*> builtin_functions_; }; -// A helper function. +// A helper function to create a GCC identifier from a C++ string. static inline tree get_identifier_from_string(const std::string& str) @@ -555,25 +558,25 @@ Gcc_backend::Gcc_backend() { /* We need to define the fetch_and_add functions, since we use them for ++ and --. */ - tree t = this->integer_type(BITS_PER_UNIT, 1)->get_tree(); + tree t = this->integer_type(true, BITS_PER_UNIT)->get_tree(); tree p = build_pointer_type(build_qualified_type(t, TYPE_QUAL_VOLATILE)); this->define_builtin(BUILT_IN_SYNC_ADD_AND_FETCH_1, "__sync_fetch_and_add_1", NULL, build_function_type_list(t, p, t, NULL_TREE), false, false); - t = this->integer_type(BITS_PER_UNIT * 2, 1)->get_tree(); + t = this->integer_type(true, BITS_PER_UNIT * 2)->get_tree(); p = build_pointer_type(build_qualified_type(t, TYPE_QUAL_VOLATILE)); this->define_builtin(BUILT_IN_SYNC_ADD_AND_FETCH_2, "__sync_fetch_and_add_2", NULL, build_function_type_list(t, p, t, NULL_TREE), false, false); - t = this->integer_type(BITS_PER_UNIT * 4, 1)->get_tree(); + t = this->integer_type(true, BITS_PER_UNIT * 4)->get_tree(); p = build_pointer_type(build_qualified_type(t, TYPE_QUAL_VOLATILE)); this->define_builtin(BUILT_IN_SYNC_ADD_AND_FETCH_4, "__sync_fetch_and_add_4", NULL, build_function_type_list(t, p, t, NULL_TREE), false, false); - t = this->integer_type(BITS_PER_UNIT * 8, 1)->get_tree(); + t = this->integer_type(true, BITS_PER_UNIT * 8)->get_tree(); p = build_pointer_type(build_qualified_type(t, TYPE_QUAL_VOLATILE)); this->define_builtin(BUILT_IN_SYNC_ADD_AND_FETCH_8, "__sync_fetch_and_add_8", NULL, build_function_type_list(t, p, t, NULL_TREE), @@ -596,6 +599,28 @@ Gcc_backend::Gcc_backend() NULL_TREE), false, false); + // Used by runtime/internal/sys. + this->define_builtin(BUILT_IN_CTZ, "__builtin_ctz", "ctz", + build_function_type_list(integer_type_node, + unsigned_type_node, + NULL_TREE), + true, false); + this->define_builtin(BUILT_IN_CTZLL, "__builtin_ctzll", "ctzll", + build_function_type_list(integer_type_node, + long_long_unsigned_type_node, + NULL_TREE), + true, false); + this->define_builtin(BUILT_IN_BSWAP32, "__builtin_bswap32", "bswap32", + build_function_type_list(uint32_type_node, + uint32_type_node, + NULL_TREE), + true, false); + this->define_builtin(BUILT_IN_BSWAP64, "__builtin_bswap64", "bswap64", + build_function_type_list(uint64_type_node, + uint64_type_node, + NULL_TREE), + true, false); + // We provide some functions for the math library. tree math_function_type = build_function_type_list(double_type_node, double_type_node, @@ -700,11 +725,21 @@ Gcc_backend::Gcc_backend() math_function_type_long, true, false); // We use __builtin_return_address in the thunk we build for - // functions which call recover. + // functions which call recover, and for runtime.getcallerpc. + t = build_function_type_list(ptr_type_node, unsigned_type_node, NULL_TREE); this->define_builtin(BUILT_IN_RETURN_ADDRESS, "__builtin_return_address", - NULL, + NULL, t, false, false); + + // The runtime calls __builtin_frame_address for runtime.getcallersp. + this->define_builtin(BUILT_IN_FRAME_ADDRESS, "__builtin_frame_address", + NULL, t, false, false); + + // The runtime calls __builtin_extract_return_addr when recording + // the address to which a function returns. + this->define_builtin(BUILT_IN_EXTRACT_RETURN_ADDR, + "__builtin_extract_return_addr", NULL, build_function_type_list(ptr_type_node, - unsigned_type_node, + ptr_type_node, NULL_TREE), false, false); @@ -857,6 +892,14 @@ Gcc_backend::function_type(const Btyped_identifier& receiver, if (result == error_mark_node) return this->error_type(); + // The libffi library can not represent a zero-sized object. To + // avoid causing confusion on 32-bit SPARC, we treat a function that + // returns a zero-sized value as returning void. That should do no + // harm since there is no actual value to be returned. See + // https://gcc.gnu.org/PR72814 for details. + if (result != void_type_node && int_size_in_bytes(result) == 0) + result = void_type_node; + tree fntype = build_function_type(result, args); if (fntype == error_mark_node) return this->error_type(); @@ -1201,7 +1244,7 @@ Gcc_backend::zero_expression(Btype* btype) // An expression that references a variable. Bexpression* -Gcc_backend::var_expression(Bvariable* var, Location location) +Gcc_backend::var_expression(Bvariable* var, Varexpr_context, Location location) { tree ret = var->get_tree(location); if (ret == error_mark_node) @@ -1503,7 +1546,8 @@ Gcc_backend::compound_expression(Bstatement* bstat, Bexpression* bexpr, // ELSE_EXPR otherwise. Bexpression* -Gcc_backend::conditional_expression(Btype* btype, Bexpression* condition, +Gcc_backend::conditional_expression(Bfunction*, Btype* btype, + Bexpression* condition, Bexpression* then_expr, Bexpression* else_expr, Location location) { @@ -1921,13 +1965,16 @@ Gcc_backend::stack_allocation_expression(int64_t size, Location location) tree alloca = builtin_decl_explicit(BUILT_IN_ALLOCA); tree size_tree = build_int_cst(integer_type_node, size); tree ret = build_call_expr_loc(location.gcc_location(), alloca, 1, size_tree); + tree memset = builtin_decl_explicit(BUILT_IN_MEMSET); + ret = build_call_expr_loc(location.gcc_location(), memset, 3, + ret, integer_zero_node, size_tree); return this->make_expression(ret); } // An expression as a statement. Bstatement* -Gcc_backend::expression_statement(Bexpression* expr) +Gcc_backend::expression_statement(Bfunction*, Bexpression* expr) { return this->make_statement(expr->get_tree()); } @@ -1935,7 +1982,7 @@ Gcc_backend::expression_statement(Bexpression* expr) // Variable initialization. Bstatement* -Gcc_backend::init_statement(Bvariable* var, Bexpression* init) +Gcc_backend::init_statement(Bfunction*, Bvariable* var, Bexpression* init) { tree var_tree = var->get_decl(); tree init_tree = init->get_tree(); @@ -1968,8 +2015,8 @@ Gcc_backend::init_statement(Bvariable* var, Bexpression* init) // Assignment. Bstatement* -Gcc_backend::assignment_statement(Bexpression* lhs, Bexpression* rhs, - Location location) +Gcc_backend::assignment_statement(Bfunction* bfn, Bexpression* lhs, + Bexpression* rhs, Location location) { tree lhs_tree = lhs->get_tree(); tree rhs_tree = rhs->get_tree(); @@ -1984,8 +2031,8 @@ Gcc_backend::assignment_statement(Bexpression* lhs, Bexpression* rhs, // anything anyhow. if (int_size_in_bytes(TREE_TYPE(lhs_tree)) == 0 || int_size_in_bytes(TREE_TYPE(rhs_tree)) == 0) - return this->compound_statement(this->expression_statement(lhs), - this->expression_statement(rhs)); + return this->compound_statement(this->expression_statement(bfn, lhs), + this->expression_statement(bfn, rhs)); // Sometimes the same unnamed Go type can be created multiple times // and thus have multiple tree representations. Make sure this does @@ -2031,6 +2078,28 @@ Gcc_backend::return_statement(Bfunction* bfunction, if (result == error_mark_node) return this->error_statement(); + // If the result size is zero bytes, we have set the function type + // to have a result type of void, so don't return anything. + // See the function_type method. + tree res_type = TREE_TYPE(result); + if (res_type == void_type_node || int_size_in_bytes(res_type) == 0) + { + tree stmt_list = NULL_TREE; + for (std::vector<Bexpression*>::const_iterator p = vals.begin(); + p != vals.end(); + p++) + { + tree val = (*p)->get_tree(); + if (val == error_mark_node) + return this->error_statement(); + append_to_statement_list(val, &stmt_list); + } + tree ret = fold_build1_loc(location.gcc_location(), RETURN_EXPR, + void_type_node, NULL_TREE); + append_to_statement_list(ret, &stmt_list); + return this->make_statement(stmt_list); + } + tree ret; if (vals.empty()) ret = fold_build1_loc(location.gcc_location(), RETURN_EXPR, void_type_node, @@ -2128,8 +2197,9 @@ Gcc_backend::exception_handler_statement(Bstatement* bstat, // If. Bstatement* -Gcc_backend::if_statement(Bexpression* condition, Bblock* then_block, - Bblock* else_block, Location location) +Gcc_backend::if_statement(Bfunction*, Bexpression* condition, + Bblock* then_block, Bblock* else_block, + Location location) { tree cond_tree = condition->get_tree(); tree then_tree = then_block->get_tree(); @@ -2419,9 +2489,8 @@ Gcc_backend::non_zero_size_type(tree type) // Make a global variable. Bvariable* -Gcc_backend::global_variable(const std::string& package_name, - const std::string& pkgpath, - const std::string& name, +Gcc_backend::global_variable(const std::string& var_name, + const std::string& asm_name, Btype* btype, bool is_external, bool is_hidden, @@ -2437,9 +2506,6 @@ Gcc_backend::global_variable(const std::string& package_name, if ((is_external || !is_hidden) && int_size_in_bytes(type_tree) == 0) type_tree = this->non_zero_size_type(type_tree); - std::string var_name(package_name); - var_name.push_back('.'); - var_name.append(name); tree decl = build_decl(location.gcc_location(), VAR_DECL, get_identifier_from_string(var_name), type_tree); @@ -2450,12 +2516,13 @@ Gcc_backend::global_variable(const std::string& package_name, if (!is_hidden) { TREE_PUBLIC(decl) = 1; - - std::string asm_name(pkgpath); - asm_name.push_back('.'); - asm_name.append(name); SET_DECL_ASSEMBLER_NAME(decl, get_identifier_from_string(asm_name)); } + else + { + SET_DECL_ASSEMBLER_NAME(decl, get_identifier_from_string(asm_name)); + } + TREE_USED(decl) = 1; if (in_unique_section) @@ -2637,8 +2704,9 @@ Gcc_backend::temporary_variable(Bfunction* function, Bblock* bblock, // Don't initialize VAR with BINIT, but still evaluate BINIT for // its side effects. if (this->type_size(btype) == 0 && init_tree != NULL_TREE) - *pstatement = this->compound_statement(this->expression_statement(binit), - *pstatement); + *pstatement = + this->compound_statement(this->expression_statement(function, binit), + *pstatement); return new Bvariable(var); } @@ -2647,8 +2715,9 @@ Gcc_backend::temporary_variable(Bfunction* function, Bblock* bblock, // generating GC root variables and storing the values of a slice initializer. Bvariable* -Gcc_backend::implicit_variable(const std::string& name, Btype* type, - bool is_hidden, bool is_constant, +Gcc_backend::implicit_variable(const std::string& name, + const std::string& asm_name, + Btype* type, bool is_hidden, bool is_constant, bool is_common, int64_t alignment) { tree type_tree = type->get_tree(); @@ -2687,9 +2756,11 @@ Gcc_backend::implicit_variable(const std::string& name, Btype* type, } if (alignment != 0) { - DECL_ALIGN(decl) = alignment * BITS_PER_UNIT; + SET_DECL_ALIGN(decl, alignment * BITS_PER_UNIT); DECL_USER_ALIGN(decl) = 1; } + if (! asm_name.empty()) + SET_DECL_ASSEMBLER_NAME(decl, get_identifier_from_string(asm_name)); go_preserve_from_gc(decl); return new Bvariable(decl); @@ -2730,7 +2801,9 @@ Gcc_backend::implicit_variable_set_init(Bvariable* var, const std::string&, // Return a reference to an implicit variable defined in another package. Bvariable* -Gcc_backend::implicit_variable_reference(const std::string& name, Btype* btype) +Gcc_backend::implicit_variable_reference(const std::string& name, + const std::string& asm_name, + Btype* btype) { tree type_tree = btype->get_tree(); if (type_tree == error_mark_node) @@ -2742,6 +2815,8 @@ Gcc_backend::implicit_variable_reference(const std::string& name, Btype* btype) TREE_PUBLIC(decl) = 1; TREE_STATIC(decl) = 1; DECL_ARTIFICIAL(decl) = 1; + if (! asm_name.empty()) + SET_DECL_ASSEMBLER_NAME(decl, get_identifier_from_string(asm_name)); go_preserve_from_gc(decl); return new Bvariable(decl); } @@ -2749,7 +2824,9 @@ Gcc_backend::implicit_variable_reference(const std::string& name, Btype* btype) // Create a named immutable initialized data structure. Bvariable* -Gcc_backend::immutable_struct(const std::string& name, bool is_hidden, +Gcc_backend::immutable_struct(const std::string& name, + const std::string& asm_name, + bool is_hidden, bool is_common, Btype* btype, Location location) { tree type_tree = btype->get_tree(); @@ -2766,6 +2843,8 @@ Gcc_backend::immutable_struct(const std::string& name, bool is_hidden, DECL_ARTIFICIAL(decl) = 1; if (!is_hidden) TREE_PUBLIC(decl) = 1; + if (! asm_name.empty()) + SET_DECL_ASSEMBLER_NAME(decl, get_identifier_from_string(asm_name)); // When the initializer for one immutable_struct refers to another, // it needs to know the visibility of the referenced struct so that @@ -2825,7 +2904,9 @@ Gcc_backend::immutable_struct_set_init(Bvariable* var, const std::string&, // defined in another package. Bvariable* -Gcc_backend::immutable_struct_reference(const std::string& name, Btype* btype, +Gcc_backend::immutable_struct_reference(const std::string& name, + const std::string& asm_name, + Btype* btype, Location location) { tree type_tree = btype->get_tree(); @@ -2840,6 +2921,8 @@ Gcc_backend::immutable_struct_reference(const std::string& name, Btype* btype, DECL_ARTIFICIAL(decl) = 1; TREE_PUBLIC(decl) = 1; DECL_EXTERNAL(decl) = 1; + if (! asm_name.empty()) + SET_DECL_ASSEMBLER_NAME(decl, get_identifier_from_string(asm_name)); go_preserve_from_gc(decl); return new Bvariable(decl); } @@ -2929,7 +3012,7 @@ Gcc_backend::function(Btype* fntype, const std::string& name, return this->error_function(); tree decl = build_decl(location.gcc_location(), FUNCTION_DECL, id, functype); - if (!asm_name.empty()) + if (! asm_name.empty()) SET_DECL_ASSEMBLER_NAME(decl, get_identifier_from_string(asm_name)); if (is_visible) TREE_PUBLIC(decl) = 1; diff --git a/gcc/go/go-gcc.h b/gcc/go/go-gcc.h new file mode 100644 index 0000000000..9f57c86288 --- /dev/null +++ b/gcc/go/go-gcc.h @@ -0,0 +1,33 @@ +/* go-gcc.h -- Header file for go backend-specific interfaces. + Copyright (C) 2016-2017 Free Software Foundation, Inc. + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 3, or (at your option) any later +version. + +GCC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +<http://www.gnu.org/licenses/>. */ + +#ifndef GO_GO_GCC_BACKEND_H +#define GO_GO_GCC_BACKEND_H + +class Backend; + +// Create and return a Backend object for use with the GCC backend. + +extern Backend *go_get_backend(); + +// Create and return a Linemap object for use with the GCC backend. + +extern Linemap *go_get_linemap(); + +#endif // !defined(GO_GCC_BACKEND_H) diff --git a/gcc/go/go-lang.c b/gcc/go/go-lang.c index 9c95c8e0bb..780d73799e 100644 --- a/gcc/go/go-lang.c +++ b/gcc/go/go-lang.c @@ -1,5 +1,5 @@ /* go-lang.c -- Go frontend gcc interface. - Copyright (C) 2009-2016 Free Software Foundation, Inc. + Copyright (C) 2009-2017 Free Software Foundation, Inc. This file is part of GCC. @@ -37,6 +37,7 @@ along with GCC; see the file COPYING3. If not see #include <mpfr.h> #include "go-c.h" +#include "go-gcc.h" /* Language-dependent contents of a type. */ @@ -83,6 +84,7 @@ struct GTY(()) language_function static const char *go_pkgpath = NULL; static const char *go_prefix = NULL; static const char *go_relative_import_path = NULL; +static const char *go_c_header = NULL; /* Language hooks. */ @@ -99,9 +101,20 @@ go_langhook_init (void) to, e.g., unsigned_char_type_node) but before calling build_common_builtin_nodes (because it calls, indirectly, go_type_for_size). */ - go_create_gogo (INT_TYPE_SIZE, POINTER_SIZE, go_pkgpath, go_prefix, - go_relative_import_path, go_check_divide_zero, - go_check_divide_overflow); + struct go_create_gogo_args args; + args.int_type_size = INT_TYPE_SIZE; + args.pointer_size = POINTER_SIZE; + args.pkgpath = go_pkgpath; + args.prefix = go_prefix; + args.relative_import_path = go_relative_import_path; + args.c_header = go_c_header; + args.check_divide_by_zero = go_check_divide_zero; + args.check_divide_overflow = go_check_divide_overflow; + args.compiling_runtime = go_compiling_runtime; + args.debug_escape_level = go_debug_escape_level; + args.linemap = go_get_linemap(); + args.backend = go_get_backend(); + go_create_gogo (&args); build_common_builtin_nodes (); @@ -247,6 +260,10 @@ go_langhook_handle_option ( go_relative_import_path = arg; break; + case OPT_fgo_c_header_: + go_c_header = arg; + break; + default: /* Just return 1 to indicate that the option is valid. */ break; @@ -356,10 +373,9 @@ go_langhook_type_for_mode (machine_mode mode, int unsignedp) return NULL_TREE; } - // FIXME: This static_cast should be in machmode.h. - enum mode_class mc = static_cast<enum mode_class>(GET_MODE_CLASS(mode)); + enum mode_class mc = GET_MODE_CLASS (mode); if (mc == MODE_INT) - return go_langhook_type_for_size(GET_MODE_BITSIZE(mode), unsignedp); + return go_langhook_type_for_size (GET_MODE_BITSIZE (mode), unsignedp); else if (mc == MODE_FLOAT) { switch (GET_MODE_BITSIZE (mode)) diff --git a/gcc/go/go-linemap.cc b/gcc/go/go-linemap.cc index b41559ed4c..2accb95e5c 100644 --- a/gcc/go/go-linemap.cc +++ b/gcc/go/go-linemap.cc @@ -6,6 +6,8 @@ #include "go-linemap.h" +#include "go-gcc.h" + // This class implements the Linemap interface defined by the // frontend. @@ -29,6 +31,12 @@ class Gcc_linemap : public Linemap void stop(); + std::string + to_string(Location); + + int + location_line(Location); + protected: Location get_predeclared_location(); @@ -60,6 +68,38 @@ Gcc_linemap::start_file(const char *file_name, unsigned line_begin) this->in_file_ = true; } +// Stringify a location + +std::string +Gcc_linemap::to_string(Location location) +{ + const line_map_ordinary *lmo; + source_location resolved_location; + + // Screen out unknown and predeclared locations; produce output + // only for simple file:line locations. + resolved_location = + linemap_resolve_location (line_table, location.gcc_location(), + LRK_SPELLING_LOCATION, &lmo); + if (lmo == NULL || resolved_location < RESERVED_LOCATION_COUNT) + return ""; + const char *path = LINEMAP_FILE (lmo); + if (!path) + return ""; + + // Strip the source file down to the base file, to reduce clutter. + std::stringstream ss; + ss << lbasename(path) << ":" << SOURCE_LINE (lmo, location.gcc_location()); + return ss.str(); +} + +// Return the line number for a given location (for debugging dumps) +int +Gcc_linemap::location_line(Location loc) +{ + return LOCATION_LINE(loc.gcc_location()); +} + // Stop getting locations. void diff --git a/gcc/go/go-location.h b/gcc/go/go-location.h index f2731d9686..90258ea1a2 100644 --- a/gcc/go/go-location.h +++ b/gcc/go/go-location.h @@ -26,10 +26,6 @@ class Location gcc_location() const { return this->gcc_loc_; } - // Temporary hack till error_at and warning_at can deal with a Location. - operator source_location() const - { return this->gcc_loc_; } - private: source_location gcc_loc_; }; @@ -42,4 +38,10 @@ operator<(Location loca, Location locb) return loca.gcc_location() < locb.gcc_location(); } +inline bool +operator==(Location loca, Location locb) +{ + return loca.gcc_location() == locb.gcc_location(); +} + #endif // !defined(GO_LOCATION_H) diff --git a/gcc/go/go-sha1.cc b/gcc/go/go-sha1.cc new file mode 100644 index 0000000000..0a4d5ac549 --- /dev/null +++ b/gcc/go/go-sha1.cc @@ -0,0 +1,71 @@ +/* go-sha1.cc -- Go frontend interface to gcc backend. + Copyright (C) 2016-2017 Free Software Foundation, Inc. + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 3, or (at your option) any later +version. + +GCC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +<http://www.gnu.org/licenses/>. */ + +#include "go-sha1.h" +#include "sha1.h" + +class Gcc_sha1_helper : public Go_sha1_helper +{ + public: + + Gcc_sha1_helper() : ctx_(new sha1_ctx) { sha1_init_ctx(this->ctx_); } + + ~Gcc_sha1_helper(); + + // Incorporate 'len' bytes from 'buffer' into checksum. + void + process_bytes(const void* buffer, size_t len); + + // Finalize checksum and return in the form of a string. + std::string + finish(); + + private: + sha1_ctx *ctx_; +}; + +Gcc_sha1_helper::~Gcc_sha1_helper() +{ + delete ctx_; +} + +void +Gcc_sha1_helper::process_bytes(const void* buffer, size_t len) +{ + sha1_process_bytes(buffer, len, this->ctx_); +} + +std::string +Gcc_sha1_helper::finish() +{ + // Use a union to provide the required alignment. + union + { + char checksum[checksum_len]; + long align; + } u; + sha1_finish_ctx(this->ctx_, u.checksum); + return std::string(u.checksum, checksum_len); +} + +Go_sha1_helper* +go_create_sha1_helper() +{ + return new Gcc_sha1_helper(); +} diff --git a/gcc/go/go-system.h b/gcc/go/go-system.h index cb7e74500c..90185435c1 100644 --- a/gcc/go/go-system.h +++ b/gcc/go/go-system.h @@ -1,5 +1,5 @@ // go-system.h -- Go frontend inclusion of gcc header files -*- C++ -*- -// Copyright (C) 2009-2016 Free Software Foundation, Inc. +// Copyright (C) 2009-2017 Free Software Foundation, Inc. // This file is part of GCC. @@ -30,6 +30,7 @@ #include <map> #include <set> #include <vector> +#include <sstream> #if defined(HAVE_UNORDERED_MAP) diff --git a/gcc/go/gofrontend/MERGE b/gcc/go/gofrontend/MERGE index ce20dad142..181b018b77 100644 --- a/gcc/go/gofrontend/MERGE +++ b/gcc/go/gofrontend/MERGE @@ -1,4 +1,4 @@ -ff29ea8e4e69eb94958aef4388da09a61b2b52b6 +a4f445e18fb06a032a4399859f432e03245f1a7d The first line of this file holds the git revision number of the last merge done from the gofrontend repository. diff --git a/gcc/go/gofrontend/ast-dump.cc b/gcc/go/gofrontend/ast-dump.cc index 72b01420aa..94bf5ef436 100644 --- a/gcc/go/gofrontend/ast-dump.cc +++ b/gcc/go/gofrontend/ast-dump.cc @@ -16,6 +16,7 @@ #include "ast-dump.h" #include "go-c.h" #include "go-dump.h" +#include "go-diagnostics.h" // The -fgo-dump-ast flag to activate AST dumps. @@ -166,24 +167,25 @@ const char* kAstDumpFileExtension = ".dump.ast"; void Ast_dump_context::dump(Gogo* gogo, const char* basename) { - std::ofstream* out = new std::ofstream(); + std::ofstream out; std::string dumpname(basename); dumpname += ".dump.ast"; - out->open(dumpname.c_str()); + out.open(dumpname.c_str()); - if (out->fail()) + if (out.fail()) { - error("cannot open %s:%m, -fgo-dump-ast ignored", dumpname.c_str()); + go_error_at(Linemap::unknown_location(), + "cannot open %s:%m, -fgo-dump-ast ignored", dumpname.c_str()); return; } this->gogo_ = gogo; - this->ostream_ = out; + this->ostream_ = &out; Ast_dump_traverse_blocks_and_functions adtbf(this); gogo->traverse(&adtbf); - out->close(); + out.close(); } // Dump a textual representation of a type to the diff --git a/gcc/go/gofrontend/backend.h b/gcc/go/gofrontend/backend.h index 5593fcb470..93835d9ce5 100644 --- a/gcc/go/gofrontend/backend.h +++ b/gcc/go/gofrontend/backend.h @@ -55,7 +55,7 @@ class Backend Location location; Btyped_identifier() - : name(), btype(NULL), location(UNKNOWN_LOCATION) + : name(), btype(NULL), location(Linemap::unknown_location()) { } Btyped_identifier(const std::string& a_name, Btype* a_btype, @@ -254,7 +254,7 @@ class Backend // Create a reference to a variable. virtual Bexpression* - var_expression(Bvariable* var, Location) = 0; + var_expression(Bvariable* var, Varexpr_context in_lvalue_pos, Location) = 0; // Create an expression that indirects through the pointer expression EXPR // (i.e., return the expression for *EXPR). KNOWN_VALID is true if the pointer @@ -324,12 +324,12 @@ class Backend compound_expression(Bstatement* bstat, Bexpression* bexpr, Location) = 0; // Return an expression that executes THEN_EXPR if CONDITION is true, or - // ELSE_EXPR otherwise and returns the result as type BTYPE. ELSE_EXPR - // may be NULL. BTYPE may be NULL. + // ELSE_EXPR otherwise and returns the result as type BTYPE, within the + // specified function FUNCTION. ELSE_EXPR may be NULL. BTYPE may be NULL. virtual Bexpression* - conditional_expression(Btype* btype, Bexpression* condition, - Bexpression* then_expr, Bexpression* else_expr, - Location) = 0; + conditional_expression(Bfunction* function, Btype* btype, + Bexpression* condition, Bexpression* then_expr, + Bexpression* else_expr, Location) = 0; // Return an expression for the unary operation OP EXPR. // Supported values of OP are (from operators.h): @@ -389,19 +389,19 @@ class Backend virtual Bstatement* error_statement() = 0; - // Create an expression statement. + // Create an expression statement within the specified function. virtual Bstatement* - expression_statement(Bexpression*) = 0; + expression_statement(Bfunction*, Bexpression*) = 0; - // Create a variable initialization statement. This initializes a - // local variable at the point in the program flow where it is - // declared. + // Create a variable initialization statement in the specified + // function. This initializes a local variable at the point in the + // program flow where it is declared. virtual Bstatement* - init_statement(Bvariable* var, Bexpression* init) = 0; + init_statement(Bfunction*, Bvariable* var, Bexpression* init) = 0; - // Create an assignment statement. + // Create an assignment statement within the specified function. virtual Bstatement* - assignment_statement(Bexpression* lhs, Bexpression* rhs, + assignment_statement(Bfunction*, Bexpression* lhs, Bexpression* rhs, Location) = 0; // Create a return statement, passing the representation of the @@ -410,9 +410,10 @@ class Backend return_statement(Bfunction*, const std::vector<Bexpression*>&, Location) = 0; - // Create an if statement. ELSE_BLOCK may be NULL. + // Create an if statement within a function. ELSE_BLOCK may be NULL. virtual Bstatement* - if_statement(Bexpression* condition, Bblock* then_block, Bblock* else_block, + if_statement(Bfunction*, Bexpression* condition, + Bblock* then_block, Bblock* else_block, Location) = 0; // Create a switch statement where the case values are constants. @@ -482,21 +483,19 @@ class Backend virtual Bvariable* error_variable() = 0; - // Create a global variable. PACKAGE_NAME is the name of the - // package where the variable is defined. PKGPATH is the package - // path for that package, from the -fgo-pkgpath or -fgo-prefix - // option. NAME is the name of the variable. BTYPE is the type of - // the variable. IS_EXTERNAL is true if the variable is defined in - // some other package. IS_HIDDEN is true if the variable is not - // exported (name begins with a lower case letter). - // IN_UNIQUE_SECTION is true if the variable should be put into a - // unique section if possible; this is intended to permit the linker - // to garbage collect the variable if it is not referenced. - // LOCATION is where the variable was defined. + // Create a global variable. NAME is the package-qualified name of + // the variable. ASM_NAME is the encoded identifier for the + // variable, incorporating the package, and made safe for the + // assembler. BTYPE is the type of the variable. IS_EXTERNAL is + // true if the variable is defined in some other package. IS_HIDDEN + // is true if the variable is not exported (name begins with a lower + // case letter). IN_UNIQUE_SECTION is true if the variable should + // be put into a unique section if possible; this is intended to + // permit the linker to garbage collect the variable if it is not + // referenced. LOCATION is where the variable was defined. virtual Bvariable* - global_variable(const std::string& package_name, - const std::string& pkgpath, - const std::string& name, + global_variable(const std::string& name, + const std::string& asm_name, Btype* btype, bool is_external, bool is_hidden, @@ -561,6 +560,9 @@ class Backend // // NAME is the name to use for the initialized variable this will create. // + // ASM_NAME is encoded assembler-friendly version of the name, or the + // empty string if no encoding is needed. + // // TYPE is the type of the implicit variable. // // IS_HIDDEN will be true if the descriptor should only be visible @@ -578,8 +580,9 @@ class Backend // // If ALIGNMENT is not zero, it is the desired alignment of the variable. virtual Bvariable* - implicit_variable(const std::string& name, Btype* type, bool is_hidden, - bool is_constant, bool is_common, int64_t alignment) = 0; + implicit_variable(const std::string& name, const std::string& asm_name, + Btype* type, bool is_hidden, bool is_constant, + bool is_common, int64_t alignment) = 0; // Set the initial value of a variable created by implicit_variable. @@ -597,12 +600,15 @@ class Backend bool is_hidden, bool is_constant, bool is_common, Bexpression* init) = 0; - // Create a reference to a named implicit variable defined in some other - // package. This will be a variable created by a call to implicit_variable - // with the same NAME and TYPE and with IS_COMMON passed as false. This - // corresponds to an extern global variable in C. + // Create a reference to a named implicit variable defined in some + // other package. This will be a variable created by a call to + // implicit_variable with the same NAME, ASM_NAME and TYPE and with + // IS_COMMON passed as false. This corresponds to an extern global + // variable in C. virtual Bvariable* - implicit_variable_reference(const std::string& name, Btype* type) = 0; + implicit_variable_reference(const std::string& name, + const std::string& asm_name, + Btype* type) = 0; // Create a named immutable initialized data structure. This is // used for type descriptors, map descriptors, and function @@ -612,6 +618,9 @@ class Backend // NAME is the name to use for the initialized global variable which // this call will create. // + // ASM_NAME is the encoded, assembler-friendly version of NAME, or + // the empty string if no encoding is needed. + // // IS_HIDDEN will be true if the descriptor should only be visible // within the current object. // @@ -630,7 +639,9 @@ class Backend // address. After calling this the frontend will call // immutable_struct_set_init. virtual Bvariable* - immutable_struct(const std::string& name, bool is_hidden, bool is_common, + immutable_struct(const std::string& name, + const std::string& asm_name, + bool is_hidden, bool is_common, Btype* type, Location) = 0; // Set the initial value of a variable created by immutable_struct. @@ -648,11 +659,12 @@ class Backend // Create a reference to a named immutable initialized data // structure defined in some other package. This will be a // structure created by a call to immutable_struct with the same - // NAME and TYPE and with IS_COMMON passed as false. This + // NAME, ASM_NAME and TYPE and with IS_COMMON passed as false. This // corresponds to an extern const global variable in C. virtual Bvariable* - immutable_struct_reference(const std::string& name, Btype* type, - Location) = 0; + immutable_struct_reference(const std::string& name, + const std::string& asm_name, + Btype* type, Location) = 0; // Labels. @@ -707,7 +719,7 @@ class Backend // Create a statement that runs all deferred calls for FUNCTION. This should // be a statement that looks like this in C++: // finish: - // try { UNDEFER; } catch { CHECK_DEFER; goto finish; } + // try { DEFER_RETURN; } catch { CHECK_DEFER; goto finish; } virtual Bstatement* function_defer_statement(Bfunction* function, Bexpression* undefer, Bexpression* check_defer, Location) = 0; @@ -740,8 +752,4 @@ class Backend const std::vector<Bvariable*>& variable_decls) = 0; }; -// The backend interface has to define this function. - -extern Backend* go_get_backend(); - #endif // !defined(GO_BACKEND_H) diff --git a/gcc/go/gofrontend/dataflow.cc b/gcc/go/gofrontend/dataflow.cc deleted file mode 100644 index bf1d54ab26..0000000000 --- a/gcc/go/gofrontend/dataflow.cc +++ /dev/null @@ -1,299 +0,0 @@ -// dataflow.cc -- Go frontend dataflow. - -// Copyright 2009 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-system.h" - -#include "gogo.h" -#include "expressions.h" -#include "statements.h" -#include "dataflow.h" - -// This class is used to traverse the tree to look for uses of -// variables. - -class Dataflow_traverse_expressions : public Traverse -{ - public: - Dataflow_traverse_expressions(Dataflow* dataflow, Statement* statement) - : Traverse(traverse_blocks | traverse_expressions), - dataflow_(dataflow), statement_(statement) - { } - - protected: - // Only look at top-level expressions: do not descend into blocks. - // They will be examined via Dataflow_traverse_statements. - int - block(Block*) - { return TRAVERSE_SKIP_COMPONENTS; } - - int - expression(Expression**); - - private: - // The dataflow information. - Dataflow* dataflow_; - // The Statement in which we are looking. - Statement* statement_; -}; - -// Given an expression, return the Named_object that it refers to, if -// it is a local variable. - -static Named_object* -get_var(Expression* expr) -{ - Var_expression* ve = expr->var_expression(); - if (ve == NULL) - return NULL; - Named_object* no = ve->named_object(); - go_assert(no->is_variable() || no->is_result_variable()); - if (no->is_variable() && no->var_value()->is_global()) - return NULL; - return no; -} - -// Look for a reference to a variable in an expression. - -int -Dataflow_traverse_expressions::expression(Expression** expr) -{ - Named_object* no = get_var(*expr); - if (no != NULL) - this->dataflow_->add_ref(no, this->statement_); - return TRAVERSE_CONTINUE; -} - -// This class is used to handle an assignment statement. - -class Dataflow_traverse_assignment : public Traverse_assignments -{ - public: - Dataflow_traverse_assignment(Dataflow* dataflow, Statement* statement) - : dataflow_(dataflow), statement_(statement) - { } - - protected: - void - initialize_variable(Named_object*); - - void - assignment(Expression** lhs, Expression** rhs); - - void - value(Expression**, bool, bool); - - private: - // The dataflow information. - Dataflow* dataflow_; - // The Statement in which we are looking. - Statement* statement_; -}; - -// Handle a variable initialization. - -void -Dataflow_traverse_assignment::initialize_variable(Named_object* var) -{ - Expression* init = var->var_value()->init(); - this->dataflow_->add_def(var, init, this->statement_, true); - if (init != NULL) - { - Expression* e = init; - this->value(&e, true, true); - go_assert(e == init); - } -} - -// Handle an assignment in a statement. - -void -Dataflow_traverse_assignment::assignment(Expression** plhs, Expression** prhs) -{ - Named_object* no = get_var(*plhs); - if (no != NULL) - { - Expression* rhs = prhs == NULL ? NULL : *prhs; - this->dataflow_->add_def(no, rhs, this->statement_, false); - } - else - { - // If this is not a variable it may be some computed lvalue, and - // we want to look for references to variables in that lvalue. - this->value(plhs, false, false); - } - if (prhs != NULL) - this->value(prhs, true, false); -} - -// Handle a value in a statement. - -void -Dataflow_traverse_assignment::value(Expression** pexpr, bool, bool) -{ - Named_object* no = get_var(*pexpr); - if (no != NULL) - this->dataflow_->add_ref(no, this->statement_); - else - { - Dataflow_traverse_expressions dte(this->dataflow_, this->statement_); - Expression::traverse(pexpr, &dte); - } -} - -// This class is used to traverse the tree to look for statements. - -class Dataflow_traverse_statements : public Traverse -{ - public: - Dataflow_traverse_statements(Dataflow* dataflow) - : Traverse(traverse_statements), - dataflow_(dataflow) - { } - - protected: - int - statement(Block*, size_t* pindex, Statement*); - - private: - // The dataflow information. - Dataflow* dataflow_; -}; - -// For each Statement, we look for expressions. - -int -Dataflow_traverse_statements::statement(Block* block, size_t* pindex, - Statement *statement) -{ - Dataflow_traverse_assignment dta(this->dataflow_, statement); - - // For thunk statements, make sure to traverse the call expression to - // find any reference to a variable being used as an argument. - if (!statement->traverse_assignments(&dta) - || statement->thunk_statement() != NULL) - { - // Case statements in selects will be lowered into temporaries at this - // point so our dataflow analysis will miss references between a/c and ch - // in case statements of the form a,c := <-ch. Do a special dataflow - // analysis for select statements here; the analysis for the blocks will - // be handled as usual. - if (statement->select_statement() != NULL) - statement->select_statement()->analyze_dataflow(this->dataflow_); - - Dataflow_traverse_expressions dte(this->dataflow_, statement); - statement->traverse(block, pindex, &dte); - } - return TRAVERSE_CONTINUE; -} - -// Compare variables. - -bool -Dataflow::Compare_vars::operator()(const Named_object* no1, - const Named_object* no2) const -{ - if (no1->name() < no2->name()) - return true; - if (no1->name() > no2->name()) - return false; - - // We can have two different variables with the same name. - Location loc1 = no1->location(); - Location loc2 = no2->location(); - if (loc1 < loc2) - return false; - if (loc1 > loc2) - return true; - if (Linemap::is_predeclared_location(loc1)) - return false; - - if (no1 == no2 - || (no1->is_result_variable() - && no2->is_result_variable()) - || ((no1->is_variable() - && no1->var_value()->is_type_switch_var()) - && (no2->is_variable() - && no2->var_value()->is_type_switch_var()))) - return false; - - // We can't have two variables with the same name in the same - // location unless they are type switch variables which share the same - // fake location. - go_unreachable(); -} - -// Class Dataflow. - -Dataflow::Dataflow() - : defs_(), refs_() -{ -} - -// Build the dataflow information. - -void -Dataflow::initialize(Gogo* gogo) -{ - Dataflow_traverse_statements dts(this); - gogo->traverse(&dts); -} - -// Add a definition of a variable. - -void -Dataflow::add_def(Named_object* var, Expression* val, Statement* statement, - bool is_init) -{ - Defs* defnull = NULL; - std::pair<Defmap::iterator, bool> ins = - this->defs_.insert(std::make_pair(var, defnull)); - if (ins.second) - ins.first->second = new Defs; - Def def; - def.statement = statement; - def.val = val; - def.is_init = is_init; - ins.first->second->push_back(def); -} - -// Add a reference to a variable. - -void -Dataflow::add_ref(Named_object* var, Statement* statement) -{ - Refs* refnull = NULL; - std::pair<Refmap::iterator, bool> ins = - this->refs_.insert(std::make_pair(var, refnull)); - if (ins.second) - ins.first->second = new Refs; - Ref ref; - ref.statement = statement; - ins.first->second->push_back(ref); -} - -// Return the definitions of a variable. - -const Dataflow::Defs* -Dataflow::find_defs(Named_object* var) const -{ - Defmap::const_iterator p = this->defs_.find(var); - if (p == this->defs_.end()) - return NULL; - else - return p->second; -} - -// Return the references of a variable. - -const Dataflow::Refs* -Dataflow::find_refs(Named_object* var) const -{ - Refmap::const_iterator p = this->refs_.find(var); - if (p == this->refs_.end()) - return NULL; - else - return p->second; -} diff --git a/gcc/go/gofrontend/dataflow.h b/gcc/go/gofrontend/dataflow.h deleted file mode 100644 index a75c8e661f..0000000000 --- a/gcc/go/gofrontend/dataflow.h +++ /dev/null @@ -1,91 +0,0 @@ -// dataflow.h -- Go frontend dataflow. -*- C++ -*- - -// Copyright 2009 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. - -#ifndef GO_DATAFLOW_H -#define GO_DATAFLOW_H - -class Expression; -class Named_object; -class Statement; - -// Dataflow information about the Go program. - -class Dataflow -{ - public: - // A variable definition. - struct Def - { - // The statement where the variable is defined. - Statement* statement; - // The value to which the variable is set. This may be NULL. - Expression* val; - // Whether this is an initialization of the variable. - bool is_init; - }; - - // A variable reference. - struct Ref - { - // The statement where the variable is referenced. - Statement* statement; - }; - - // A list of defs. - typedef std::vector<Def> Defs; - - // A list of refs. - typedef std::vector<Ref> Refs; - - Dataflow(); - - // Initialize the dataflow information. - void - initialize(Gogo*); - - // Add a definition of a variable. STATEMENT assigns a value to - // VAR. VAL is the value if it is known, NULL otherwise. - void - add_def(Named_object* var, Expression* val, Statement* statement, - bool is_init); - - // Add a reference to a variable. VAR is the variable, and - // STATEMENT is the statement which refers to it. - void - add_ref(Named_object* var, Statement* statement); - - // Return the definitions of VAR--the places where it is set. - const Defs* - find_defs(Named_object* var) const; - - // Return the references to VAR--the places where it is used. - const Refs* - find_refs(Named_object* var) const; - - private: - // Order variables in the map. - struct Compare_vars - { - bool - operator()(const Named_object*, const Named_object*) const; - }; - - // Map from variables to a list of defs of the variable. We use a - // map rather than a hash table because the order in which we - // process variables may affect the resulting code. - typedef std::map<Named_object*, Defs*, Compare_vars> Defmap; - - // Map from variables to a list of refs to the vairable. - typedef std::map<Named_object*, Refs*, Compare_vars> Refmap; - - // Variable defs. - Defmap defs_; - // Variable refs; - Refmap refs_; -}; - - -#endif // !defined(GO_DATAFLOW_H) diff --git a/gcc/go/gofrontend/escape.cc b/gcc/go/gofrontend/escape.cc index 3a5738381d..a90c527a36 100644 --- a/gcc/go/gofrontend/escape.cc +++ b/gcc/go/gofrontend/escape.cc @@ -1,1669 +1,2869 @@ -// escape.cc -- Go frontend escape analysis. +// escape.cc -- Go escape analysis (based on Go compiler algorithm). -// Copyright 2015 The Go Authors. All rights reserved. +// Copyright 2016 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-system.h" -#include <fstream> +#include <limits> +#include <stack> +#include <sstream> -#include "go-c.h" -#include "go-dump.h" -#include "go-optimize.h" +#include "gogo.h" #include "types.h" -#include "statements.h" #include "expressions.h" -#include "dataflow.h" -#include "gogo.h" +#include "statements.h" #include "escape.h" +#include "ast-dump.h" +#include "go-optimize.h" +#include "go-diagnostics.h" -// Class Node. +// class Node. -Node::Node(Node_classification classification, Named_object* object) - : classification_(classification), object_(object) -{ - // Give every node a unique ID for representation purposes. - static int count; - this->id_ = count++; -} +// Return the node's type, if it makes sense for it to have one. -Node::~Node() +Type* +Node::type() const { + if (this->object() != NULL + && this->object()->is_variable()) + return this->object()->var_value()->type(); + else if (this->object() != NULL + && this->object()->is_function()) + return this->object()->func_value()->type(); + else if (this->expr() != NULL) + return this->expr()->type(); + else + return NULL; } -// Make a call node for FUNCTION. +// A helper for reporting; return this node's location. -Node* -Node::make_call(Named_object* function) +Location +Node::location() const { - return new Call_node(function); + if (this->object() != NULL && !this->object()->is_sink()) + return this->object()->location(); + else if (this->expr() != NULL) + return this->expr()->location(); + else if (this->statement() != NULL) + return this->statement()->location(); + else + return Linemap::unknown_location(); } -// Make a connection node for OBJECT. +// To match the cmd/gc debug output, strip away the packed prefixes on functions +// and variable/expressions. -Node* -Node::make_connection(Named_object* object, Escapement_lattice e) +std::string +strip_packed_prefix(Gogo* gogo, const std::string& s) { - return new Connection_node(object, e); + std::string packed_prefix = "." + gogo->pkgpath() + "."; + std::string fmt = s; + for (size_t pos = fmt.find(packed_prefix); + pos != std::string::npos; + pos = fmt.find(packed_prefix)) + fmt.erase(pos, packed_prefix.length()); + return fmt; } -// Return this node's label, which will be the name seen in the graphical -// representation. +// A helper for debugging; return this node's AST formatted string. +// This is an implementation of gc's Nconv with obj.FmtShort. -const std::string& -Node::label() +std::string +Node::ast_format(Gogo* gogo) const { - if (this->label_.empty()) + std::ostringstream ss; + if (this->is_sink()) + ss << ".sink"; + else if (this->object() != NULL) { - this->label_ = "[label=\""; - this->label_ += this->object_->name(); - this->label_ += "\"]"; + Named_object* no = this->object(); + if (no->is_function() && no->func_value()->enclosing() != NULL) + return "func literal"; + ss << no->name(); } - return this->label_; -} + else if (this->expr() != NULL) + { + Expression* e = this->expr(); + bool is_call = e->call_expression() != NULL; + if (is_call) + e->call_expression()->fn(); + Func_expression* fe = e->func_expression();; + + bool is_closure = fe != NULL && fe->closure() != NULL; + if (is_closure) + { + if (is_call) + return "(func literal)()"; + return "func literal"; + } + Ast_dump_context::dump_to_stream(this->expr(), &ss); + } + else + { + Statement* s = this->statement(); + Goto_unnamed_statement* unnamed = s->goto_unnamed_statement(); + if (unnamed != NULL) + { + Statement* derived = unnamed->unnamed_label()->derived_from(); + if (derived != NULL) + { + switch (derived->classification()) + { + case Statement::STATEMENT_FOR: + case Statement::STATEMENT_FOR_RANGE: + return "for loop"; + break; -// Class Call_node. + case Statement::STATEMENT_SWITCH: + return "switch"; + break; -Call_node::Call_node(Named_object* function) - : Node(NODE_CALL, function) -{ go_assert(function->is_function() || function->is_function_declaration()); } + case Statement::STATEMENT_TYPE_SWITCH: + return "type switch"; + break; -const std::string& -Call_node::name() -{ - if (this->get_name().empty()) - { - char buf[30]; - snprintf(buf, sizeof buf, "CallNode%d", this->id()); - this->set_name(std::string(buf)); + default: + break; + } + } + } + Ast_dump_context::dump_to_stream(s, &ss); } - return this->get_name(); + + return strip_packed_prefix(gogo, ss.str()); } -// Class Connection_node. +// A helper for debugging; return this node's detailed format string. +// This is an implementation of gc's Jconv with obj.FmtShort. -const std::string& -Connection_node::name() +std::string +Node::details() const { - if (this->get_name().empty()) - { - char buf[30]; - snprintf(buf, sizeof buf, "ConnectionNode%d", this->id()); - this->set_name(std::string(buf)); - } - return this->get_name(); -} + std::stringstream details; -const std::string& -Connection_node::label() -{ - if (this->get_label().empty()) - { - std::string label = "[label=\""; - label += this->object()->name(); - label += "\",color="; - switch (this->escape_state_) - { - case ESCAPE_GLOBAL: - label += "red"; - break; - case ESCAPE_ARG: - label += "blue"; - break; - case ESCAPE_NONE: - label += "black"; - break; - } - label += "]"; - this->set_label(label); - } - return this->get_label(); -} + if (!this->is_sink()) + details << " l(" << Linemap::location_to_line(this->location()) << ")"; -// Dump a connection node and its edges to a dump file. + bool is_varargs = false; + bool is_address_taken = false; + bool is_in_heap = false; + bool is_assigned = false; + std::string class_name; -void -Connection_node::dump_connection(Connection_dump_context* cdc) -{ - cdc->write_string(this->name() + this->label()); - cdc->write_c_string("\n"); + Expression* e = this->expr(); + Named_object* node_object = NULL; + if (this->object() != NULL) + node_object = this->object(); + else if (e != NULL && e->var_expression() != NULL) + node_object = e->var_expression()->named_object(); - for (std::set<Node*>::const_iterator p = this->edges().begin(); - p != this->edges().end(); - ++p) + if (node_object) { - cdc->write_string(this->name()); - cdc->write_c_string("->"); - - if ((*p)->object()->is_function()) + // TODO(cmang): For named variables and functions, we want to output + // the function depth. + if (node_object->is_variable()) { - char buf[100]; - snprintf(buf, sizeof buf, "dummy%d[lhead=cluster%d]", - (*p)->id(), (*p)->id()); - cdc->write_c_string(buf); + Variable* var = node_object->var_value(); + is_varargs = var->is_varargs_parameter(); + is_address_taken = (var->is_address_taken() + || var->is_non_escaping_address_taken()); + is_in_heap = var->is_in_heap(); + is_assigned = var->init() != NULL; + + if (var->is_global()) + class_name = "PEXTERN"; + else if (var->is_parameter()) + class_name = "PPARAM"; + else if (var->is_closure()) + class_name = "PPARAMREF"; + else + class_name = "PAUTO"; + } + else if (node_object->is_result_variable()) + class_name = "PPARAMOUT"; + else if (node_object->is_function() + || node_object->is_function_declaration()) + class_name = "PFUNC"; + } + else if (e != NULL && e->enclosed_var_expression() != NULL) + { + Named_object* enclosed = e->enclosed_var_expression()->variable(); + if (enclosed->is_variable()) + { + Variable* var = enclosed->var_value(); + is_address_taken = (var->is_address_taken() + || var->is_non_escaping_address_taken()); } else - cdc->write_string((*p)->name()); - cdc->write_c_string("\n"); + { + Result_variable* var = enclosed->result_var_value(); + is_address_taken = (var->is_address_taken() + || var->is_non_escaping_address_taken()); + } + class_name = "PPARAMREF"; + } + + if (!class_name.empty()) + { + details << " class(" << class_name; + if (is_in_heap) + details << ",heap"; + details << ")"; } -} -// The -fgo-dump-calls flag to activate call graph dumps in GraphViz DOT format. + switch ((this->encoding() & ESCAPE_MASK)) + { + case Node::ESCAPE_UNKNOWN: + break; -Go_dump call_graph_dump_flag("calls"); + case Node::ESCAPE_HEAP: + details << " esc(h)"; + break; -// Class Call_dump_context. + case Node::ESCAPE_SCOPE: + details << " esc(s)"; + break; -Call_dump_context::Call_dump_context(std::ostream* out) - : ostream_(out), gogo_(NULL) -{ } + case Node::ESCAPE_NONE: + details << " esc(no)"; + break; -// Dump files will be named %basename%.calls.dot + case Node::ESCAPE_NEVER: + details << " esc(N)"; + break; -const char* kCallDumpFileExtension = ".calls.dot"; + default: + details << " esc(" << this->encoding() << ")"; + break; + } -// Dump the call graph in DOT format. + if (this->state_ != NULL && this->state_->loop_depth != 0) + details << " ld(" << this->state_->loop_depth << ")"; -void -Call_dump_context::dump(Gogo* gogo, const char* basename) -{ - std::ofstream* out = new std::ofstream(); - std::string dumpname(basename); - dumpname += kCallDumpFileExtension; - out->open(dumpname.c_str()); + if (is_varargs) + details << " isddd(1)"; + if (is_address_taken) + details << " addrtaken"; + if (is_assigned) + details << " assigned"; - if (out->fail()) + return details.str(); +} + +std::string +Node::op_format() const +{ + std::stringstream op; + Ast_dump_context adc(&op, false); + if (this->expr() != NULL) { - error("cannot open %s:%m, -fgo-dump-calls ignored", dumpname.c_str()); - return; - } + Expression* e = this->expr(); + switch (e->classification()) + { + case Expression::EXPRESSION_UNARY: + adc.dump_operator(e->unary_expression()->op()); + break; - this->gogo_ = gogo; - this->ostream_ = out; + case Expression::EXPRESSION_BINARY: + adc.dump_operator(e->binary_expression()->op()); + break; - this->write_string("digraph CallGraph {\n"); - std::set<Node*> call_graph = gogo->call_graph(); + case Expression::EXPRESSION_CALL: + op << "function call"; + break; - // Generate GraphViz nodes for each node. - for (std::set<Node*>::const_iterator p = call_graph.begin(); - p != call_graph.end(); - ++p) + case Expression::EXPRESSION_FUNC_REFERENCE: + if (e->func_expression()->is_runtime_function()) + { + switch (e->func_expression()->runtime_code()) + { + case Runtime::GOPANIC: + op << "panic"; + break; + + case Runtime::GROWSLICE: + op << "append"; + break; + + case Runtime::SLICECOPY: + case Runtime::SLICESTRINGCOPY: + case Runtime::TYPEDSLICECOPY: + op << "copy"; + break; + + case Runtime::MAKECHAN: + case Runtime::MAKEMAP: + case Runtime::MAKESLICE: + case Runtime::MAKESLICE64: + op << "make"; + break; + + case Runtime::DEFERPROC: + op << "defer"; + break; + + case Runtime::GORECOVER: + op << "recover"; + break; + + case Runtime::CLOSE: + op << "close"; + break; + + default: + break; + } + } + break; + + case Expression::EXPRESSION_ALLOCATION: + op << "new"; + break; + + case Expression::EXPRESSION_RECEIVE: + op << "<-"; + break; + + default: + break; + } + } + + if (this->statement() != NULL) { - this->write_string((*p)->name() + (*p)->label()); - this->write_c_string("\n"); - - // Generate a graphical representation of the caller-callee relationship. - std::set<Node*> callees = (*p)->edges(); - for (std::set<Node*>::const_iterator ce = callees.begin(); - ce != callees.end(); - ++ce) + switch (this->statement()->classification()) { - this->write_string((*p)->name() + "->" + (*ce)->name()); - this->write_c_string("\n"); + case Statement::STATEMENT_DEFER: + op << "defer"; + break; + + case Statement::STATEMENT_RETURN: + op << "return"; + break; + + default: + break; } } - this->write_string("}"); - out->close(); + return op.str(); } -// Dump the Call Graph of the program to the dump file. +// Return this node's state, creating it if has not been initialized. -void Gogo::dump_call_graph(const char* basename) +Node::Escape_state* +Node::state(Escape_context* context, Named_object* fn) { - if (::call_graph_dump_flag.is_enabled()) + if (this->state_ == NULL) { - Call_dump_context cdc; - cdc.dump(this, basename); + if (this->expr() != NULL && this->expr()->var_expression() != NULL) + { + // Tie state of variable references to underlying variables. + Named_object* var_no = this->expr()->var_expression()->named_object(); + Node* var_node = Node::make_node(var_no); + this->state_ = var_node->state(context, fn); + } + else + { + this->state_ = new Node::Escape_state; + if (fn == NULL) + fn = context->current_function(); + + this->state_->fn = fn; + } } + go_assert(this->state_ != NULL); + return this->state_; } -// Implementation of String_dump interface. - void -Call_dump_context::write_c_string(const char* s) +Node::set_encoding(int enc) { - this->ostream() << s; + this->encoding_ = enc; + if (this->expr() != NULL + && this->expr()->var_expression() != NULL) + { + // Set underlying object as well. + Named_object* no = this->expr()->var_expression()->named_object(); + Node::make_node(no)->set_encoding(enc); + } } -void -Call_dump_context::write_string(const std::string& s) +bool +Node::is_big(Escape_context* context) const { - this->ostream() << s; + Type* t = this->type(); + if (t == NULL + || t->is_call_multiple_result_type() + || t->is_sink_type() + || t->is_void_type() + || t->is_abstract()) + return false; + + int64_t size; + bool ok = t->backend_type_size(context->gogo(), &size); + bool big = ok && (size < 0 || size > 10 * 1024 * 1024); + + if (this->expr() != NULL) + { + if (this->expr()->allocation_expression() != NULL) + { + ok = t->deref()->backend_type_size(context->gogo(), &size); + big = big || size <= 0 || size >= (1 << 16); + } + else if (this->expr()->call_expression() != NULL) + { + Call_expression* call = this->expr()->call_expression(); + Func_expression* fn = call->fn()->func_expression(); + if (fn != NULL + && fn->is_runtime_function() + && (fn->runtime_code() == Runtime::MAKESLICE + || fn->runtime_code() == Runtime::MAKESLICE64)) + { + // Second argument is length. + Expression_list::iterator p = call->args()->begin(); + ++p; + + Numeric_constant nc; + unsigned long v; + if ((*p)->numeric_constant_value(&nc) + && nc.to_unsigned_long(&v) == Numeric_constant::NC_UL_VALID) + big = big || v >= (1 << 16); + } + } + } + + return big; } -// The -fgo-dump-conns flag to activate connection graph dumps in -// GraphViz DOT format. +bool +Node::is_sink() const +{ + if (this->object() != NULL + && this->object()->is_sink()) + return true; + else if (this->expr() != NULL + && this->expr()->is_sink_expression()) + return true; + return false; +} -Go_dump connection_graph_dump_flag("conns"); +std::map<Named_object*, Node*> Node::objects; +std::map<Expression*, Node*> Node::expressions; +std::map<Statement*, Node*> Node::statements; -// Class Connection_dump_context. +// Make a object node or return a cached node for this object. -Connection_dump_context::Connection_dump_context(std::ostream* out) - : ostream_(out), gogo_(NULL) -{ } +Node* +Node::make_node(Named_object* no) +{ + if (Node::objects.find(no) != Node::objects.end()) + return Node::objects[no]; -// Dump files will be named %basename%.conns.dot + Node* n = new Node(no); + std::pair<Named_object*, Node*> val(no, n); + Node::objects.insert(val); + return n; +} -const char* kConnectionDumpFileExtension = ".conns.dot"; +// Make an expression node or return a cached node for this expression. -// Dump the connection graph in DOT format. +Node* +Node::make_node(Expression* e) +{ + if (Node::expressions.find(e) != Node::expressions.end()) + return Node::expressions[e]; -void -Connection_dump_context::dump(Gogo* gogo, const char* basename) + Node* n = new Node(e); + std::pair<Expression*, Node*> val(e, n); + Node::expressions.insert(val); + return n; +} + +// Make a statement node or return a cached node for this statement. + +Node* +Node::make_node(Statement* s) { - std::ofstream* out = new std::ofstream(); - std::string dumpname(basename); - dumpname += kConnectionDumpFileExtension; - out->open(dumpname.c_str()); + if (Node::statements.find(s) != Node::statements.end()) + return Node::statements[s]; - if (out->fail()) - { - error("cannot open %s:%m, -fgo-dump-conns ignored", dumpname.c_str()); - return; - } + Node* n = new Node(s); + std::pair<Statement*, Node*> val(s, n); + Node::statements.insert(val); + return n; +} + +// Returns the maximum of an exisiting escape value +// (and its additional parameter flow flags) and a new escape type. + +int +Node::max_encoding(int e, int etype) +{ + if ((e & ESCAPE_MASK) >= etype) + return e; + if (etype == Node::ESCAPE_NONE || etype == Node::ESCAPE_RETURN) + return (e & ~ESCAPE_MASK) | etype; + return etype; +} - this->gogo_ = gogo; - this->ostream_ = out; - - this->write_string("digraph ConnectionGraph {\n"); - this->write_string("compound=true\n"); - - // Dump global objects. - std::set<Node*> globals = this->gogo_->global_connections(); - this->write_c_string("subgraph globals{\n"); - this->write_c_string("label=\"NonLocalGraph\"\n"); - this->write_c_string("color=red\n"); - for (std::set<Node*>::const_iterator p1 = globals.begin(); - p1 != globals.end(); - ++p1) - (*p1)->connection_node()->dump_connection(this); - this->write_c_string("}\n"); - - std::set<Node*> roots = this->gogo_->connection_roots(); - for (std::set<Node*>::const_reverse_iterator p1 = roots.rbegin(); - p1 != roots.rend(); - ++p1) +// Return a modified encoding for an input parameter that flows into an +// output parameter. + +int +Node::note_inout_flows(int e, int index, Level level) +{ + // Flow+level is encoded in two bits. + // 00 = not flow, xx = level+1 for 0 <= level <= maxEncodedLevel. + // 16 bits for Esc allows 6x2bits or 4x3bits or 3x4bits if additional + // information would be useful. + if (level.value() <= 0 && level.suffix_value() > 0) + return Node::max_encoding(e|ESCAPE_CONTENT_ESCAPES, Node::ESCAPE_NONE); + if (level.value() < 0) + return Node::ESCAPE_HEAP; + if (level.value() > ESCAPE_MAX_ENCODED_LEVEL) + level = Level::From(ESCAPE_MAX_ENCODED_LEVEL); + + int encoded = level.value() + 1; + int shift = ESCAPE_BITS_PER_OUTPUT_IN_TAG * index + ESCAPE_RETURN_BITS; + int old = (e >> shift) & ESCAPE_BITS_MASK_FOR_TAG; + if (old == 0 + || (encoded != 0 && encoded < old)) + old = encoded; + + int encoded_flow = old << shift; + if (((encoded_flow >> shift) & ESCAPE_BITS_MASK_FOR_TAG) != old) { - std::set<Node*> objects = (*p1)->connection_node()->objects(); - - char buf[150]; - snprintf(buf, sizeof buf, "subgraph cluster%d", (*p1)->id()); - this->write_c_string(buf); - this->write_string("{\n"); - snprintf(buf, sizeof buf, "dummy%d[shape=point,style=invis]\n", - (*p1)->id()); - this->write_c_string(buf); - this->write_string("label = \"" + (*p1)->object()->name() + "\"\n"); - - for (std::set<Node*>::const_iterator p2 = objects.begin(); - p2 != objects.end(); - ++p2) - (*p2)->connection_node()->dump_connection(this); - - this->write_string("}\n"); + // Failed to encode. Put this on the heap. + return Node::ESCAPE_HEAP; } - this->write_string("}"); - out->close(); + + return (e & ~(ESCAPE_BITS_MASK_FOR_TAG << shift)) | encoded_flow; +} + +// Class Escape_context. + +Escape_context::Escape_context(Gogo* gogo, bool recursive) + : gogo_(gogo), current_function_(NULL), recursive_(recursive), + sink_(Node::make_node(Named_object::make_sink())), loop_depth_(0), + flood_id_(0), pdepth_(0) +{ + // The sink always escapes to heap and strictly lives outside of the + // current function i.e. loop_depth == -1. + this->sink_->set_encoding(Node::ESCAPE_HEAP); + Node::Escape_state* state = this->sink_->state(this, NULL); + state->loop_depth = -1; +} + +std::string +debug_function_name(Named_object* fn) +{ + if (fn == NULL) + return "<S>"; + + if (!fn->is_function() + || fn->func_value()->enclosing() == NULL) + return Gogo::unpack_hidden_name(fn->name()); + + // Closures are named ".$nested#" where # starts from 0 to distinguish + // between closures. The cmd/gc closures are named in the format + // "enclosing.func#" where # starts from 1. If this is a closure, format + // its name to match cmd/gc. + Named_object* enclosing = fn->func_value()->enclosing(); + + // Extract #. + std::string name = Gogo::unpack_hidden_name(fn->name()); + int closure_num = (int)strtol(name.substr(6).c_str(), NULL, 0); + closure_num++; + + name = Gogo::unpack_hidden_name(enclosing->name()); + char buf[200]; + snprintf(buf, sizeof buf, "%s.func%d", name.c_str(), closure_num); + return buf; } +// Return the name of the current function. + +std::string +Escape_context::current_function_name() const +{ + return debug_function_name(this->current_function_); +} + +// Initialize the dummy return values for this Node N using the results +// in FNTYPE. + void -Gogo::dump_connection_graphs(const char* basename) +Escape_context::init_retvals(Node* n, Function_type* fntype) { - if (::connection_graph_dump_flag.is_enabled()) + if (fntype == NULL || fntype->results() == NULL) + return; + + Node::Escape_state* state = n->state(this, NULL); + Location loc = n->location(); + + int i = 0; + char buf[50]; + for (Typed_identifier_list::const_iterator p = fntype->results()->begin(); + p != fntype->results()->end(); + ++p, ++i) { - Connection_dump_context cdc; - cdc.dump(this, basename); + snprintf(buf, sizeof buf, ".out%d", i); + Variable* dummy_var = new Variable(p->type(), NULL, false, false, + false, loc); + dummy_var->set_is_used(); + Named_object* dummy_no = + Named_object::make_variable(buf, NULL, dummy_var); + Node* dummy_node = Node::make_node(dummy_no); + // Initialize the state of the dummy output node. + dummy_node->state(this, NULL); + + // Add dummy node to the retvals of n. + state->retvals.push_back(dummy_node); } } -// Implementation of String_dump interface. + +// Apply an indirection to N and return the result. +// This really only works if N is an expression node; it essentially becomes +// Node::make_node(n->expr()->deref()). We need the escape context to set the +// correct loop depth, however. + +Node* +Escape_context::add_dereference(Node* n) +{ + // Just return the original node if we can't add an indirection. + if (n->object() != NULL || n->statement() != NULL) + return n; + + Node* ind = Node::make_node(n->expr()->deref()); + // Initialize the state if this node doesn't already exist. + ind->state(this, NULL); + return ind; +} void -Connection_dump_context::write_c_string(const char* s) +Escape_context::track(Node* n) +{ + n->set_encoding(Node::ESCAPE_NONE); + // Initialize this node's state if it hasn't been encountered + // before. + Node::Escape_state* state = n->state(this, NULL); + state->loop_depth = this->loop_depth_; + + this->noesc_.push_back(n); +} + +// Return the string representation of an escapement encoding. + +std::string +Escape_note::make_tag(int encoding) { - this->ostream() << s; + char buf[50]; + snprintf(buf, sizeof buf, "esc:0x%x", encoding); + return buf; } +// Return the escapement encoding for a string tag. + +int +Escape_note::parse_tag(std::string* tag) +{ + if (tag == NULL || tag->substr(0, 4) != "esc:") + return Node::ESCAPE_UNKNOWN; + int encoding = (int)strtol(tag->substr(4).c_str(), NULL, 0); + if (encoding == 0) + return Node::ESCAPE_UNKNOWN; + return encoding; +} + + +// The -fgo-optimize-alloc flag activates this escape analysis. + +Go_optimize optimize_allocation_flag("allocs"); + +// Analyze the program flow for escape information. + void -Connection_dump_context::write_string(const std::string& s) +Gogo::analyze_escape() { - this->ostream() << s; + if (!optimize_allocation_flag.is_enabled() || saw_errors()) + return; + + // Discover strongly connected groups of functions to analyze for escape + // information in this package. + this->discover_analysis_sets(); + + for (std::vector<Analysis_set>::iterator p = this->analysis_sets_.begin(); + p != this->analysis_sets_.end(); + ++p) + { + std::vector<Named_object*> stack = p->first; + Escape_context* context = new Escape_context(this, p->second); + + // Analyze the flow of each function; build the connection graph. + // This is the assign phase. + for (std::vector<Named_object*>::reverse_iterator fn = stack.rbegin(); + fn != stack.rend(); + ++fn) + { + context->set_current_function(*fn); + this->assign_connectivity(context, *fn); + } + + // Propagate levels across each dst. This is the flood phase. + std::set<Node*> dsts = context->dsts(); + for (std::set<Node*>::iterator n = dsts.begin(); + n != dsts.end(); + ++n) + this->propagate_escape(context, *n); + + // Tag each exported function's parameters with escape information. + for (std::vector<Named_object*>::iterator fn = stack.begin(); + fn != stack.end(); + ++fn) + this->tag_function(context, *fn); + + if (this->debug_escape_level() != 0) + { + std::vector<Node*> noesc = context->non_escaping_nodes(); + for (std::vector<Node*>::const_iterator n = noesc.begin(); + n != noesc.end(); + ++n) + { + Node::Escape_state* state = (*n)->state(context, NULL); + if (((*n)->encoding() & ESCAPE_MASK) == int(Node::ESCAPE_NONE)) + go_inform((*n)->location(), "%s %s does not escape", + strip_packed_prefix(this, debug_function_name(state->fn)).c_str(), + (*n)->ast_format(this).c_str()); + } + // TODO(cmang): Which objects in context->noesc actually don't escape. + } + delete context; + } } -// A traversal class used to build a call graph for this program. +// Traverse the program, discovering the functions that are roots of strongly +// connected components. The goal of this phase to produce a set of functions +// that must be analyzed in order. -class Build_call_graph : public Traverse +class Escape_analysis_discover : public Traverse { public: - Build_call_graph(Gogo* gogo) - : Traverse(traverse_functions - | traverse_expressions), - gogo_(gogo), current_function_(NULL) + Escape_analysis_discover(Gogo* gogo) + : Traverse(traverse_functions), + gogo_(gogo), component_ids_() { } int function(Named_object*); int - expression(Expression**); + visit(Named_object*); + + int + visit_code(Named_object*, int); private: - // The IR. + // A counter used to generate the ID for the function node in the graph. + static int id; + + // Type used to map functions to an ID in a graph of connected components. + typedef Unordered_map(Named_object*, int) Component_ids; + + // The Go IR. Gogo* gogo_; - // The current function being traversed, for reference when traversing the - // function body. - Named_object* current_function_; + // The list of functions encountered during connected component discovery. + Component_ids component_ids_; + // The stack of functions that this component consists of. + std::stack<Named_object*> stack_; }; -// Add each function to the call graph and then traverse each function's -// body to find callee functions. +int Escape_analysis_discover::id = 0; + +// Visit each function. int -Build_call_graph::function(Named_object* fn) +Escape_analysis_discover::function(Named_object* fn) { - this->gogo_->add_call_node(fn); - go_assert(this->current_function_ == NULL); - this->current_function_ = fn; - fn->func_value()->traverse(this); - this->current_function_ = NULL; + this->visit(fn); return TRAVERSE_CONTINUE; } -// Find function calls and add them as callees to CURRENT_FUNCTION. +// Visit a function FN, adding it to the current stack of functions +// in this connected component. If this is the root of the component, +// create a set of functions to be analyzed later. +// +// Finding these sets is finding strongly connected components +// in the static call graph. The algorithm for doing that is taken +// from Sedgewick, Algorithms, Second Edition, p. 482, with two +// adaptations. +// +// First, a closure (fn->func_value()->enclosing() == NULL) cannot be the +// root of a connected component. Refusing to use it as a root +// forces it into the component of the function in which it appears. +// This is more convenient for escape analysis. +// +// Second, each function becomes two virtual nodes in the graph, +// with numbers n and n+1. We record the function's node number as n +// but search from node n+1. If the search tells us that the component +// number (min) is n+1, we know that this is a trivial component: one function +// plus its closures. If the search tells us that the component number is +// n, then there was a path from node n+1 back to node n, meaning that +// the function set is mutually recursive. The escape analysis can be +// more precise when analyzing a single non-recursive function than +// when analyzing a set of mutually recursive functions. int -Build_call_graph::expression(Expression** pexpr) +Escape_analysis_discover::visit(Named_object* fn) { - if (this->current_function_ == NULL) - return TRAVERSE_CONTINUE; - - Expression* expr = *pexpr; - Named_object* fn; - if (expr->call_expression() != NULL) + Component_ids::const_iterator p = this->component_ids_.find(fn); + if (p != this->component_ids_.end()) + // Already visited. + return p->second; + + this->id++; + int id = this->id; + this->component_ids_[fn] = id; + this->id++; + int min = this->id; + + this->stack_.push(fn); + min = this->visit_code(fn, min); + if ((min == id || min == id + 1) + && fn->is_function() + && fn->func_value()->enclosing() == NULL) { - Func_expression* func = expr->call_expression()->fn()->func_expression(); - if (func == NULL) + bool recursive = min == id; + std::vector<Named_object*> group; + + for (; !this->stack_.empty(); this->stack_.pop()) { - // This is probably a variable holding a function value or a closure. - return TRAVERSE_CONTINUE; + Named_object* n = this->stack_.top(); + if (n == fn) + { + this->stack_.pop(); + break; + } + + group.push_back(n); + this->component_ids_[n] = std::numeric_limits<int>::max(); } - fn = func->named_object(); + group.push_back(fn); + this->component_ids_[fn] = std::numeric_limits<int>::max(); + + std::reverse(group.begin(), group.end()); + this->gogo_->add_analysis_set(group, recursive); } - else if (expr->func_expression() != NULL) - fn = expr->func_expression()->named_object(); - else - return TRAVERSE_CONTINUE; - Node* caller = this->gogo_->lookup_call_node(this->current_function_); - go_assert(caller != NULL); + return min; +} + +// Helper class for discovery step. Traverse expressions looking for +// function calls and closures to visit during the discovery step. + +class Escape_discover_expr : public Traverse +{ + public: + Escape_discover_expr(Escape_analysis_discover* ead, int min) + : Traverse(traverse_expressions), + ead_(ead), min_(min) + { } + + int + min() + { return this->min_; } + + int + expression(Expression** pexpr); + + private: + // The original discovery analysis. + Escape_analysis_discover* ead_; + // The minimum component ID in this group. + int min_; +}; + +// Visit any calls or closures found when discovering expressions. + +int +Escape_discover_expr::expression(Expression** pexpr) +{ + Expression* e = *pexpr; + Named_object* fn = NULL; + if (e->call_expression() != NULL + && e->call_expression()->fn()->func_expression() != NULL) + { + // Method call or function call. + fn = e->call_expression()->fn()->func_expression()->named_object(); + } + else if (e->func_expression() != NULL + && e->func_expression()->closure() != NULL) + { + // Closure. + fn = e->func_expression()->named_object(); + } - // Create the callee here if it hasn't been seen yet. This could also be a - // function defined in another package. - Node* callee = this->gogo_->add_call_node(fn); - caller->add_edge(callee); + if (fn != NULL) + this->min_ = std::min(this->min_, this->ead_->visit(fn)); return TRAVERSE_CONTINUE; } -// Build the call graph. +// Visit the body of each function, returns ID of the minimum connected +// component found in the body. + +int +Escape_analysis_discover::visit_code(Named_object* fn, int min) +{ + if (!fn->is_function()) + return min; + + Escape_discover_expr ede(this, min); + fn->func_value()->traverse(&ede); + return ede.min(); +} + +// Discover strongly connected groups of functions to analyze. void -Gogo::build_call_graph() +Gogo::discover_analysis_sets() { - Build_call_graph build_calls(this); - this->traverse(&build_calls); + Escape_analysis_discover ead(this); + this->traverse(&ead); } -// A traversal class used to build a connection graph for each node in the -// call graph. +// Traverse all label and goto statements and mark the underlying label +// as looping or not looping. -class Build_connection_graphs : public Traverse +class Escape_analysis_loop : public Traverse { public: - Build_connection_graphs(Gogo* gogo) - : Traverse(traverse_variables - | traverse_statements), - gogo_(gogo), dataflow_(new Dataflow), current_function_(NULL) - { - // Collect dataflow information for this program. - this->dataflow_->initialize(this->gogo_); - } - - void - set_current_function(Named_object* function) - { this->current_function_ = function; } + Escape_analysis_loop() + : Traverse(traverse_statements) + { } int - variable(Named_object*); + statement(Block*, size_t*, Statement*); +}; + +int +Escape_analysis_loop::statement(Block*, size_t*, Statement* s) +{ + if (s->label_statement() != NULL) + s->label_statement()->label()->set_nonlooping(); + else if (s->goto_statement() != NULL) + { + if (s->goto_statement()->label()->nonlooping()) + s->goto_statement()->label()->set_looping(); + } + return TRAVERSE_CONTINUE; +} + +// Traversal class used to look at all interesting statements within a function +// in order to build a connectivity graph between all nodes within a context's +// scope. + +class Escape_analysis_assign : public Traverse +{ +public: + Escape_analysis_assign(Escape_context* context, Named_object* fn) + : Traverse(traverse_statements + | traverse_expressions), + context_(context), fn_(fn) + { } + // Model statements within a function as assignments and flows between nodes. int statement(Block*, size_t*, Statement*); + // Model expressions within a function as assignments and flows between nodes. + int + expression(Expression**); - private: - // Handle a call EXPR referencing OBJECT. + // Model calls within a function as assignments and flows between arguments + // and results. void - handle_call(Named_object* object, Expression* expr); - - // Get the initialization values of a composite literal EXPR. - Expression_list* - get_composite_arguments(Expression* expr); + call(Call_expression* call); - // Handle defining OBJECT as a composite literal EXPR. + // Model the assignment of DST to SRC. void - handle_composite_literal(Named_object* object, Expression* expr); + assign(Node* dst, Node* src); - // Handle analysis of the left and right operands of a binary expression - // with respect to OBJECT. + // Model the assignment of DST to dereference of SRC. void - handle_binary(Named_object* object, Expression* expr); + assign_deref(Node* dst, Node* src); - // Resolve the outermost named object of EXPR if there is one. - Named_object* - resolve_var_reference(Expression* expr); + // Model the input-to-output assignment flow of one of a function call's + // arguments, where the flow is encoding in NOTE. + int + assign_from_note(std::string* note, const std::vector<Node*>& dsts, + Node* src); - // The IR. - Gogo* gogo_; - // The Dataflow information for this program. - Dataflow* dataflow_; - // The current function whose connection graph is being built. - Named_object* current_function_; + // Record the flow of SRC to DST in DST. + void + flows(Node* dst, Node* src); + +private: + // The escape context for this set of functions. + Escape_context* context_; + // The current function being analyzed. + Named_object* fn_; }; -// Given an expression, return the outermost Named_object that it refers to. -// This is used to model the simplification between assignments in our analysis. +// Model statements within a function as assignments and flows between nodes. -Named_object* -Build_connection_graphs::resolve_var_reference(Expression* expr) +int +Escape_analysis_assign::statement(Block*, size_t*, Statement* s) { - bool done = false; - Expression* orig = expr; - while (!done) + // Adjust the loop depth as we enter/exit blocks related to for statements. + bool is_for_statement = (s->is_block_statement() + && s->block_statement()->is_lowered_for_statement()); + if (is_for_statement) + this->context_->increase_loop_depth(); + + s->traverse_contents(this); + + if (is_for_statement) + this->context_->decrease_loop_depth(); + + Gogo* gogo = this->context_->gogo(); + int debug_level = gogo->debug_escape_level(); + if (debug_level > 1 + && s->unnamed_label_statement() == NULL + && s->expression_statement() == NULL + && !s->is_block_statement()) { - // The goal of this loop is to find the variable being referenced, p, - // when the expression is: - switch (expr->classification()) - { - case Expression::EXPRESSION_UNARY: - // &p or *p - expr = expr->unary_expression()->operand(); - break; - - case Expression::EXPRESSION_ARRAY_INDEX: - // p[i][j] - expr = expr->array_index_expression()->array(); - break; - - case Expression::EXPRESSION_FIELD_REFERENCE: - // p.i.j - orig = expr; - expr = expr->field_reference_expression()->expr(); - break; - - case Expression::EXPRESSION_RECEIVE: - // <- p - expr = expr->receive_expression()->channel(); - break; - - case Expression::EXPRESSION_BOUND_METHOD: - // p.c - expr = expr->bound_method_expression()->first_argument(); - break; - - case Expression::EXPRESSION_CALL: - // p.c() - expr = expr->call_expression()->fn(); - break; + Node* n = Node::make_node(s); + std::string fn_name = this->context_->current_function_name(); + go_inform(s->location(), "[%d] %s esc: %s", + this->context_->loop_depth(), fn_name.c_str(), + n->ast_format(gogo).c_str()); + } - case Expression::EXPRESSION_TEMPORARY_REFERENCE: - // This is used after lowering, so try to retrieve the original - // expression that might have been lowered into a temporary statement. - expr = expr->temporary_reference_expression()->statement()->init(); - if (expr == NULL) - return NULL; - break; + switch (s->classification()) + { + case Statement::STATEMENT_VARIABLE_DECLARATION: + { + Named_object* var = s->variable_declaration_statement()->var(); + Node* var_node = Node::make_node(var); + Node::Escape_state* state = var_node->state(this->context_, NULL); + state->loop_depth = this->context_->loop_depth(); + + // Set the loop depth for this declaration. + if (var->is_variable() + && var->var_value()->init() != NULL) + { + Node* init_node = Node::make_node(var->var_value()->init()); + this->assign(var_node, init_node); + } + } + break; - case Expression::EXPRESSION_SET_AND_USE_TEMPORARY: - expr = expr->set_and_use_temporary_expression()->expression(); - break; + case Statement::STATEMENT_LABEL: + { + Label_statement* label_stmt = s->label_statement(); + if (label_stmt->label()->looping()) + this->context_->increase_loop_depth(); - case Expression::EXPRESSION_COMPOUND: - // p && q - expr = expr->compound_expression()->init(); - break; + if (debug_level > 1) + { + std::string label_type = (label_stmt->label()->looping() + ? "looping" + : "nonlooping"); + go_inform(s->location(), "%s %s label", + label_stmt->label()->name().c_str(), + label_type.c_str()); + } + } + break; - case Expression::EXPRESSION_CONDITIONAL: - // if p { - expr = expr->conditional_expression()->condition(); - break; + case Statement::STATEMENT_SWITCH: + case Statement::STATEMENT_TYPE_SWITCH: + // Want to model the assignment of each case variable to the switched upon + // variable. This should be lowered into assignment statements; nothing + // to here if that's the case. + // TODO(cmang): Verify. + break; - case Expression::EXPRESSION_CONVERSION: - // T(p) - expr = expr->conversion_expression()->expr(); - break; + case Statement::STATEMENT_ASSIGNMENT: + { + Assignment_statement* assn = s->assignment_statement(); + Node* lhs = Node::make_node(assn->lhs()); + Node* rhs = Node::make_node(assn->rhs()); + + // TODO(cmang): Add special case for escape analysis no-op: + // func (b *Buffer) Foo() { + // n, m := ... + // b.buf = b.buf[n:m] + // } + // This is okay for now, it just means b escapes; it is conservative. + this->assign(lhs, rhs); + } + break; - case Expression::EXPRESSION_TYPE_GUARD: - // p.(T) - expr = expr->type_guard_expression()->expr(); - break; + case Statement::STATEMENT_SEND: + { + Node* sent_node = Node::make_node(s->send_statement()->val()); + this->assign(this->context_->sink(), sent_node); + } + break; - case Expression::EXPRESSION_UNSAFE_CONVERSION: - { - Expression* e = expr->unsafe_conversion_expression()->expr(); - if (e->call_result_expression() != NULL - && e->call_result_expression()->index() == 0) - { - // a, ok := p.(T) gets lowered into a call to one of the interface - // to type conversion functions instead of a type guard expression. - // We only want to make a connection between a and p, the bool - // result should not escape because p escapes. - e = e->call_result_expression()->call(); - - Named_object* fn = - e->call_expression()->fn()->func_expression()->named_object(); - std::string fn_name = fn->name(); - if (fn->package() == NULL - && fn->is_function_declaration() - && !fn->func_declaration_value()->asm_name().empty()) - { - if (fn_name == "ifaceI2E2" - || fn_name == "ifaceI2I2") - e = e->call_expression()->args()->at(0); - else if (fn_name == "ifaceE2I2" - || fn_name == "ifaceI2I2" - || fn_name == "ifaceE2T2P" - || fn_name == "ifaceI2T2P" - || fn_name == "ifaceE2T2" - || fn_name == "ifaceI2T2") - e = e->call_expression()->args()->at(1); - } - } - expr = e; - } + case Statement::STATEMENT_DEFER: + if (this->context_->loop_depth() == 1) break; + // fallthrough - default: - done = true; - break; + case Statement::STATEMENT_GO: + { + // Defer f(x) or go f(x). + // Both f and x escape to the heap. + Thunk_statement* thunk = s->thunk_statement(); + if (thunk->call()->call_expression() == NULL) + break; + + Call_expression* call = thunk->call()->call_expression(); + Node* func_node = Node::make_node(call->fn()); + this->assign(this->context_->sink(), func_node); + if (call->args() != NULL) + { + for (Expression_list::const_iterator p = call->args()->begin(); + p != call->args()->end(); + ++p) + { + Node* arg_node = Node::make_node(*p); + this->assign(this->context_->sink(), arg_node); + } + } } - } + break; - Var_expression* ve = expr->var_expression(); - if (ve != NULL) - { - Named_object* no = ve->named_object(); - go_assert(no->is_variable() || no->is_result_variable()); + // TODO(cmang): Associate returned values with dummy return nodes. - if (no->is_variable() - && no->var_value()->is_closure() - && this->current_function_->func_value()->needs_closure()) - { - // CURRENT_FUNCTION is a closure and NO is being set to a - // variable in the enclosing function. - Named_object* closure = this->current_function_; - - // If NO is a closure variable, the expression is a field - // reference to the enclosed variable. - Field_reference_expression* fre = - orig->deref()->field_reference_expression(); - if (fre == NULL) - return NULL; - - unsigned int closure_index = fre->field_index(); - no = closure->func_value()->enclosing_var(closure_index - 1); - } - return no; + default: + break; } - return NULL; + return TRAVERSE_SKIP_COMPONENTS; } -// For a call that references OBJECT, associate the OBJECT argument with the -// appropriate call parameter. +// Model expressions within a function as assignments and flows between nodes. -void -Build_connection_graphs::handle_call(Named_object* object, Expression* e) +int +Escape_analysis_assign::expression(Expression** pexpr) { - // Only call expression statements are interesting - // e.g. 'func(var)' for which we can show var does not escape. - Call_expression* ce = e->call_expression(); - if (ce == NULL) - return; - else if (ce->args() == NULL) - { - if (ce->fn()->interface_field_reference_expression() != NULL) - { - // This is a call to an interface method with no arguments. OBJECT - // must be the receiver and we assume it escapes. - Connection_node* rcvr_node = - this->gogo_->add_connection_node(object)->connection_node(); - rcvr_node->set_escape_state(Node::ESCAPE_ARG); - } - return; - } - - // If the function call that references OBJECT is unknown, we must be - // conservative and assume every argument escapes. A function call is unknown - // if it is a call to a function stored in a variable or a call to an - // interface method. - if (ce->fn()->func_expression() == NULL) + Gogo* gogo = this->context_->gogo(); + int debug_level = gogo->debug_escape_level(); + + // Big stuff escapes unconditionally. + Node* n = Node::make_node(*pexpr); + if ((n->encoding() & ESCAPE_MASK) != int(Node::ESCAPE_HEAP) + && n->is_big(this->context_)) { - for (Expression_list::const_iterator arg = ce->args()->begin(); - arg != ce->args()->end(); - ++arg) - { - Named_object* arg_no = this->resolve_var_reference(*arg); - if (arg_no != NULL) - { - Connection_node* arg_node = - this->gogo_->add_connection_node(arg_no)->connection_node(); - arg_node->set_escape_state(Node::ESCAPE_ARG); - } - else if ((*arg)->call_expression() != NULL) - this->handle_call(object, *arg); - } - return; + if (debug_level > 1) + go_inform((*pexpr)->location(), "too large for stack"); + n->set_encoding(Node::ESCAPE_HEAP); + (*pexpr)->address_taken(true); + this->assign(this->context_->sink(), n); } - Named_object* callee = ce->fn()->func_expression()->named_object(); - Function_type* fntype; - if (callee->is_function()) - fntype = callee->func_value()->type(); - else - fntype = callee->func_declaration_value()->type(); + if ((*pexpr)->func_expression() == NULL) + (*pexpr)->traverse_subexpressions(this); - Node* callee_node = this->gogo_->lookup_connection_node(callee); - if (callee_node == NULL && callee->is_function()) + if (debug_level > 1) { - // Might be a nested closure that hasn't been analyzed yet. - Named_object* currfn = this->current_function_; - callee_node = this->gogo_->add_connection_node(callee); - this->current_function_ = callee; - callee->func_value()->traverse(this); - this->current_function_ = currfn; + Node* n = Node::make_node(*pexpr); + std::string fn_name = this->context_->current_function_name(); + go_inform((*pexpr)->location(), "[%d] %s esc: %s", + this->context_->loop_depth(), fn_name.c_str(), + n->ast_format(gogo).c_str()); } - // First find which arguments OBJECT is to CALLEE. Given a function call, - // OBJECT could be an argument multiple times e.g. CALLEE(OBJECT, OBJECT). - // TODO(cmang): This should be done by the Dataflow analysis so we don't have - // to do it each time we see a function call. FIXME. - Expression_list* args = ce->args()->copy(); - if (fntype->is_varargs() - && args->back()->slice_literal() != NULL) + switch ((*pexpr)->classification()) { - // Is the function is varargs, the last argument is lowered into a slice - // containing all original arguments. We want to traverse the original - // arguments here. - Slice_construction_expression* sce = args->back()->slice_literal(); - for (Expression_list::const_iterator p = sce->vals()->begin(); - p != sce->vals()->end(); - ++p) - { - if (*p != NULL) - args->push_back(*p); - } - } - - // ARG_POSITION is just a counter used to keep track of the index in the list - // of arguments to this call. In a method call, the receiver will always be - // the first argument. When looking at the function type, it will not be the - // first element in the parameter list; instead, the receiver will be - // non-NULL. For convenience, mark the position of the receiver argument - // as negative. - int arg_position = fntype->is_method() ? -1 : 0; - std::list<int> positions; - for (Expression_list::const_iterator p = args->begin(); - p != args->end(); - ++p, ++arg_position) - { - Expression* arg = *p; - - // An argument might be a chain of method calls, some of which are - // converted from value to pointer types. Just remove the unary - // conversion if it exists. - if (arg->unary_expression() != NULL) - arg = arg->unary_expression()->operand(); - - // The reference to OBJECT might be in a nested call argument. - if (arg->call_expression() != NULL) - this->handle_call(object, arg); - - std::vector<Named_object*> objects; - if (arg->is_composite_literal() - || arg->heap_expression() != NULL) - { - // For a call that has a composite literal as an argument, traverse - // the initializers of the composite literal for extra objects to - // associate with a parameter in this function. - Expression_list* comp_args = this->get_composite_arguments(arg); - if (comp_args == NULL) - continue; + case Expression::EXPRESSION_CALL: + { + Call_expression* call = (*pexpr)->call_expression(); + this->call(call); - for (size_t i = 0; i < comp_args->size(); ++i) - { - Expression* comp_arg = comp_args->at(i); - if (comp_arg == NULL) - continue; - else if (comp_arg->is_composite_literal() - || comp_arg->heap_expression() != NULL) + Func_expression* fe = call->fn()->func_expression(); + if (fe != NULL && fe->is_runtime_function()) + { + switch (fe->runtime_code()) + { + case Runtime::GOPANIC: { - // Of course, there are situations where a composite literal - // initialization value is also a composite literal. - Expression_list* nested_args = - this->get_composite_arguments(comp_arg); - if (nested_args != NULL) - comp_args->append(nested_args); + // Argument could leak through recover. + Node* panic_arg = Node::make_node(call->args()->front()); + this->assign(this->context_->sink(), panic_arg); } + break; - Named_object* no = this->resolve_var_reference(comp_arg); - if (no != NULL) - objects.push_back(no); - } - } - else - { - Named_object* arg_no = this->resolve_var_reference(arg); - if (arg_no != NULL) - objects.push_back(arg_no); - } - - // There are no variables to consider for this parameter. - if (objects.empty()) - continue; - - for (std::vector<Named_object*>::const_iterator p1 = objects.begin(); - p1 != objects.end(); - ++p1) - { - // If CALLEE is defined in another package and we have imported escape - // information about its parameters, update the escape state of this - // argument appropriately. If there is no escape information for this - // function, we have to assume all arguments escape. - if (callee->package() != NULL - || fntype->is_builtin()) - { - Node::Escapement_lattice param_escape = Node::ESCAPE_NONE; - if (fntype->has_escape_info()) + case Runtime::GROWSLICE: { - if (arg_position == -1) + // The contents being appended leak. + if (call->is_varargs()) { - // Use the escape info from the receiver. - param_escape = fntype->receiver_escape_state(); + Node* appended = Node::make_node(call->args()->back()); + this->assign_deref(this->context_->sink(), appended); } - else if (fntype->parameters() != NULL) + else { - const Node::Escape_states* states = - fntype->parameter_escape_states(); - - int param_size = fntype->parameters()->size(); - if (arg_position >= param_size) + for (Expression_list::const_iterator pa = + call->args()->begin(); + pa != call->args()->end(); + ++pa) { - go_assert(fntype->is_varargs()); - param_escape = states->back(); + Node* arg = Node::make_node(*pa); + this->assign(this->context_->sink(), arg); } - else - param_escape = - fntype->parameter_escape_states()->at(arg_position); } - } - else - param_escape = Node::ESCAPE_ARG; - Connection_node* arg_node = - this->gogo_->add_connection_node(*p1)->connection_node(); - if (arg_node->escape_state() > param_escape) - arg_node->set_escape_state(param_escape); - } + if (debug_level > 2) + go_error_at((*pexpr)->location(), + "special treatment of append(slice1, slice2...)"); - if (*p1 == object) - positions.push_back(arg_position); - } - } + // The content of the original slice leaks as well. + Node* appendee = Node::make_node(call->args()->front()); + this->assign_deref(this->context_->sink(), appendee); + } + break; - // If OBJECT was not found in CALLEE's arguments, OBJECT is likely a - // subexpression of one of the arguments e.g. CALLEE(a[OBJECT]). This call - // does not give any useful information about whether OBJECT escapes. - if (positions.empty()) - return; + case Runtime::SLICECOPY: + case Runtime::SLICESTRINGCOPY: + case Runtime::TYPEDSLICECOPY: + { + // Lose track of the copied content. + Node* copied = Node::make_node(call->args()->back()); + this->assign_deref(this->context_->sink(), copied); + } + break; - // The idea here is to associate the OBJECT in the caller context with the - // parameter in the callee context. This also needs to consider varargs. - // This only works with functions with arguments. - if (!callee->is_function()) - return; + case Runtime::MAKECHAN: + case Runtime::MAKEMAP: + case Runtime::MAKESLICE: + case Runtime::MAKESLICE64: + case Runtime::SLICEBYTETOSTRING: + case Runtime::SLICERUNETOSTRING: + case Runtime::STRINGTOSLICEBYTE: + case Runtime::STRINGTOSLICERUNE: + case Runtime::CONCATSTRINGS: + case Runtime::CONCATSTRING2: + case Runtime::CONCATSTRING3: + case Runtime::CONCATSTRING4: + case Runtime::CONCATSTRING5: + case Runtime::CONSTRUCT_MAP: + case Runtime::INTSTRING: + { + Node* runtime_node = Node::make_node(fe); + this->context_->track(runtime_node); + } + break; - // Use the bindings in the callee to lookup the associated parameter. - const Bindings* callee_bindings = callee->func_value()->block()->bindings(); + default: + break; + } + } + } + break; - // Next find the corresponding named parameters in the function signature. - const Typed_identifier_list* params = fntype->parameters(); - for (std::list<int>::const_iterator pos = positions.begin(); - params != NULL && pos != positions.end(); - ++pos) - { - std::string param_name; - if (*pos >= 0 && params->size() <= static_cast<size_t>(*pos)) - { - // There were more arguments than there are parameters. This must be - // varargs and the argument corresponds to the last parameter. - go_assert(fntype->is_varargs()); - param_name = params->back().name(); - } - else if (*pos < 0) - { - // We adjust the recorded position of method arguments by one to - // account for the receiver, so *pos == -1 implies this is the - // receiver and this must be a method call. - go_assert(fntype->is_method() && fntype->receiver() != NULL); - param_name = fntype->receiver()->name(); - } - else - param_name = params->at(*pos).name(); + case Expression::EXPRESSION_ALLOCATION: + { + // Same as above; this is Runtime::NEW. + Node* alloc_node = Node::make_node(*pexpr); + this->context_->track(alloc_node); + } + break; - if (Gogo::is_sink_name(param_name) || param_name.empty()) - continue; + case Expression::EXPRESSION_CONVERSION: + { + Type_conversion_expression* tce = (*pexpr)->conversion_expression(); + Node* tce_node = Node::make_node(tce); + Node* converted = Node::make_node(tce->expr()); + this->context_->track(tce_node); - // Get the object for the associated parameter in this binding. - Named_object* param_no = callee_bindings->lookup_local(param_name); - go_assert(param_no != NULL); + this->assign(tce_node, converted); + } + break; - // Add an edge from ARG_NODE in the caller context to the PARAM_NODE in - // the callee context. - if (object->is_variable() && object->var_value()->is_closure()) - { - int position = *pos; - if (fntype->is_method()) - ++position; + case Expression::EXPRESSION_FIXED_ARRAY_CONSTRUCTION: + case Expression::EXPRESSION_SLICE_CONSTRUCTION: + { + Node* array_node = Node::make_node(*pexpr); + if ((*pexpr)->slice_literal() != NULL) + this->context_->track(array_node); - // Calling a function within a closure with a closure argument. - // Resolve the real variable using the closure argument. - object = this->resolve_var_reference(ce->args()->at(position)); - } + Expression_list* vals = ((*pexpr)->slice_literal() != NULL + ? (*pexpr)->slice_literal()->vals() + : (*pexpr)->array_literal()->vals()); - Node* arg_node = this->gogo_->add_connection_node(object); - Node* param_node = this->gogo_->add_connection_node(param_no); - param_node->add_edge(arg_node); - } + if (vals != NULL) + { + // Connect the array to its values. + for (Expression_list::const_iterator p = vals->begin(); + p != vals->end(); + ++p) + if ((*p) != NULL) + this->assign(array_node, Node::make_node(*p)); + } + } + break; - // This is a method call with one argument: the receiver. - if (params == NULL) - { - go_assert(positions.size() == 1); - std::string rcvr_name = fntype->receiver()->name(); - if (Gogo::is_sink_name(rcvr_name) || rcvr_name.empty()) - return; - - Named_object* rcvr_no = callee_bindings->lookup_local(rcvr_name); - Node* arg_node = this->gogo_->add_connection_node(object); - Node* rcvr_node = this->gogo_->add_connection_node(rcvr_no); - rcvr_node->add_edge(arg_node); - } -} + case Expression::EXPRESSION_STRUCT_CONSTRUCTION: + { + Node* struct_node = Node::make_node(*pexpr); + Expression_list* vals = (*pexpr)->struct_literal()->vals(); + if (vals != NULL) + { + // Connect the struct to its values. + for (Expression_list::const_iterator p = vals->begin(); + p != vals->end(); + ++p) + { + if ((*p) != NULL) + this->assign(struct_node, Node::make_node(*p)); + } + } + } + break; -// Given a composite literal expression, return the initialization values. -// This is used to handle situations where call and composite literal -// expressions have nested composite literals as arguments/initializers. + case Expression::EXPRESSION_HEAP: + { + Node* pointer_node = Node::make_node(*pexpr); + Node* lit_node = Node::make_node((*pexpr)->heap_expression()->expr()); + this->context_->track(pointer_node); -Expression_list* -Build_connection_graphs::get_composite_arguments(Expression* expr) -{ - // A heap expression is just any expression that takes the address of a - // composite literal. - if (expr->heap_expression() != NULL) - expr = expr->heap_expression()->expr(); + // Connect pointer node to literal node; if the pointer node escapes, so + // does the literal node. + this->assign(pointer_node, lit_node); + } + break; - switch (expr->classification()) - { - case Expression::EXPRESSION_STRUCT_CONSTRUCTION: - return expr->struct_literal()->vals(); + case Expression::EXPRESSION_BOUND_METHOD: + { + Node* bound_node = Node::make_node(*pexpr); + this->context_->track(bound_node); - case Expression::EXPRESSION_FIXED_ARRAY_CONSTRUCTION: - return expr->array_literal()->vals(); + Expression* obj = (*pexpr)->bound_method_expression()->first_argument(); + Node* obj_node = Node::make_node(obj); - case Expression::EXPRESSION_SLICE_CONSTRUCTION: - return expr->slice_literal()->vals(); + // A bound method implies the receiver will be used outside of the + // lifetime of the method in some way. We lose track of the receiver. + this->assign(this->context_->sink(), obj_node); + } + break; case Expression::EXPRESSION_MAP_CONSTRUCTION: - return expr->map_literal()->vals(); + { + Map_construction_expression* mce = (*pexpr)->map_literal(); + Node* map_node = Node::make_node(mce); + this->context_->track(map_node); + + // All keys and values escape to memory. + if (mce->vals() != NULL) + { + for (Expression_list::const_iterator p = mce->vals()->begin(); + p != mce->vals()->end(); + ++p) + { + if ((*p) != NULL) + this->assign(this->context_->sink(), Node::make_node(*p)); + } + } + } + break; + + case Expression::EXPRESSION_FUNC_REFERENCE: + { + Func_expression* fe = (*pexpr)->func_expression(); + if (fe->closure() != NULL) + { + // Connect captured variables to the closure. + Node* closure_node = Node::make_node(fe); + this->context_->track(closure_node); + + // A closure expression already exists as the heap expression: + // &struct{f func_code, v []*Variable}{...}. + // Link closure to the addresses of the variables enclosed. + Heap_expression* he = fe->closure()->heap_expression(); + Struct_construction_expression* sce = he->expr()->struct_literal(); + + // First field is function code, other fields are variable + // references. + Expression_list::const_iterator p = sce->vals()->begin(); + ++p; + for (; p != sce->vals()->end(); ++p) + { + Node* enclosed_node = Node::make_node(*p); + Node::Escape_state* state = + enclosed_node->state(this->context_, NULL); + state->loop_depth = this->context_->loop_depth(); + this->assign(closure_node, enclosed_node); + } + } + } + break; + + case Expression::EXPRESSION_UNARY: + { + if ((*pexpr)->unary_expression()->op() != OPERATOR_AND) + break; + + Node* addr_node = Node::make_node(*pexpr); + this->context_->track(addr_node); + + Expression* operand = (*pexpr)->unary_expression()->operand(); + Named_object* var = NULL; + if (operand->var_expression() != NULL) + var = operand->var_expression()->named_object(); + else if (operand->enclosed_var_expression() != NULL) + var = operand->enclosed_var_expression()->variable(); + else if (operand->temporary_reference_expression() != NULL) + { + // Found in runtime/chanbarrier_test.go. The address of a struct + // reference is usually a heap expression, except when it is a part + // of a case statement. In that case, it is lowered into a + // temporary reference and never linked to the heap expression that + // initializes it. In general, when taking the address of some + // temporary, the analysis should really be looking at the initial + // value of that temporary. + Temporary_reference_expression* tre = + operand->temporary_reference_expression(); + if (tre->statement() != NULL + && tre->statement()->temporary_statement()->init() != NULL) + { + Expression* init = + tre->statement()->temporary_statement()->init(); + Node* init_node = Node::make_node(init); + this->assign(addr_node, init_node); + } + } + + if (var == NULL) + break; + + if (var->is_variable() + && !var->var_value()->is_parameter()) + { + // For &x, use the loop depth of x if known. + Node::Escape_state* addr_state = + addr_node->state(this->context_, NULL); + Node* operand_node = Node::make_node(operand); + Node::Escape_state* operand_state = + operand_node->state(this->context_, NULL); + if (operand_state->loop_depth != 0) + addr_state->loop_depth = operand_state->loop_depth; + } + else if ((var->is_variable() + && var->var_value()->is_parameter()) + || var->is_result_variable()) + { + Node::Escape_state* addr_state = + addr_node->state(this->context_, NULL); + addr_state->loop_depth = 1; + } + } + break; default: - return NULL; + break; } + return TRAVERSE_SKIP_COMPONENTS; } -// Given an OBJECT defined as a composite literal EXPR, create edges between -// OBJECT and all variables referenced in EXPR. +// Model calls within a function as assignments and flows between arguments +// and results. void -Build_connection_graphs::handle_composite_literal(Named_object* object, - Expression* expr) +Escape_analysis_assign::call(Call_expression* call) { - Expression_list* args = this->get_composite_arguments(expr); - if (args == NULL) - return; - - std::vector<Named_object*> composite_args; - for (Expression_list::const_iterator p = args->begin(); - p != args->end(); - ++p) + Gogo* gogo = this->context_->gogo(); + int debug_level = gogo->debug_escape_level(); + + Func_expression* fn = call->fn()->func_expression(); + Function_type* fntype = call->get_function_type(); + bool indirect = false; + + // Interface method calls or closure calls are indirect calls. + if (fntype == NULL + || (fntype->is_method() + && fntype->receiver()->type()->interface_type() != NULL) + || fn == NULL + || (fn->named_object()->is_function() + && fn->named_object()->func_value()->enclosing() != NULL)) + indirect = true; + + Node* call_node = Node::make_node(call); + std::vector<Node*> arg_nodes; + if (call->fn()->interface_field_reference_expression() != NULL) { - if (*p == NULL) - continue; - else if ((*p)->call_expression() != NULL) - this->handle_call(object, *p); - else if ((*p)->func_expression() != NULL) - composite_args.push_back((*p)->func_expression()->named_object()); - else if ((*p)->is_composite_literal() - || (*p)->heap_expression() != NULL) - this->handle_composite_literal(object, *p); - - Named_object* no = this->resolve_var_reference(*p); - if (no != NULL) - composite_args.push_back(no); + Interface_field_reference_expression* ifre = + call->fn()->interface_field_reference_expression(); + Node* field_node = Node::make_node(ifre->expr()); + arg_nodes.push_back(field_node); } - Node* object_node = this->gogo_->add_connection_node(object); - for (std::vector<Named_object*>::const_iterator p = composite_args.begin(); - p != composite_args.end(); - ++p) + if (call->args() != NULL) { - Node* arg_node = this->gogo_->add_connection_node(*p); - object_node->add_edge(arg_node); + for (Expression_list::const_iterator p = call->args()->begin(); + p != call->args()->end(); + ++p) + arg_nodes.push_back(Node::make_node(*p)); } -} -// Given an OBJECT reference in a binary expression E, analyze the left and -// right operands for possible edges. + if (indirect) + { + // We don't know what happens to the parameters through indirect calls. + // Be conservative and assume they all flow to theSink. + for (std::vector<Node*>::iterator p = arg_nodes.begin(); + p != arg_nodes.end(); + ++p) + { + if (debug_level > 2) + go_inform(call->location(), + "esccall:: indirect call <- %s, untracked", + (*p)->ast_format(gogo).c_str()); + this->assign(this->context_->sink(), *p); + } -void -Build_connection_graphs::handle_binary(Named_object* object, Expression* e) -{ - Binary_expression* be = e->binary_expression(); - go_assert(be != NULL); - Expression* left = be->left(); - Expression* right = be->right(); - - if (left->call_result_expression() != NULL) - left = left->call_result_expression()->call(); - if (left->call_expression() != NULL) - this->handle_call(object, left); - else if (left->binary_expression() != NULL) - this->handle_binary(object, left); - if (right->call_result_expression() != NULL) - right = right->call_result_expression()->call(); - if (right->call_expression() != NULL) - this->handle_call(object, right); - else if (right->binary_expression() != NULL) - this->handle_binary(object, right); -} + this->context_->init_retvals(call_node, fntype); + return; + } -// Create connection nodes for each variable in a called function. + // If FN is an untagged function. + if (fn != NULL + && fn->named_object()->is_function() + && !fntype->is_tagged()) + { + if (debug_level > 2) + go_inform(call->location(), "esccall:: %s in recursive group", + call_node->ast_format(gogo).c_str()); -int -Build_connection_graphs::variable(Named_object* var) -{ - Node* var_node = this->gogo_->add_connection_node(var); - Node* root = this->gogo_->lookup_connection_node(this->current_function_); - go_assert(root != NULL); + Function* f = fn->named_object()->func_value(); + const Bindings* callee_bindings = f->block()->bindings(); - // Add VAR to the set of objects in CURRENT_FUNCTION's connection graph. - root->connection_node()->add_object(var_node); + const Typed_identifier_list* results = fntype->results(); + if (results != NULL) + { + // Setup output list on this call node. + Node::Escape_state* state = call_node->state(this->context_, NULL); + for (Typed_identifier_list::const_iterator p1 = results->begin(); + p1 != results->end(); + ++p1) + { + if (p1->name().empty() || Gogo::is_sink_name(p1->name())) + continue; - // A function's results always escape. - if (var->is_result_variable()) - var_node->connection_node()->set_escape_state(Node::ESCAPE_ARG); + Named_object* result_no = + callee_bindings->lookup_local(p1->name()); + go_assert(result_no != NULL); + Node* result_node = Node::make_node(result_no); + state->retvals.push_back(result_node); + } + } - // Create edges from a variable to its definitions. - const Dataflow::Defs* defs = this->dataflow_->find_defs(var); - if (defs != NULL) - { - for (Dataflow::Defs::const_iterator p = defs->begin(); - p != defs->end(); - ++p) + std::vector<Node*>::iterator p = arg_nodes.begin(); + if (fntype->is_method() + && fntype->receiver()->type()->has_pointer()) { - Expression* def = p->val; - if (def == NULL) - continue; - - if (def->conversion_expression() != NULL) - def = def->conversion_expression()->expr(); - if (def->func_expression() != NULL) + std::string rcvr_name = fntype->receiver()->name(); + if (rcvr_name.empty() || Gogo::is_sink_name(rcvr_name)) + ; + else { - // VAR is being defined as a function object. - Named_object* fn = def->func_expression()->named_object(); - Node* fn_node = this->gogo_->add_connection_node(fn); - var_node->add_edge(fn_node); + Named_object* rcvr_no = + callee_bindings->lookup_local(fntype->receiver()->name()); + go_assert(rcvr_no != NULL); + Node* rcvr_node = Node::make_node(rcvr_no); + this->assign(rcvr_node, *p); } - else if(def->is_composite_literal() - || def->heap_expression() != NULL) - this->handle_composite_literal(var, def); + ++p; + } - Named_object* ref = this->resolve_var_reference(def); - if (ref == NULL) - continue; + const Typed_identifier_list* til = fntype->parameters(); + if (til != NULL) + { + for (Typed_identifier_list::const_iterator p1 = til->begin(); + p1 != til->end(); + ++p1, ++p) + { + if (p1->name().empty() || Gogo::is_sink_name(p1->name())) + continue; - Node* ref_node = this->gogo_->add_connection_node(ref); - var_node->add_edge(ref_node); + Named_object* param_no = + callee_bindings->lookup_local(p1->name()); + go_assert(param_no != NULL); + Expression* arg = (*p)->expr(); + if (arg->var_expression() != NULL + && arg->var_expression()->named_object() == param_no) + continue; + + Node* param_node = Node::make_node(param_no); + this->assign(param_node, *p); + } + + for (; p != arg_nodes.end(); ++p) + { + if (debug_level > 2) + go_inform(call->location(), "esccall:: ... <- %s, untracked", + (*p)->ast_format(gogo).c_str()); + this->assign(this->context_->sink(), *p); + } } + + return; } - // Create edges from a reference to a variable. - const Dataflow::Refs* refs = this->dataflow_->find_refs(var); - if (refs != NULL) + if (debug_level > 2) + go_inform(call->location(), "esccall:: %s not recursive", + call_node->ast_format(gogo).c_str()); + + Node::Escape_state* call_state = call_node->state(this->context_, NULL); + if (!call_state->retvals.empty()) + go_error_at(Linemap::unknown_location(), + "esc already decorated call %s", + call_node->ast_format(gogo).c_str()); + this->context_->init_retvals(call_node, fntype); + + // Receiver. + std::vector<Node*>::iterator p = arg_nodes.begin(); + if (fntype->is_method() + && fntype->receiver()->type()->has_pointer() + && p != arg_nodes.end()) { - for (Dataflow::Refs::const_iterator p = refs->begin(); - p != refs->end(); - ++p) + // First argument to call will be the receiver. + std::string* note = fntype->receiver()->note(); + if (fntype->receiver()->type()->points_to() == NULL + && (*p)->expr()->unary_expression() != NULL + && (*p)->expr()->unary_expression()->op() == OPERATOR_AND) { - switch (p->statement->classification()) - { - case Statement::STATEMENT_ASSIGNMENT: + // This is a call to a value method that has been lowered into a call + // to a pointer method. Gccgo generates a pointer method for all + // method calls and takes the address of the value passed as the + // receiver then immediately dereferences it within the function. + // In this case, the receiver does not escape. + } + else + { + if (!Type::are_identical(fntype->receiver()->type(), + (*p)->expr()->type(), true, NULL)) { - Assignment_statement* assn = - p->statement->assignment_statement(); - Named_object* lhs_no = this->resolve_var_reference(assn->lhs()); - Named_object* rhs_no = this->resolve_var_reference(assn->rhs()); + // This will be converted later, preemptively track it instead + // of its conversion expression which will show up in a later pass. + this->context_->track(*p); + } + this->assign_from_note(note, call_state->retvals, *p); + } + p++; + } - Expression* rhs = assn->rhs(); - if (rhs->is_composite_literal() - || rhs->heap_expression() != NULL) - this->handle_composite_literal(var, rhs); + const Typed_identifier_list* til = fntype->parameters(); + if (til != NULL) + { + for (Typed_identifier_list::const_iterator pn = til->begin(); + pn != til->end() && p != arg_nodes.end(); + ++pn, ++p) + { + if (!Type::are_identical(pn->type(), (*p)->expr()->type(), + true, NULL)) + { + // This will be converted later, preemptively track it instead + // of its conversion expression which will show up in a later pass. + this->context_->track(*p); + } - if (rhs->call_result_expression() != NULL) + // TODO(cmang): Special care for varargs parameter? + Type* t = pn->type(); + if (t != NULL + && t->has_pointer()) + { + std::string* note = pn->note(); + int enc = this->assign_from_note(note, call_state->retvals, *p); + if (enc == Node::ESCAPE_NONE + && (call->is_deferred() + || call->is_concurrent())) { - // V's initialization will be a call result if - // V, V1 := call(VAR). - // There are no useful edges to make from V, but we want - // to make sure we handle the call that references VAR. - rhs = rhs->call_result_expression()->call(); + // TODO(cmang): Mark the argument as strictly non-escaping. } - if (rhs->call_expression() != NULL) - this->handle_call(var, rhs); - - // If there is no standalone variable on the rhs, this could be a - // binary expression, which isn't interesting for analysis or a - // composite literal or call expression, which we handled above. - // If the underlying variable on the rhs isn't VAR then it is - // likely an indexing expression where VAR is the index. - if(lhs_no == NULL - || rhs_no == NULL - || rhs_no != var) + } + } + + for (; p != arg_nodes.end(); ++p) + { + if (debug_level > 2) + go_inform(call->location(), "esccall:: ... <- %s, untracked", + (*p)->ast_format(gogo).c_str()); + this->assign(this->context_->sink(), *p); + } + } +} + +// Model the assignment of DST to SRC. +// Assert that SRC somehow gets assigned to DST. +// DST might need to be examined for evaluations that happen inside of it. +// For example, in [DST]*f(x) = [SRC]y, we lose track of the indirection and +// DST becomes the sink in our model. + +void +Escape_analysis_assign::assign(Node* dst, Node* src) +{ + Gogo* gogo = this->context_->gogo(); + int debug_level = gogo->debug_escape_level(); + if (debug_level > 1) + go_inform(dst->location(), "[%d] %s escassign: %s(%s)[%s] = %s(%s)[%s]", + this->context_->loop_depth(), + strip_packed_prefix(gogo, this->context_->current_function_name()).c_str(), + dst->ast_format(gogo).c_str(), dst->details().c_str(), + dst->op_format().c_str(), + src->ast_format(gogo).c_str(), src->details().c_str(), + src->op_format().c_str()); + + if (dst->expr() != NULL) + { + // Analyze the lhs of the assignment. + // Replace DST with this->context_->sink() if we can't track it. + Expression* e = dst->expr(); + switch (e->classification()) + { + case Expression::EXPRESSION_VAR_REFERENCE: + { + // If DST is a global variable, we have no way to track it. + Named_object* var = e->var_expression()->named_object(); + if (var->is_variable() && var->var_value()->is_global()) + dst = this->context_->sink(); + } + break; + + case Expression::EXPRESSION_FIELD_REFERENCE: + { + Expression* strct = e->field_reference_expression()->expr(); + if (strct->heap_expression() != NULL) + { + // When accessing the field of a struct reference, we lose + // track of the indirection. + dst = this->context_->sink(); break; + } - Node* def_node = this->gogo_->add_connection_node(lhs_no); - def_node->add_edge(var_node); - } - break; + // Treat DST.x = SRC as if it were DST = SRC. + Node* struct_node = Node::make_node(strct); + this->assign(struct_node, src); + return; + } - case Statement::STATEMENT_SEND: - { - Send_statement* send = p->statement->send_statement(); - Named_object* chan_no = this->resolve_var_reference(send->channel()); - Named_object* val_no = resolve_var_reference(send->val()); + case Expression::EXPRESSION_ARRAY_INDEX: + { + Array_index_expression* are = e->array_index_expression(); + if (!are->array()->type()->is_slice_type()) + { + // Treat DST[i] = SRC as if it were DST = SRC if DST if a fixed + // array. + Node* array_node = Node::make_node(are->array()); + this->assign(array_node, src); + return; + } + + // Lose track of the slice dereference. + dst = this->context_->sink(); + } + break; + + case Expression::EXPRESSION_UNARY: + // Lose track of the dereference. + if (e->unary_expression()->op() == OPERATOR_MULT) + dst = this->context_->sink(); + break; - if (chan_no == NULL || val_no == NULL) + case Expression::EXPRESSION_MAP_INDEX: + { + // Lose track of the map's key and value. + Expression* index = e->map_index_expression()->index(); + Node* index_node = Node::make_node(index); + this->assign(this->context_->sink(), index_node); + + dst = this->context_->sink(); + } + break; + + default: + // TODO(cmang): Add debugging info here: only a few expressions + // should leave DST unmodified. + break; + } + } + + if (src->expr() != NULL) + { + Expression* e = src->expr(); + switch (e->classification()) + { + case Expression::EXPRESSION_VAR_REFERENCE: + // DST = var + case Expression::EXPRESSION_HEAP: + // DST = &T{...}. + case Expression::EXPRESSION_FIXED_ARRAY_CONSTRUCTION: + case Expression::EXPRESSION_SLICE_CONSTRUCTION: + // DST = [...]T{...}. + case Expression::EXPRESSION_MAP_CONSTRUCTION: + // DST = map[T]V{...}. + case Expression::EXPRESSION_STRUCT_CONSTRUCTION: + // DST = T{...}. + case Expression::EXPRESSION_ALLOCATION: + // DST = new(T). + case Expression::EXPRESSION_BOUND_METHOD: + // DST = x.M. + this->flows(dst, src); + break; + + case Expression::EXPRESSION_UNSAFE_CONVERSION: + { + Expression* underlying = e->unsafe_conversion_expression()->expr(); + Node* underlying_node = Node::make_node(underlying); + this->assign(dst, underlying_node); + } + break; + + case Expression::EXPRESSION_ENCLOSED_VAR_REFERENCE: + { + Named_object* var = e->enclosed_var_expression()->variable(); + Node* var_node = Node::make_node(var); + this->flows(dst, var_node); + } + break; + + case Expression::EXPRESSION_CALL: + { + Call_expression* call = e->call_expression(); + Func_expression* fe = call->fn()->func_expression(); + if (fe != NULL && fe->is_runtime_function()) + { + switch (fe->runtime_code()) + { + case Runtime::GROWSLICE: + { + // Append returns the first argument. + // The subsequent arguments are already leaked because + // they are operands to append. + Node* appendee = Node::make_node(call->args()->front()); + this->assign(dst, appendee); + break; + } + + case Runtime::MAKECHAN: + case Runtime::MAKEMAP: + case Runtime::MAKESLICE: + case Runtime::MAKESLICE64: + // DST = make(...). + case Runtime::SLICEBYTETOSTRING: + // DST = string([]byte{...}). + case Runtime::SLICERUNETOSTRING: + // DST = string([]int{...}). + case Runtime::STRINGTOSLICEBYTE: + // DST = []byte(str). + case Runtime::STRINGTOSLICERUNE: + // DST = []rune(str). + case Runtime::CONCATSTRINGS: + case Runtime::CONCATSTRING2: + case Runtime::CONCATSTRING3: + case Runtime::CONCATSTRING4: + case Runtime::CONCATSTRING5: + // DST = str1 + str2 + case Runtime::CONSTRUCT_MAP: + // When building a map literal's backend representation. + // Likely never seen here and covered in + // Expression::EXPRESSION_MAP_CONSTRUCTION. + case Runtime::INTSTRING: + // DST = string(i). + case Runtime::IFACEE2E2: + case Runtime::IFACEI2E2: + case Runtime::IFACEE2I2: + case Runtime::IFACEI2I2: + case Runtime::IFACEE2T2P: + case Runtime::IFACEI2T2P: + case Runtime::IFACEE2T2: + case Runtime::IFACEI2T2: + case Runtime::REQUIREITAB: + // All versions of interface conversion that might result + // from a type assertion. Some of these are the result of + // a tuple type assertion statement and may not be covered + // by the case in Expression::EXPRESSION_CONVERSION or + // Expression::EXPRESSION_TYPE_GUARD. + this->flows(dst, src); + break; + + default: + break; + } + break; + } + else if (fe != NULL + && fe->named_object()->is_function() + && fe->named_object()->func_value()->is_method() + && (call->is_deferred() + || call->is_concurrent())) + { + // For a method call thunk, lose track of the call and treat it + // as if DST = RECEIVER. + Node* rcvr_node = Node::make_node(call->args()->front()); + this->assign(dst, rcvr_node); break; + } - Node* chan_node = this->gogo_->add_connection_node(chan_no); - Node* val_node = this->gogo_->add_connection_node(val_no); - chan_node->add_edge(val_node); - } - break; + // TODO(cmang): Handle case from issue 4529. + // Node* call_node = Node::make_node(e); + // Node::Escape_state* call_state = call_node->state(this->context_, NULL); + // std::vector<Node*> retvals = call_state->retvals; + // for (std::vector<Node*>::const_iterator p = retvals.begin(); + // p != retvals.end(); + // ++p) + // this->flows(dst, *p); + } + break; - case Statement::STATEMENT_EXPRESSION: - { - Expression* call = p->statement->expression_statement()->expr(); - if (call->call_result_expression() != NULL) - call = call->call_result_expression()->call(); - this->handle_call(var, call); - } - break; - - case Statement::STATEMENT_GO: - case Statement::STATEMENT_DEFER: - // Any variable referenced via a go or defer statement escapes to - // a different goroutine. - if (var_node->connection_node()->escape_state() > Node::ESCAPE_ARG) - var_node->connection_node()->set_escape_state(Node::ESCAPE_ARG); - this->handle_call(var, p->statement->thunk_statement()->call()); - break; - - case Statement::STATEMENT_IF: + case Expression::EXPRESSION_FUNC_REFERENCE: + if (e->func_expression()->closure() != NULL) { - // If this is a reference via an if statement, it is interesting - // if there is a function call in the condition. References in - // the then and else blocks would be discovered in an earlier - // case. - If_statement* if_stmt = p->statement->if_statement(); - Expression* cond = if_stmt->condition(); - if (cond->call_expression() != NULL) - this->handle_call(var, cond); - else if (cond->binary_expression() != NULL) - this->handle_binary(var, cond); + // If SRC is a reference to a function closure, DST flows into + // the underyling closure variable. + Expression* closure = e->func_expression()->closure(); + Node* closure_node = Node::make_node(closure); + this->flows(dst, closure_node); } - break; + break; - case Statement::STATEMENT_VARIABLE_DECLARATION: - { - // VAR could be referenced as the initialization for another - // variable, V e.g. V := call(VAR) or V := &T{field: VAR}. - Variable_declaration_statement* decl = - p->statement->variable_declaration_statement(); - Named_object* decl_no = decl->var(); - Variable* v = decl_no->var_value(); - - Expression* init = v->init(); - if (init == NULL) - break; + case Expression::EXPRESSION_FIELD_REFERENCE: + { + // A non-pointer can't escape from a struct. + if (!e->type()->has_pointer()) + break; + } + // Fall through. + + case Expression::EXPRESSION_CONVERSION: + case Expression::EXPRESSION_TYPE_GUARD: + case Expression::EXPRESSION_ARRAY_INDEX: + case Expression::EXPRESSION_STRING_INDEX: + { + Expression* left = NULL; + if (e->field_reference_expression() != NULL) + { + left = e->field_reference_expression()->expr(); + if (left->unary_expression() != NULL + && left->unary_expression()->op() == OPERATOR_MULT) + { + // DST = (*x).f + this->flows(dst, src); + break; + } + } + else if (e->conversion_expression() != NULL) + left = e->conversion_expression()->expr(); + else if (e->type_guard_expression() != NULL) + left = e->type_guard_expression()->expr(); + else if (e->array_index_expression() != NULL) + { + Array_index_expression* aie = e->array_index_expression(); + if (e->type()->is_slice_type()) + left = aie->array(); + else if (!aie->array()->type()->is_slice_type()) + { + // Indexing an array preserves the input value. + Node* array_node = Node::make_node(aie->array()); + this->assign(dst, array_node); + break; + } + else + { + this->flows(dst, src); + break; + } + } + else if (e->string_index_expression() != NULL) + { + String_index_expression* sie = e->string_index_expression(); + if (e->type()->is_slice_type()) + left = sie->string(); + else if (!sie->string()->type()->is_slice_type()) + { + // Indexing a string preserves the input value. + Node* string_node = Node::make_node(sie->string()); + this->assign(dst, string_node); + break; + } + else + { + this->flows(dst, src); + break; + } + } + go_assert(left != NULL); - if (init->is_composite_literal() - || init->heap_expression() != NULL) + // Conversions, field access, and slicing all preserve the input + // value. + Node* left_node = Node::make_node(left); + this->assign(dst, left_node); + } + break; + + case Expression::EXPRESSION_BINARY: + { + switch (e->binary_expression()->op()) + { + case OPERATOR_PLUS: + case OPERATOR_MINUS: + case OPERATOR_XOR: + case OPERATOR_MULT: + case OPERATOR_DIV: + case OPERATOR_MOD: + case OPERATOR_LSHIFT: + case OPERATOR_RSHIFT: + case OPERATOR_AND: + case OPERATOR_BITCLEAR: { - // Create edges between DECL_NO and each named object in the - // composite literal. - this->handle_composite_literal(decl_no, init); + Node* left = Node::make_node(e->binary_expression()->left()); + this->assign(dst, left); + Node* right = Node::make_node(e->binary_expression()->right()); + this->assign(dst, right); } + break; - if (init->call_result_expression() != NULL) - init = init->call_result_expression()->call(); - if (init->call_expression() != NULL) - this->handle_call(var, init); - else if (init->binary_expression() != NULL) - this->handle_binary(var, init); - } - break; - - case Statement::STATEMENT_TEMPORARY: - { - // A call to a function with mutliple results that references VAR - // will be lowered into a temporary at this point. Make sure the - // call that references VAR is handled. - Expression* init = p->statement->temporary_statement()->init(); - if (init == NULL) + default: break; - else if (init->call_result_expression() != NULL) + } + } + break; + + case Expression::EXPRESSION_UNARY: + { + switch (e->unary_expression()->op()) + { + case OPERATOR_PLUS: + case OPERATOR_MINUS: + case OPERATOR_XOR: { - Expression* call = init->call_result_expression()->call(); - this->handle_call(var, call); + Node* op_node = + Node::make_node(e->unary_expression()->operand()); + this->assign(dst, op_node); } - } + break; + + case OPERATOR_MULT: + // DST = *x + case OPERATOR_AND: + // DST = &x + this->flows(dst, src); + break; - default: - break; + default: + break; + } + } + break; + + case Expression::EXPRESSION_TEMPORARY_REFERENCE: + { + Statement* temp = e->temporary_reference_expression()->statement(); + if (temp != NULL + && temp->temporary_statement()->init() != NULL) + { + Expression* init = temp->temporary_statement()->init(); + Node* init_node = Node::make_node(init); + this->assign(dst, init_node); + } } + break; + + default: + // TODO(cmang): Add debug info here; this should not be reachable. + // For now, just to be conservative, we'll just say dst flows to src. + break; } } - return TRAVERSE_CONTINUE; } -// Traverse statements to find interesting references that might have not -// been recorded in the dataflow analysis. For example, many statements -// in closures are not properly recorded during dataflow analysis. This should -// handle all of the cases handled above in statements that reference a -// variable. FIXME. +// Model the assignment of DST to an indirection of SRC. -int -Build_connection_graphs::statement(Block*, size_t*, Statement* s) +void +Escape_analysis_assign::assign_deref(Node* dst, Node* src) { - switch(s->classification()) - { - case Statement::STATEMENT_ASSIGNMENT: + if (src->expr() != NULL) { - Assignment_statement* assn = s->assignment_statement(); - Named_object* lhs_no = this->resolve_var_reference(assn->lhs()); - - if (lhs_no == NULL) - break; - - Expression* rhs = assn->rhs(); - if (rhs->temporary_reference_expression() != NULL) - rhs = rhs->temporary_reference_expression()->statement()->init(); - if (rhs == NULL) - break; - - if (rhs->call_result_expression() != NULL) - rhs = rhs->call_result_expression()->call(); - if (rhs->call_expression() != NULL) - { - // It's not clear what variables we are trying to find references to - // so just use the arguments to this call. - Expression_list* args = rhs->call_expression()->args(); - if (args == NULL) - break; - - for (Expression_list::const_iterator p = args->begin(); - p != args->end(); - ++p) - { - Named_object* no = this->resolve_var_reference(*p); - if (no != NULL) { - Node* lhs_node = this->gogo_->add_connection_node(lhs_no); - Node* rhs_node = this->gogo_->add_connection_node(no); - lhs_node->add_edge(rhs_node); + switch (src->expr()->classification()) + { + case Expression::EXPRESSION_BOOLEAN: + case Expression::EXPRESSION_STRING: + case Expression::EXPRESSION_INTEGER: + case Expression::EXPRESSION_FLOAT: + case Expression::EXPRESSION_COMPLEX: + case Expression::EXPRESSION_NIL: + case Expression::EXPRESSION_IOTA: + // No need to try indirections on literal values + // or numeric constants. + return; + + case Expression::EXPRESSION_FIXED_ARRAY_CONSTRUCTION: + case Expression::EXPRESSION_SLICE_CONSTRUCTION: + case Expression::EXPRESSION_STRUCT_CONSTRUCTION: + { + // Dereferencing an array, slice, or struct is like accessing each + // of its values. In this situation, we model the flow from src to + // dst where src is one of the above as a flow from each of src's + // values to dst. + Expression* e = src->expr(); + Expression_list* vals = NULL; + if (e->slice_literal() != NULL) + vals = e->slice_literal()->vals(); + else if (e->array_literal() != NULL) + vals = e->array_literal()->vals(); + else + vals = e->struct_literal()->vals(); + + if (vals != NULL) + { + for (Expression_list::const_iterator p = vals->begin(); + p != vals->end(); + ++p) + { + if ((*p) != NULL) + this->assign(dst, Node::make_node(*p)); + } } - } + } + return; - this->handle_call(lhs_no, rhs); - } - else if (rhs->func_expression() != NULL) - { - Node* lhs_node = this->gogo_->add_connection_node(lhs_no); - Named_object* fn = rhs->func_expression()->named_object(); - Node* fn_node = this->gogo_->add_connection_node(fn); - lhs_node->add_edge(fn_node); - } - else - { - Named_object* rhs_no = this->resolve_var_reference(rhs); - if (rhs_no != NULL) - { - Node* lhs_node = this->gogo_->add_connection_node(lhs_no); - Node* rhs_node = this->gogo_->add_connection_node(rhs_no); - lhs_node->add_edge(rhs_node); - } - } + default: + break; + } } - break; - case Statement::STATEMENT_SEND: - { - Send_statement* send = s->send_statement(); - Named_object* chan_no = this->resolve_var_reference(send->channel()); - Named_object* val_no = this->resolve_var_reference(send->val()); + this->assign(dst, this->context_->add_dereference(src)); +} - if (chan_no == NULL || val_no == NULL) - break; +// Model the input-to-output assignment flow of one of a function call's +// arguments, where the flow is encoded in NOTE. - Node* chan_node = this->gogo_->add_connection_node(chan_no); - Node* val_node = this->gogo_->add_connection_node(val_no); - chan_node->add_edge(val_node); +int +Escape_analysis_assign::assign_from_note(std::string* note, + const std::vector<Node*>& dsts, + Node* src) +{ + int enc = Escape_note::parse_tag(note); + if (src->expr() != NULL) + { + switch (src->expr()->classification()) + { + case Expression::EXPRESSION_BOOLEAN: + case Expression::EXPRESSION_STRING: + case Expression::EXPRESSION_INTEGER: + case Expression::EXPRESSION_FLOAT: + case Expression::EXPRESSION_COMPLEX: + case Expression::EXPRESSION_NIL: + case Expression::EXPRESSION_IOTA: + // There probably isn't a note for a literal value. Literal values + // usually don't escape unless we lost track of the value some how. + return enc; + + default: + break; + } } - break; - case Statement::STATEMENT_EXPRESSION: + if (this->context_->gogo()->debug_escape_level() > 2) + go_inform(src->location(), "assignfromtag:: src= em=%s", + Escape_note::make_tag(enc).c_str()); + + if (enc == Node::ESCAPE_UNKNOWN) { - Expression* expr = s->expression_statement()->expr(); - if (expr->call_result_expression() != NULL) - expr = expr->call_result_expression()->call(); - if (expr->call_expression() != NULL) - { - // It's not clear what variables we are trying to find references to - // so just use the arguments to this call. - Expression_list* args = expr->call_expression()->args(); - if (args == NULL) - break; - - for (Expression_list::const_iterator p = args->begin(); - p != args->end(); - ++p) - { - Named_object* no = this->resolve_var_reference(*p); - if (no != NULL) - this->handle_call(no, expr); - } - } + // Lost track of the value. + this->assign(this->context_->sink(), src); + return enc; } - break; - - case Statement::STATEMENT_GO: - case Statement::STATEMENT_DEFER: + else if (enc == Node::ESCAPE_NONE) + return enc; + + // If the content inside a parameter (reached via indirection) escapes to + // the heap, mark it. + if ((enc & ESCAPE_CONTENT_ESCAPES) != 0) + this->assign(this->context_->sink(), this->context_->add_dereference(src)); + + int save_enc = enc; + enc >>= ESCAPE_RETURN_BITS; + for (std::vector<Node*>::const_iterator p = dsts.begin(); + enc != 0 && p != dsts.end(); + ++p) { - // Any variable referenced via a go or defer statement escapes to - // a different goroutine. - Expression* call = s->thunk_statement()->call(); - if (call->call_expression() != NULL) + // Prefer the lowest-level path to the reference (for escape purposes). + // Two-bit encoding (for example. 1, 3, and 4 bits are other options) + // 01 = 0-level + // 10 = 1-level, (content escapes), + // 11 = 2-level, (content of content escapes). + int bits = enc & ESCAPE_BITS_MASK_FOR_TAG; + if (bits > 0) { - // It's not clear what variables we are trying to find references to - // so just use the arguments to this call. - Expression_list* args = call->call_expression()->args(); - if (args == NULL) - break; - - for (Expression_list::const_iterator p = args->begin(); - p != args->end(); - ++p) + Node* n = src; + for (int i = 0; i < bits - 1; ++i) { - Named_object* no = this->resolve_var_reference(*p); - if (no != NULL) - this->handle_call(no, call); + // Encode level > 0 as indirections. + n = this->context_->add_dereference(n); } + this->assign(*p, n); } + enc >>= ESCAPE_BITS_PER_OUTPUT_IN_TAG; } - break; - case Statement::STATEMENT_VARIABLE_DECLARATION: - { - Variable_declaration_statement* decl = - s->variable_declaration_statement(); - Named_object* decl_no = decl->var(); - Variable* v = decl_no->var_value(); + // If there are too many outputs to fit in the tag, that is handled on the + // encoding end as ESCAPE_HEAP, so there is no need to check here. + return save_enc; +} - Expression* init = v->init(); - if (init == NULL) - break; +// Record the flow of SRC to DST in DST. - if (init->is_composite_literal() - || init->heap_expression() != NULL) - { - // Create edges between DECL_NO and each named object in the - // composite literal. - this->handle_composite_literal(decl_no, init); - } +void +Escape_analysis_assign::flows(Node* dst, Node* src) +{ + // Don't bother capturing the flow from scalars. + if (src->expr() != NULL + && !src->expr()->type()->has_pointer()) + return; - if (init->call_result_expression() != NULL) - init = init->call_result_expression()->call(); - if (init->call_expression() != NULL) - { - // It's not clear what variables we are trying to find references to - // so just use the arguments to this call. - Expression_list* args = init->call_expression()->args(); - if (args == NULL) - break; - - for (Expression_list::const_iterator p = args->begin(); - p != args->end(); - ++p) - { - Named_object* no = this->resolve_var_reference(*p); - if (no != NULL) - this->handle_call(no, init); - } - } - } - break; + // Don't confuse a blank identifier with the sink. + if (dst->is_sink() && dst != this->context_->sink()) + return; + + Node::Escape_state* dst_state = dst->state(this->context_, NULL); + Node::Escape_state* src_state = src->state(this->context_, NULL); + if (dst == src + || dst_state == src_state + || dst_state->flows.find(src) != dst_state->flows.end() + || src_state->flows.find(dst) != src_state->flows.end()) + return; - default: - break; - } + Gogo* gogo = this->context_->gogo(); + if (gogo->debug_escape_level() > 2) + go_inform(Linemap::unknown_location(), "flows:: %s <- %s", + dst->ast_format(gogo).c_str(), src->ast_format(gogo).c_str()); - return TRAVERSE_CONTINUE; + if (dst_state->flows.empty()) + this->context_->add_dst(dst); + + dst_state->flows.insert(src); } -// Build the connection graphs for each function present in the call graph. +// Build a connectivity graph between nodes in the function being analyzed. void -Gogo::build_connection_graphs() +Gogo::assign_connectivity(Escape_context* context, Named_object* fn) { - Build_connection_graphs build_conns(this); + // Must be defined outside of this package. + if (!fn->is_function()) + return; + + int save_depth = context->loop_depth(); + context->set_loop_depth(1); + + Escape_analysis_assign ea(context, fn); + Function::Results* res = fn->func_value()->result_variables(); + if (res != NULL) + { + for (Function::Results::const_iterator p = res->begin(); + p != res->end(); + ++p) + { + Node* res_node = Node::make_node(*p); + Node::Escape_state* res_state = res_node->state(context, fn); + res_state->loop_depth = 0; + + // If this set of functions is recursive, we lose track of the return values. + // Just say that the result flows to the sink. + if (context->recursive()) + ea.flows(context->sink(), res_node); + } + } + + const Bindings* callee_bindings = fn->func_value()->block()->bindings(); + Function_type* fntype = fn->func_value()->type(); + Typed_identifier_list* params = (fntype->parameters() != NULL + ? fntype->parameters()->copy() + : new Typed_identifier_list); + if (fntype->receiver() != NULL) + params->push_back(*fntype->receiver()); - for (std::set<Node*>::const_iterator p = this->call_graph_.begin(); - p != this->call_graph_.end(); + for (Typed_identifier_list::const_iterator p = params->begin(); + p != params->end(); ++p) { - Named_object* func = (*p)->object(); - - go_assert(func->is_function() || func->is_function_declaration()); - Function_type* fntype; - if (func->is_function()) - fntype = func->func_value()->type(); - else - fntype = func->func_declaration_value()->type(); - if (fntype->is_builtin()) + if (p->name().empty() || Gogo::is_sink_name(p->name())) continue; - this->add_connection_node(func); - build_conns.set_current_function(func); - if (func->is_function()) - { - // A pointer receiver of a method always escapes from the method. - if (fntype->is_method() && - fntype->receiver()->type()->points_to() != NULL) - { - const Bindings* callee_bindings = - func->func_value()->block()->bindings(); - std::string rcvr_name = fntype->receiver()->name(); - if (Gogo::is_sink_name(rcvr_name) || rcvr_name.empty()) - return; + Named_object* param_no = callee_bindings->lookup_local(p->name()); + go_assert(param_no != NULL); + Node* param_node = Node::make_node(param_no); + Node::Escape_state* param_state = param_node->state(context, fn); + param_state->loop_depth = 1; - Named_object* rcvr_no = callee_bindings->lookup_local(rcvr_name); - Node* rcvr_node = this->add_connection_node(rcvr_no); - rcvr_node->connection_node()->set_escape_state(Node::ESCAPE_ARG); - } - func->func_value()->traverse(&build_conns); - } + if (!p->type()->has_pointer()) + continue; + + // External function? Parameters must escape unless //go:noescape is set. + // TODO(cmang): Implement //go:noescape directive. + if (fn->package() != NULL) + param_node->set_encoding(Node::ESCAPE_HEAP); + else + param_node->set_encoding(Node::ESCAPE_NONE); + + // TODO(cmang): Track this node in no_escape list. } + + Escape_analysis_loop el; + fn->func_value()->traverse(&el); + + fn->func_value()->traverse(&ea); + context->set_loop_depth(save_depth); } -void -Gogo::analyze_reachability() +class Escape_analysis_flood { - std::list<Node*> worklist; + public: + Escape_analysis_flood(Escape_context* context) + : context_(context) + { } - // Run reachability analysis on all globally escaping objects. - for (std::set<Node*>::const_iterator p = this->global_connections_.begin(); - p != this->global_connections_.end(); - ++p) - worklist.push_back(*p); + // Use the escape information in dst to update the escape information in src + // and src's upstream. + void + flood(Level, Node* dst, Node* src, int); + + private: + // The escape context for the group of functions being flooded. + Escape_context* context_; +}; + +// Whenever we hit a dereference node, the level goes up by one, and whenever +// we hit an address-of, the level goes down by one. as long as we're on a +// level > 0 finding an address-of just means we're following the upstream +// of a dereference, so this address doesn't leak (yet). +// If level == 0, it means the /value/ of this node can reach the root of this +// flood so if this node is an address-of, its argument should be marked as +// escaping iff its current function and loop depth are different from the +// flood's root. +// Once an object has been moved to the heap, all of its upstream should be +// considered escaping to the global scope. +// This is an implementation of gc/esc.go:escwalkBody. - while (!worklist.empty()) +void +Escape_analysis_flood::flood(Level level, Node* dst, Node* src, + int extra_loop_depth) +{ + // No need to flood src if it is a literal. + if (src->expr() != NULL) { - Node* m = worklist.front(); - worklist.pop_front(); + switch (src->expr()->classification()) + { + case Expression::EXPRESSION_BOOLEAN: + case Expression::EXPRESSION_STRING: + case Expression::EXPRESSION_INTEGER: + case Expression::EXPRESSION_FLOAT: + case Expression::EXPRESSION_COMPLEX: + case Expression::EXPRESSION_NIL: + case Expression::EXPRESSION_IOTA: + return; + + default: + break; + } + } - std::set<Node*> reachable = m->edges(); - if (m->object()->is_function() - && m->object()->func_value()->needs_closure()) + Node::Escape_state* src_state = src->state(this->context_, NULL); + if (src_state->flood_id == this->context_->flood_id()) + { + // Esclevels are vectors, do not compare as integers, + // and must use "min" of old and new to guarantee + // convergence. + level = level.min(src_state->level); + if (level == src_state->level) { - // If a closure escapes everything it closes over also escapes. - Function* closure = m->object()->func_value(); - for (size_t i = 0; i < closure->closure_field_count(); i++) - { - Named_object* enclosed = closure->enclosing_var(i); - Node* enclosed_node = this->lookup_connection_node(enclosed); - go_assert(enclosed_node != NULL); - reachable.insert(enclosed_node); - } + // Have we been here already with an extraloopdepth, + // or is the extraloopdepth provided no improvement on + // what's already been seen? + if (src_state->max_extra_loop_depth >= extra_loop_depth + || src_state->loop_depth >= extra_loop_depth) + return; + src_state->max_extra_loop_depth = extra_loop_depth; } - for (std::set<Node*>::iterator n = reachable.begin(); - n != reachable.end(); - ++n) + } + else + src_state->max_extra_loop_depth = -1; + + src_state->flood_id = this->context_->flood_id(); + src_state->level = level; + int mod_loop_depth = std::max(extra_loop_depth, src_state->loop_depth); + + Gogo* gogo = this->context_->gogo(); + int debug_level = gogo->debug_escape_level(); + if (debug_level > 1) + go_inform(Linemap::unknown_location(), + "escwalk: level:{%d %d} depth:%d " + "op=%s %s(%s) " + "scope:%s[%d] " + "extraloopdepth=%d", + level.value(), level.suffix_value(), this->context_->pdepth(), + src->op_format().c_str(), + src->ast_format(gogo).c_str(), + src->details().c_str(), + debug_function_name(src_state->fn).c_str(), + src_state->loop_depth, + extra_loop_depth); + + this->context_->increase_pdepth(); + + // Input parameter flowing into output parameter? + Named_object* src_no = NULL; + if (src->expr() != NULL && src->expr()->var_expression() != NULL) + src_no = src->expr()->var_expression()->named_object(); + else + src_no = src->object(); + bool src_is_param = (src_no != NULL + && src_no->is_variable() + && src_no->var_value()->is_parameter()); + + Named_object* dst_no = NULL; + if (dst->expr() != NULL && dst->expr()->var_expression() != NULL) + dst_no = dst->expr()->var_expression()->named_object(); + else + dst_no = dst->object(); + bool dst_is_result = dst_no != NULL && dst_no->is_result_variable(); + + if (src_is_param + && dst_is_result + && (src->encoding() & ESCAPE_MASK) < int(Node::ESCAPE_SCOPE) + && dst->encoding() != Node::ESCAPE_HEAP) + { + // This case handles: + // 1. return in + // 2. return &in + // 3. tmp := in; return &tmp + // 4. return *in + if (debug_level != 0) { - // If an object can be reached from a node with ESCAPE_GLOBAL, - // it also must ESCAPE_GLOBAL. - if ((*n)->connection_node()->escape_state() != Node::ESCAPE_GLOBAL) - { - (*n)->connection_node()->set_escape_state(Node::ESCAPE_GLOBAL); - worklist.push_back(*n); - } + if (debug_level == 1) + go_inform(src->location(), + "leaking param: %s to result %s level=%d", + src->ast_format(gogo).c_str(), + dst->ast_format(gogo).c_str(), + level.value()); + else + go_inform(src->location(), + "leaking param: %s to result %s level={%d %d}", + src->ast_format(gogo).c_str(), + dst->ast_format(gogo).c_str(), + level.value(), level.suffix_value()); + } + + if ((src->encoding() & ESCAPE_MASK) != Node::ESCAPE_RETURN) + { + int enc = + Node::ESCAPE_RETURN | (src->encoding() & ESCAPE_CONTENT_ESCAPES); + src->set_encoding(enc); } + + int enc = Node::note_inout_flows(src->encoding(), + dst_no->result_var_value()->index(), + level); + src->set_encoding(enc); + + // In gc/esc.go:escwalkBody, this is a goto to the label for recursively + // flooding the connection graph. Inlined here for convenience. + level = level.copy(); + for (std::set<Node*>::const_iterator p = src_state->flows.begin(); + p != src_state->flows.end(); + ++p) + this->flood(level, dst, *p, extra_loop_depth); + return; } - // Run reachability analysis on all objects that escape via arguments. - for (Named_escape_nodes::const_iterator p = - this->named_connection_nodes_.begin(); - p != this->named_connection_nodes_.end(); - ++p) + // If parameter content escape to heap, set ESCAPE_CONTENT_ESCAPES. + // Note minor confusion around escape from pointer-to-struct vs + // escape from struct. + if (src_is_param + && dst->encoding() == Node::ESCAPE_HEAP + && (src->encoding() & ESCAPE_MASK) < int(Node::ESCAPE_SCOPE) + && level.value() > 0) { - if (p->second->connection_node()->escape_state() < Node::ESCAPE_NONE) - worklist.push_back(p->second); + int enc = + Node::max_encoding((src->encoding() | ESCAPE_CONTENT_ESCAPES), + Node::ESCAPE_NONE); + src->set_encoding(enc); + if (debug_level != 0) + go_inform(src->location(), "mark escaped content: %s", + src->ast_format(gogo).c_str()); } - while (!worklist.empty()) + // A src object leaks if its value or address is assigned to a dst object + // in a different scope (at a different loop depth). + Node::Escape_state* dst_state = dst->state(this->context_, NULL); + bool src_leaks = (level.value() <= 0 + && level.suffix_value() <= 0 + && dst_state->loop_depth < mod_loop_depth); + + if (src_is_param + && (src_leaks || dst_state->loop_depth < 0) + && (src->encoding() & ESCAPE_MASK) < int(Node::ESCAPE_SCOPE)) { - Node* m = worklist.front(); - worklist.pop_front(); + if (level.suffix_value() > 0) + { + int enc = + Node::max_encoding((src->encoding() | ESCAPE_CONTENT_ESCAPES), + Node::ESCAPE_NONE); + src->set_encoding(enc); + if (debug_level != 0) + go_inform(src->location(), "leaking param content: %s", + src->ast_format(gogo).c_str()); + } + else + { + if (debug_level != 0) + go_inform(src->location(), "leaking param"); + src->set_encoding(Node::ESCAPE_SCOPE); + } + } + else if (src->expr() != NULL) + { + Expression* e = src->expr(); + if (e->enclosed_var_expression() != NULL) + { + if (src_leaks && debug_level != 0) + go_inform(src->location(), "leaking closure reference %s", + src->ast_format(gogo).c_str()); - std::set<Node*> reachable = m->edges(); - if (m->object()->is_function() - && m->object()->func_value()->needs_closure()) + Node* enclosed_node = + Node::make_node(e->enclosed_var_expression()->variable()); + this->flood(level, dst, enclosed_node, -1); + } + else if (e->heap_expression() != NULL + || (e->unary_expression() != NULL + && e->unary_expression()->op() == OPERATOR_AND)) { - // If a closure escapes everything it closes over also escapes. - Function* closure = m->object()->func_value(); - for (size_t i = 0; i < closure->closure_field_count(); i++) + // Pointer literals and address-of expressions. + Expression* underlying; + if (e->heap_expression()) + underlying = e->heap_expression()->expr(); + else + underlying = e->unary_expression()->operand(); + Node* underlying_node = Node::make_node(underlying); + + // If the address leaks, the underyling object must be moved + // to the heap. + underlying->address_taken(src_leaks); + if (src_leaks) + { + src->set_encoding(Node::ESCAPE_HEAP); + if (debug_level != 0) + { + go_inform(underlying->location(), "moved to heap: %s", + underlying_node->ast_format(gogo).c_str()); + + if (debug_level > 1) + go_inform(src->location(), + "%s escapes to heap, level={%d %d}, " + "dst.eld=%d, src.eld=%d", + src->ast_format(gogo).c_str(), level.value(), + level.suffix_value(), dst_state->loop_depth, + mod_loop_depth); + else + go_inform(src->location(), "%s escapes to heap", + src->ast_format(gogo).c_str()); + } + + this->flood(level.decrease(), dst, + underlying_node, mod_loop_depth); + extra_loop_depth = mod_loop_depth; + } + else { - Named_object* enclosed = closure->enclosing_var(i); - Node* enclosed_node = this->lookup_connection_node(enclosed); - go_assert(enclosed_node != NULL); - reachable.insert(enclosed_node); + // Decrease the level each time we take the address of the object. + this->flood(level.decrease(), dst, underlying_node, -1); } } - for (std::set<Node*>::iterator n = reachable.begin(); - n != reachable.end(); - ++n) + else if (e->slice_literal() != NULL) { - // If an object can be reached from a node with ESCAPE_ARG, - // it is ESCAPE_ARG or ESCAPE_GLOBAL. - Node::Escapement_lattice e = m->connection_node()->escape_state(); - if ((*n)->connection_node()->escape_state() > e) + Slice_construction_expression* slice = e->slice_literal(); + if (slice->vals() != NULL) { - (*n)->connection_node()->set_escape_state(e); - worklist.push_back(*n); + for (Expression_list::const_iterator p = slice->vals()->begin(); + p != slice->vals()->end(); + ++p) + { + if ((*p) != NULL) + this->flood(level.decrease(), dst, Node::make_node(*p), -1); + } + } + if (src_leaks) + { + src->set_encoding(Node::ESCAPE_HEAP); + if (debug_level != 0) + go_inform(src->location(), "%s escapes to heap", + src->ast_format(gogo).c_str()); + extra_loop_depth = mod_loop_depth; } } - } -} - -// Iterate over all functions analyzed in the analysis, recording escape -// information for each receiver and parameter. - -void -Gogo::mark_escaping_signatures() -{ - for (std::set<Node*>::const_iterator p = this->call_graph_.begin(); - p != this->call_graph_.end(); - ++p) - { - Named_object* fn = (*p)->object(); - if (!fn->is_function()) - continue; - - Function* func = fn->func_value(); - Function_type* fntype = func->type(); - const Bindings* bindings = func->block()->bindings(); - - // If this is a method, set the escape state of the receiver. - if (fntype->is_method()) + else if (e->call_expression() != NULL) { - std::string rcvr_name = fntype->receiver()->name(); - if (rcvr_name.empty() || Gogo::is_sink_name(rcvr_name)) - fntype->set_receiver_escape_state(Node::ESCAPE_NONE); - else + Call_expression* call = e->call_expression(); + if (call->fn()->func_expression() != NULL) { - Named_object* rcvr_no = bindings->lookup_local(rcvr_name); - go_assert(rcvr_no != NULL); + Func_expression* func = call->fn()->func_expression(); + if (func->is_runtime_function()) + { + switch (func->runtime_code()) + { + case Runtime::GROWSLICE: + { + // Propagate escape information to appendee. + Expression* appendee = call->args()->front(); + this->flood(level, dst, Node::make_node(appendee), -1); + } + break; + + case Runtime::MAKECHAN: + case Runtime::MAKEMAP: + case Runtime::MAKESLICE: + case Runtime::MAKESLICE64: + case Runtime::SLICEBYTETOSTRING: + case Runtime::SLICERUNETOSTRING: + case Runtime::STRINGTOSLICEBYTE: + case Runtime::STRINGTOSLICERUNE: + case Runtime::CONCATSTRINGS: + case Runtime::CONCATSTRING2: + case Runtime::CONCATSTRING3: + case Runtime::CONCATSTRING4: + case Runtime::CONCATSTRING5: + case Runtime::CONSTRUCT_MAP: + case Runtime::INTSTRING: + case Runtime::REQUIREITAB: + // All runtime calls that involve allocation of memory + // except new. Runtime::NEW gets lowered into an + // allocation expression. + if (src_leaks) + { + src->set_encoding(Node::ESCAPE_HEAP); + if (debug_level != 0) + go_inform(src->location(), "%s escapes to heap", + src->ast_format(gogo).c_str()); + extra_loop_depth = mod_loop_depth; + } + break; - Node* rcvr_node = this->lookup_connection_node(rcvr_no); - if (rcvr_node != NULL) + default: + break; + } + } + else if (src_leaks + && (func->closure() != NULL + || func->bound_method_expression() != NULL)) { - Node::Escapement_lattice e = - rcvr_node->connection_node()->escape_state(); - fntype->set_receiver_escape_state(e); + // A closure or bound method; we lost track of actual function + // so if this leaks, this call must be done on the heap. + src->set_encoding(Node::ESCAPE_HEAP); + if (debug_level != 0) + go_inform(src->location(), "%s escapes to heap", + src->ast_format(gogo).c_str()); } - else - fntype->set_receiver_escape_state(Node::ESCAPE_NONE); } - fntype->set_has_escape_info(); } - - const Typed_identifier_list* params = fntype->parameters(); - if (params == NULL) - continue; - - fntype->set_has_escape_info(); - Node::Escape_states* param_escape_states = new Node::Escape_states; - for (Typed_identifier_list::const_iterator p1 = params->begin(); - p1 != params->end(); - ++p1) + else if (e->allocation_expression() != NULL && src_leaks) { - std::string param_name = p1->name(); - if (param_name.empty() || Gogo::is_sink_name(param_name)) - param_escape_states->push_back(Node::ESCAPE_NONE); + // Calls to Runtime::NEW get lowered into an allocation expression. + src->set_encoding(Node::ESCAPE_HEAP); + if (debug_level != 0) + go_inform(src->location(), "%s escapes to heap", + src->ast_format(gogo).c_str()); + } + else if ((e->field_reference_expression() != NULL + && e->field_reference_expression()->expr()->unary_expression() == NULL) + || e->type_guard_expression() != NULL + || (e->array_index_expression() != NULL + && e->type()->is_slice_type()) + || (e->string_index_expression() != NULL + && e->type()->is_slice_type())) + { + Expression* underlying; + if (e->field_reference_expression() != NULL) + underlying = e->field_reference_expression()->expr(); + else if (e->type_guard_expression() != NULL) + underlying = e->type_guard_expression()->expr(); + else if (e->array_index_expression() != NULL) + underlying = e->array_index_expression()->array(); else - { - Named_object* param_no = bindings->lookup_local(param_name); - go_assert(param_no != NULL); + underlying = e->string_index_expression()->string(); - Node* param_node = this->lookup_connection_node(param_no); - if (param_node == NULL) + Node* underlying_node = Node::make_node(underlying); + this->flood(level, dst, underlying_node, -1); + } + else if ((e->field_reference_expression() != NULL + && e->field_reference_expression()->expr()->unary_expression() != NULL) + || e->array_index_expression() != NULL + || e->map_index_expression() != NULL + || (e->unary_expression() != NULL + && e->unary_expression()->op() == OPERATOR_MULT)) + { + Expression* underlying; + if (e->field_reference_expression() != NULL) + { + underlying = e->field_reference_expression()->expr(); + underlying = underlying->unary_expression()->operand(); + } + else if (e->array_index_expression() != NULL) + { + underlying = e->array_index_expression()->array(); + if (!underlying->type()->is_slice_type()) { - param_escape_states->push_back(Node::ESCAPE_NONE); - continue; + Node* underlying_node = Node::make_node(underlying); + this->flood(level, dst, underlying_node, 1); } - - Node::Escapement_lattice e = - param_node->connection_node()->escape_state(); - param_escape_states->push_back(e); } + else if (e->map_index_expression() != NULL) + underlying = e->map_index_expression()->map(); + else + underlying = e->unary_expression()->operand(); + + // Increase the level for a dereference. + Node* underlying_node = Node::make_node(underlying); + this->flood(level.increase(), dst, underlying_node, -1); } - go_assert(params->size() == param_escape_states->size()); - fntype->set_parameter_escape_states(param_escape_states); + + // TODO(cmang): Add case for Issue #10466. + } + + level = level.copy(); + for (std::set<Node*>::const_iterator p = src_state->flows.begin(); + p != src_state->flows.end(); + ++p) + this->flood(level, dst, *p, extra_loop_depth); + + this->context_->decrease_pdepth(); +} + +// Propagate escape information across the nodes modeled in this Analysis_set. +// This is an implementation of gc/esc.go:escflood. + +void +Gogo::propagate_escape(Escape_context* context, Node* dst) +{ + Node::Escape_state* state = dst->state(context, NULL); + Gogo* gogo = context->gogo(); + if (gogo->debug_escape_level() > 1) + go_inform(Linemap::unknown_location(), "escflood:%d: dst %s scope:%s[%d]", + context->flood_id(), dst->ast_format(gogo).c_str(), + debug_function_name(state->fn).c_str(), + state->loop_depth); + + Escape_analysis_flood eaf(context); + for (std::set<Node*>::const_iterator p = state->flows.begin(); + p != state->flows.end(); + ++p) + { + context->increase_flood_id(); + eaf.flood(Level::From(0), dst, *p, -1); } } -class Optimize_allocations : public Traverse +class Escape_analysis_tag { public: - Optimize_allocations(Gogo* gogo) - : Traverse(traverse_variables), - gogo_(gogo) + Escape_analysis_tag(Escape_context* context) + : context_(context) { } - int - variable(Named_object*); + // Add notes to the function's type about the escape information of its + // input parameters. + void + tag(Named_object* fn); private: - // The IR. - Gogo* gogo_; + Escape_context* context_; }; -// The -fgo-optimize-alloc flag activates this escape analysis. - -Go_optimize optimize_allocation_flag("allocs"); - -// Propagate escape information for each variable. - -int -Optimize_allocations::variable(Named_object* var) +void +Escape_analysis_tag::tag(Named_object* fn) { - Node* var_node = this->gogo_->lookup_connection_node(var); - if (var_node == NULL - || var_node->connection_node()->escape_state() != Node::ESCAPE_NONE) - return TRAVERSE_CONTINUE; + // External functions are assumed unsafe + // unless //go:noescape is given before the declaration. + if (fn->package() != NULL || !fn->is_function()) + { + // TODO(cmang): Implement //go:noescape directive for external functions; + // mark input parameters as not escaping. + return; + } + + Function_type* fntype = fn->func_value()->type(); + Bindings* bindings = fn->func_value()->block()->bindings(); - if (var->is_variable()) + if (fntype->is_method() + && !fntype->receiver()->name().empty() + && !Gogo::is_sink_name(fntype->receiver()->name())) { - var->var_value()->set_does_not_escape(); - if (var->var_value()->init() != NULL - && var->var_value()->init()->allocation_expression() != NULL) + Named_object* rcvr_no = bindings->lookup(fntype->receiver()->name()); + go_assert(rcvr_no != NULL); + Node* rcvr_node = Node::make_node(rcvr_no); + switch ((rcvr_node->encoding() & ESCAPE_MASK)) { - Allocation_expression* alloc = - var->var_value()->init()->allocation_expression(); - alloc->set_allocate_on_stack(); + case Node::ESCAPE_NONE: // not touched by flood + case Node::ESCAPE_RETURN: + if (fntype->receiver()->type()->has_pointer()) + // Don't bother tagging for scalars. + fntype->add_receiver_note(rcvr_node->encoding()); + break; + + case Node::ESCAPE_HEAP: // flooded, moved to heap. + case Node::ESCAPE_SCOPE: // flooded, value leaves scope. + break; + + default: + break; } } - return TRAVERSE_CONTINUE; + int i = 0; + if (fntype->parameters() != NULL) + { + const Typed_identifier_list* til = fntype->parameters(); + for (Typed_identifier_list::const_iterator p = til->begin(); + p != til->end(); + ++p, ++i) + { + if (p->name().empty() || Gogo::is_sink_name(p->name())) + continue; + + Named_object* param_no = bindings->lookup(p->name()); + go_assert(param_no != NULL); + Node* param_node = Node::make_node(param_no); + switch ((param_node->encoding() & ESCAPE_MASK)) + { + case Node::ESCAPE_NONE: // not touched by flood + case Node::ESCAPE_RETURN: + if (p->type()->has_pointer()) + // Don't bother tagging for scalars. + fntype->add_parameter_note(i, param_node->encoding()); + break; + + case Node::ESCAPE_HEAP: // flooded, moved to heap. + case Node::ESCAPE_SCOPE: // flooded, value leaves scope. + break; + + default: + break; + } + } + } + fntype->set_is_tagged(); } -// Perform escape analysis on this program and optimize allocations using -// the derived information if -fgo-optimize-allocs. +// Tag each top-level function with escape information that will be used to +// retain analysis results across imports. void -Gogo::optimize_allocations(const char** filenames) +Gogo::tag_function(Escape_context* context, Named_object* fn) { - if (!::optimize_allocation_flag.is_enabled() || saw_errors()) - return; - - // Build call graph for this program. - this->build_call_graph(); - - // Dump the call graph for this program if -fgo-dump-calls is enabled. - this->dump_call_graph(filenames[0]); - - // Build the connection graphs for this program. - this->build_connection_graphs(); - - // Dump the connection graphs if -fgo-dump-connections is enabled. - this->dump_connection_graphs(filenames[0]); - - // Given the connection graphs for this program, perform a reachability - // analysis to determine what objects escape. - this->analyze_reachability(); - - // Propagate escape information to variables and variable initializations. - Optimize_allocations optimize_allocs(this); - this->traverse(&optimize_allocs); - - // Store escape information for a function's receivers and parameters in the - // function's signature for use when exporting package information. - this->mark_escaping_signatures(); + Escape_analysis_tag eat(context); + eat.tag(fn); } diff --git a/gcc/go/gofrontend/escape.h b/gcc/go/gofrontend/escape.h index 42c79f61fa..e6d1a3d6e1 100644 --- a/gcc/go/gofrontend/escape.h +++ b/gcc/go/gofrontend/escape.h @@ -1,310 +1,464 @@ -// escape.h -- Go frontend escape analysis. -*- C++ -*- +// escape.h -- Go escape analysis (based on Go compiler algorithm). -// Copyright 2015 The Go Authors. All rights reserved. +// Copyright 2016 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. #ifndef GO_ESCAPE_H #define GO_ESCAPE_H -#include "go-system.h" -#include "string-dump.h" +#include "gogo.h" -class Call_node; -class Connection_node; -class Connection_dump_context; -class Gogo; class Named_object; +class Expression; +class Statement; +class Escape_context; + +// There can be loops in the escape graph that lead to arbitrary recursions. +// See comment in gc/esc.go. +static const int MIN_LEVEL = -2; + +// Level models the escapement of a Node using two integers that are computed +// by backwards-analyzing the flow of a function from its sink and increasing or +// decreasing based on dereferences and addressing, respectively. +// One integer, known as the level's VALUE (think absolute value), is just the +// sum of indirections (via referencing or dereferencing) applied to the Node. +// The second, known as the level's SUFFIX_VALUE, is the amount of indirections +// applied after some data has been copied from the node. When accessing a +// field F of an object O and then applying indirections, for example, the field +// access O.F is assumed to copy that data from O before applying indirections. +// With this, even if O.F escapes, it might mean that the content of O escape, +// but not the object O itself. + +class Level +{ +public: + Level() + : value_(0), suffix_value_(0) + { } + + Level(int value, int suffix) + : value_(value), suffix_value_(suffix) + { } + + // Return this level's value. + int + value() const + { return this->value_; } + + // Return this level's suffix value. + int + suffix_value() const + { return this->suffix_value_; } + + // Increase the level because a node is referenced. + Level + increase() const + { + if (this->value_ <= MIN_LEVEL) + return Level(MIN_LEVEL, 0); + + return Level(this->value_ + 1, this->suffix_value_ + 1); + } + + // Decrease the level because a node is dereferenced. + Level + decrease() const + { + if (this->value_ <= MIN_LEVEL) + return Level(MIN_LEVEL, 0); -// A basic escape analysis implementation for the Go frontend based on the -// algorithm from "Escape Analysis for Java" by Choi et. al in OOPSLA '99. -// This is a simplified version of the flow insensitive analysis with the goal -// of reducing the overhead cost of garbage collection by allocating objects -// on the stack when they do not escape the function they were created in. -// -// A major simplification is that the analysis only implements what Choi refers -// to as "deferred edges" which are used to used model assignments that copy -// references from one variable to another e.g. a := b. It is unnecessary to -// consider assignments to the fields of an object because, in general, if a -// field of an object escapes and must be heap-allocated, there is no way to -// heap-allocate that escaping field without heap-allocating the entire object. -// That is, for some globally escaping object GVAR, if there is an assignment -// of the form GVAR = t.f such that field f of object t escapes, it is likely -// that t must be heap-allocated as well. In the analysis, this assignment -// will be simplified to GVAR = t, which is imprecise but has the same effect. - -// This is a general graph node representing a named object used in a call graph -// or connection graph. In a call graph, each named object is either a Function -// or Function_declaration representing a function called during the program -// execution (which isn't necessarily every function declared). In a connection -// graph, there is a node for each node in the call graph, which forms the root -// of that connection graph. Each connection graph root contains nodes whose -// objects are either variables used in the function defintion or are nested -// closures created within the function definition. The connection graph is -// a way of modeling the connectivity between all objects created in a given -// function as well as understanding the relationship between input arguments -// in the caller and the formal parameters in the callee. + return Level(this->value_ - 1, this->suffix_value_ - 1); + } + + // Model a node being copied. + Level + copy() const + { + return Level(this->value_, std::max(this->suffix_value_, 0)); + } + + // Return a level with the minimum values of this level and l. + Level + min(const Level& l) const + { + return Level(std::min(this->value_, l.value()), + std::min(this->suffix_value_, l.suffix_value())); + } + + // Compare two levels for equality. + bool + operator==(const Level& l) const + { + return (this->value_ == l.value() + && this->suffix_value_ == l.suffix_value()); + } + + // Create a level from an integer value. + static Level + From(int i) + { + if (i <= MIN_LEVEL) + return Level(MIN_LEVEL, 0); + return Level(i, 0); + } + +private: + // The sum of all indirects (-1) and references (+1) applied to a Node. + int value_; + // The sum of all indirects (-1) abd references (+1) applied to a copied Node. + int suffix_value_; +}; + +// A node in the escape graph. This node is an alias to a particular node +// in the Go parse tree. Specifically, it can represent an expression node, +// a statement node, or a named object node (a variable or function). class Node { public: + // This classification represents type of nodes in the Go parse tree that are + // interesting during the analysis. enum Node_classification + { + NODE_OBJECT, + NODE_EXPRESSION, + NODE_STATEMENT + }; + + // The state necessary to keep track of how a node escapes. + struct Escape_state { - NODE_CALL, - NODE_CONNECTION + // The current function. + Named_object* fn; + // A list of source nodes that flow into this node. + std::set<Node*> flows; + // If the node is a function call, the list of nodes returned. + std::vector<Node*> retvals; + // The node's loop depth. + int loop_depth; + // There is an extra loop depth in the flood phase used to account for + // variables referenced across closures. This is the maximum value of the + // extra loop depth seen during the flood that touches this node. + int max_extra_loop_depth; + // The node's level. + Level level; + // An ID given to a node when it is encountered as a flow from the current + // dst node. This is used to avoid infinite recursion of cyclic nodes. + int flood_id; + + Escape_state() + : fn(NULL), loop_depth(0), max_extra_loop_depth(0), flood_id(0) + { } }; - virtual ~Node(); - - // Make a call node for FUNCTION. - static Node* - make_call(Named_object* function); - - // Make a connection node for OBJECT. // Note: values in this enum appear in export data, and therefore MUST NOT // change. - enum Escapement_lattice + enum Escapement_encoding { - // ESCAPE_GLOBAL means that the object escapes all functions globally. - ESCAPE_GLOBAL = 0, - // ESCAPE_ARG with respect to a function means that the object escapes that - // function it is created in via the function's arguments or results. - ESCAPE_ARG = 1, - // ESCAPE_NONE means that the object does not escape the function in which - // it was created. - ESCAPE_NONE = 2 + ESCAPE_UNKNOWN, + // Does not escape to heap, result, or parameters. + ESCAPE_NONE, + // Is returned or reachable from a return statement. + ESCAPE_RETURN, + // Allocated in an inner loop, assigned to an outer loop, + // which allows construction of non-escaping but arbitrarily large linked + // data structures (i.e., not eligible for allocation in a fixed-size stack + // stack frame). + ESCAPE_SCOPE, + // Reachable from the heap. + ESCAPE_HEAP, + // By construction will not escape. + ESCAPE_NEVER }; - // A list of states usually corresponding to a list of function parameters. - typedef std::vector<Escapement_lattice> Escape_states; + // Multiple constructors for each classification. + Node(Named_object* no) + : classification_(NODE_OBJECT), state_(NULL), encoding_(ESCAPE_UNKNOWN) + { this->u_.object_val = no; } - static Node* - make_connection(Named_object* object, Escapement_lattice e); + Node(Expression* e) + : classification_(NODE_EXPRESSION), state_(NULL), encoding_(ESCAPE_UNKNOWN) + { this->u_.expression_val = e; } - // Return the node classification. - Node_classification - classification() const - { return this->classification_; } + Node(Statement* s) + : classification_(NODE_STATEMENT), state_(NULL), encoding_(ESCAPE_UNKNOWN) + { this->u_.statement_val = s; } - // Return whether this is a call node. - bool - is_call() const - { return this->classification_ == NODE_CALL; } + // Return this node's type. + Type* + type() const; - // Return whether this is a connection node. - bool - is_connection() const - { return this->classification_ == NODE_CONNECTION; } + // Return this node's location. + Location + location() const; - // If this is a connection node, return the Connection_node. - // Otherwise, return NULL. - Connection_node* - connection_node() - { return this->convert<Connection_node, NODE_CONNECTION>(); } + // Return this node's AST formatted string. + std::string + ast_format(Gogo*) const; - // Return this node's unique id. - unsigned int - id() const - { return this->id_; } + // Return this node's detailed format string. + std::string + details() const; - // Return this node's generated name for GraphViz. - virtual const std::string& - name() = 0; + std::string + op_format() const; - // Return this node's generated label for GraphViz. - virtual const std::string& - label(); + // Return this node's escape state. + Escape_state* + state(Escape_context* context, Named_object* fn); - // Return the object this node represents. - Named_object* - object() const - { return this->object_; } + // Return this node's escape encoding. + int + encoding() const + { return this->encoding_; } + // Set the node's escape encoding. void - add_edge(Node* v) - { this->edges_.insert(v); } - - const std::set<Node*>& - edges() const - { return this->edges_; } - - protected: - Node(Node_classification, Named_object* object); + set_encoding(int enc); - const std::string& - get_name() const - { return this->name_; } - - void - set_name(const std::string& name) - { this->name_ = name; } + bool + is_big(Escape_context*) const; - const std::string& - get_label() const - { return this->label_; } + bool + is_sink() const; - void - set_label(const std::string& label) - { this->label_ = label; } + // Methods to return the underlying value in the Node union. + Named_object* + object() const + { + return (this->classification_ == NODE_OBJECT + ? this->u_.object_val + : NULL); + } - private: - template<typename Node_class, - Node_classification node_classification> - const Node_class* - convert() const + Expression* + expr() const { - return (this->classification_ == node_classification - ? static_cast<const Node_class*>(this) - : NULL); + return (this->classification_ == NODE_EXPRESSION + ? this->u_.expression_val + : NULL); } - template<typename Node_class, - Node_classification node_classification> - Node_class* - convert() + Statement* + statement() const { - return (this->classification_ == node_classification - ? static_cast<Node_class*>(this) - : NULL); + return (this->classification_ == NODE_STATEMENT + ? this->u_.statement_val + : NULL); } - // The classification of this node. + // Static creation methods for each value supported in the union. + static Node* + make_node(Named_object*); + + static Node* + make_node(Expression*); + + static Node* + make_node(Statement*); + + // Return the maximum of an existing escape encoding E and a new + // escape type. + static int + max_encoding(int e, int etype); + + // Return a modified encoding for an input parameter that flows into an + // output parameter. + static int + note_inout_flows(int e, int index, Level level); + + private: + // The classification of this Node. Node_classification classification_; - // A unique ID for this node. - unsigned int id_; - // The name for this node e.g. "Node<ID>" used as a GraphViz identifier. - std::string name_; - // The label for this node in the GraphViz representation. - std::string label_; - // The object represented by this node. - Named_object* object_; - // A distinct set of nodes that this node has edges to. - std::set<Node*> edges_; + // The value union. + union + { + // If NODE_OBJECT. + Named_object* object_val; + // If NODE_EXPRESSION. + Expression* expression_val; + // If NODE_STATEMENT. + Statement* statement_val; + } u_; + // The node's escape state. + Escape_state* state_; + // The node's escape encoding. + // The encoding: + // | Return Encoding: (width - ESCAPE_RETURN_BITS) | + // | Content Escapes bit: 1 | + // | Escapement_encoding: ESCAPE_BITS | + int encoding_; + + // Cache all the Nodes created via Node::make_node to make the API simpler. + static std::map<Named_object*, Node*> objects; + static std::map<Expression*, Node*> expressions; + static std::map<Statement*, Node*> statements; }; +// The amount of bits used for the escapement encoding. +static const int ESCAPE_BITS = 3; + +// Mask used to extract encoding. +static const int ESCAPE_MASK = (1 << ESCAPE_BITS) - 1; + +// Value obtained by indirect of parameter escapes to heap. +static const int ESCAPE_CONTENT_ESCAPES = 1 << ESCAPE_BITS; -// A node representing a function that might be called during program execution. +// The amount of bits used in encoding of return values. +static const int ESCAPE_RETURN_BITS = ESCAPE_BITS + 1; -class Call_node : public Node +// For each output, the number of bits for a tag. +static const int ESCAPE_BITS_PER_OUTPUT_IN_TAG = 3; + +// The bit max to extract a single tag. +static const int ESCAPE_BITS_MASK_FOR_TAG = (1 << ESCAPE_BITS_PER_OUTPUT_IN_TAG) - 1; + +// The largest level that can be stored in a tag. +static const int ESCAPE_MAX_ENCODED_LEVEL = ESCAPE_BITS_MASK_FOR_TAG - 1; + +// A helper for converting escape notes from encoded integers to a +// textual format and vice-versa. + +class Escape_note { public: - Call_node(Named_object* function); + // Return the string representation of an escapement encoding. + static std::string + make_tag(int encoding); - const std::string& - name(); + // Return the escapement encoding for a string tag. + static int + parse_tag(std::string* tag); }; -// A node representing an object in the connection graph built for each function -// in the call graph. +// The escape context for a set of functions being analyzed. -class Connection_node : public Node +class Escape_context { public: - Connection_node(Named_object* object, Escapement_lattice e) - : Node(NODE_CONNECTION, object), - escape_state_(e) - { } + Escape_context(Gogo* gogo, bool recursive); - // Return this node's generated name for GraphViz. - const std::string& - name(); + // Return the Go IR. + Gogo* + gogo() const + { return this->gogo_; } - // Return this node's generated label for GraphViz. - const std::string& - label(); - - // Return the escape state for this node. - Escapement_lattice - escape_state() const - { return this->escape_state_; } + // Return the current function being analyzed. + Named_object* + current_function() const + { return this->current_function_; } - // Set the escape state for this node. + // Change the function being analyzed. void - set_escape_state(Escapement_lattice e) - { this->escape_state_ = e; } + set_current_function(Named_object* fn) + { this->current_function_ = fn; } - // Return the objects inside of this connection graph. - // This is empty for all connection nodes that are not the root of a - // connection graph. Each node in the call graph is a root of a connection - // graph. - const std::set<Node*>& - objects() const - { return this->objects_; } + // Return the name of the current function. + std::string + current_function_name() const; - void - add_object(Node* object) - { this->objects_.insert(object); } + // Return true if this is the context for a mutually recursive set of functions. + bool + recursive() const + { return this->recursive_; } - void - dump_connection(Connection_dump_context*); + // Return the special sink node for this context. + Node* + sink() + { return this->sink_; } - private: - // The escapement of this node. - Escapement_lattice escape_state_; - // The set of nodes contained within this connection node. If this node is - // not a root of a connection graph, this will be empty. - std::set<Node*> objects_; -}; + // Return the current loop depth. + int + loop_depth() const + { return this->loop_depth_; } -// This class implements fgo-dump-calls. The Call graph dump of a Go program. + // Increase the loop depth. + void + increase_loop_depth() + { this->loop_depth_++; } -class Call_dump_context : public String_dump -{ - public: - Call_dump_context(std::ostream* out = NULL); + // Decrease the loop depth. + void + decrease_loop_depth() + { this->loop_depth_--; } - // Initialize the dump context. void - dump(Gogo*, const char* basename); + set_loop_depth(int depth) + { this->loop_depth_ = depth; } - // Get dump output stream. - std::ostream& - ostream() - { return *this->ostream_; } + // Return the destination nodes encountered in this context. + const std::set<Node*>& + dsts() const + { return this->dsts_; } - // Implementation of String_dump interface. + // Add a destination node. void - write_c_string(const char*); + add_dst(Node* dst) + { this->dsts_.insert(dst); } + + // Return the nodes initially marked as non-escaping before flooding. + const std::vector<Node*>& + non_escaping_nodes() const + { return this->noesc_; } + // Initialize the dummy return values for this Node N using the results + // in FNTYPE. void - write_string(const std::string&); + init_retvals(Node* n, Function_type* fntype); - private: - // Stream on output dump file. - std::ostream* ostream_; + // Return the indirection of Node N. + Node* + add_dereference(Node* n); - Gogo* gogo_; -}; + // Keep track of possibly non-escaping node N. + void + track(Node* n); -// This class implements fgo-dump-conns. The connection graph dump of -// the functions called in a Go program. + int + flood_id() const + { return this->flood_id_; } -class Connection_dump_context : public String_dump -{ - public: - Connection_dump_context(std::ostream* out = NULL); - - // Initialize the dump context. void - dump(Gogo*, const char* basename); + increase_flood_id() + { this->flood_id_++; } - // Get dump output stream. - std::ostream& - ostream() - { return *this->ostream_; } + int + pdepth() const + { return this->pdepth_; } - // Implementation of String_dump interface. void - write_c_string(const char*); + increase_pdepth() + { this->pdepth_++; } void - write_string(const std::string&); + decrease_pdepth() + { this->pdepth_--; } private: - // Stream on output dump file. - std::ostream* ostream_; - + // The Go IR. Gogo* gogo_; + // The current function being analyzed. + Named_object* current_function_; + // Return whether this is the context for a recursive function or a group of mutually + // recursive functions. + bool recursive_; + // The sink for this escape context. Nodes whose reference objects created + // outside the current function are assigned to the sink as well as nodes that + // the analysis loses track of. + Node* sink_; + // Used to detect nested loop scopes. + int loop_depth_; + // All the destination nodes considered in this set of analyzed functions. + std::set<Node*> dsts_; + // All the nodes that were noted as possibly not escaping in this context. + std::vector<Node*> noesc_; + // An ID given to each dst and the flows discovered through DFS of that dst. + // This is used to avoid infinite recursion from nodes that point to each + // other within the flooding phase. + int flood_id_; + // The current level of recursion within a flooded section; used to debug. + int pdepth_; }; #endif // !defined(GO_ESCAPE_H) diff --git a/gcc/go/gofrontend/export.cc b/gcc/go/gofrontend/export.cc index e8617a1f9d..6e085991a2 100644 --- a/gcc/go/gofrontend/export.cc +++ b/gcc/go/gofrontend/export.cc @@ -6,8 +6,7 @@ #include "go-system.h" -#include "sha1.h" - +#include "go-sha1.h" #include "go-c.h" #include "gogo.h" @@ -19,22 +18,28 @@ // Class Export. -// Version 1 magic number. +const int Export::magic_len; -const int Export::v1_magic_len; +// Current version magic string. +const char Export::cur_magic[Export::magic_len] = + { + 'v', '2', ';', '\n' + }; -const char Export::v1_magic[Export::v1_magic_len] = +// Magic string for previous version (still supported) +const char Export::v1_magic[Export::magic_len] = { 'v', '1', ';', '\n' }; -const int Export::v1_checksum_len; +const int Export::checksum_len; // Constructor. Export::Export(Stream* stream) : stream_(stream), type_refs_(), type_index_(1), packages_() { + go_assert(Export::checksum_len == Go_sha1_helper::checksum_len); } // A functor to sort Named_object pointers by name. @@ -93,11 +98,10 @@ void Export::export_globals(const std::string& package_name, const std::string& prefix, const std::string& pkgpath, - int package_priority, const std::map<std::string, Package*>& packages, const std::map<std::string, Package*>& imports, const std::string& import_init_fn, - const std::set<Import_init>& imported_init_fns, + const Import_init_set& imported_init_fns, const Bindings* bindings) { // If there have been any errors so far, don't try to export @@ -134,8 +138,8 @@ Export::export_globals(const std::string& package_name, // Although the export data is readable, at least this version is, // it is conceptually a binary format. Start with a four byte - // verison number. - this->write_bytes(Export::v1_magic, Export::v1_magic_len); + // version number. + this->write_bytes(Export::cur_magic, Export::magic_len); // The package name. this->write_c_string("package "); @@ -156,16 +160,11 @@ Export::export_globals(const std::string& package_name, } this->write_c_string(";\n"); - // The package priority. - char buf[100]; - snprintf(buf, sizeof buf, "priority %d;\n", package_priority); - this->write_c_string(buf); - this->write_packages(packages); this->write_imports(imports); - this->write_imported_init_fns(package_name, package_priority, import_init_fn, + this->write_imported_init_fns(package_name, import_init_fn, imported_init_fns); // FIXME: It might be clever to add something about the processor @@ -250,17 +249,17 @@ void Export::write_imports(const std::map<std::string, Package*>& imports) { // Sort the imports for more consistent output. - std::vector<std::pair<std::string, Package*> > imp; + std::vector<std::pair<std::string, Package*> > sorted_imports; for (std::map<std::string, Package*>::const_iterator p = imports.begin(); p != imports.end(); ++p) - imp.push_back(std::make_pair(p->first, p->second)); + sorted_imports.push_back(std::make_pair(p->first, p->second)); - std::sort(imp.begin(), imp.end(), import_compare); + std::sort(sorted_imports.begin(), sorted_imports.end(), import_compare); for (std::vector<std::pair<std::string, Package*> >::const_iterator p = - imp.begin(); - p != imp.end(); + sorted_imports.begin(); + p != sorted_imports.end(); ++p) { this->write_c_string("import "); @@ -275,18 +274,62 @@ Export::write_imports(const std::map<std::string, Package*>& imports) } } +void +Export::add_init_graph_edge(Init_graph* init_graph, unsigned src, unsigned sink) +{ + Init_graph::iterator it = init_graph->find(src); + if (it != init_graph->end()) + it->second.insert(sink); + else + { + std::set<unsigned> succs; + succs.insert(sink); + (*init_graph)[src] = succs; + } +} + +// Constructs the imported portion of the init graph, e.g. those +// edges that we read from imported packages. + +void +Export::populate_init_graph(Init_graph* init_graph, + const Import_init_set& imported_init_fns, + const std::map<std::string, unsigned>& init_idx) +{ + for (Import_init_set::const_iterator p = imported_init_fns.begin(); + p != imported_init_fns.end(); + ++p) + { + const Import_init* ii = *p; + std::map<std::string, unsigned>::const_iterator srcit = + init_idx.find(ii->init_name()); + go_assert(srcit != init_idx.end()); + unsigned src = srcit->second; + for (std::set<std::string>::const_iterator pci = ii->precursors().begin(); + pci != ii->precursors().end(); + ++pci) + { + std::map<std::string, unsigned>::const_iterator it = + init_idx.find(*pci); + go_assert(it != init_idx.end()); + unsigned sink = it->second; + add_init_graph_edge(init_graph, src, sink); + } + } +} + // Write out the initialization functions which need to run for this // package. void -Export::write_imported_init_fns( - const std::string& package_name, - int priority, - const std::string& import_init_fn, - const std::set<Import_init>& imported_init_fns) +Export::write_imported_init_fns(const std::string& package_name, + const std::string& import_init_fn, + const Import_init_set& imported_init_fns) { - if (import_init_fn.empty() && imported_init_fns.empty()) - return; + if (import_init_fn.empty() && imported_init_fns.empty()) return; + + // Maps a given init function to the its index in the exported "init" clause. + std::map<std::string, unsigned> init_idx; this->write_c_string("init"); @@ -296,35 +339,154 @@ Export::write_imported_init_fns( this->write_string(package_name); this->write_c_string(" "); this->write_string(import_init_fn); - char buf[100]; - snprintf(buf, sizeof buf, " %d", priority); - this->write_c_string(buf); + init_idx[import_init_fn] = 0; + } + + if (imported_init_fns.empty()) + { + this->write_c_string(";\n"); + return; + } + + typedef std::map<int, std::vector<std::string> > level_map; + Init_graph init_graph; + level_map inits_at_level; + + // Walk through the set of import inits (already sorted by + // init fcn name) and write them out to the exports. + for (Import_init_set::const_iterator p = imported_init_fns.begin(); + p != imported_init_fns.end(); + ++p) + { + const Import_init* ii = *p; + this->write_c_string(" "); + this->write_string(ii->package_name()); + this->write_c_string(" "); + this->write_string(ii->init_name()); + + // Populate init_idx. + go_assert(init_idx.find(ii->init_name()) == init_idx.end()); + unsigned idx = init_idx.size(); + init_idx[ii->init_name()] = idx; + + // If the init function has a non-negative priority value, this + // is an indication that it was referred to in an older version + // export data section (e.g. we read a legacy object + // file). Record such init fcns so that we can fix up the graph + // for them (handled later in this function). + if (ii->priority() > 0) + { + level_map::iterator it = inits_at_level.find(ii->priority()); + if (it == inits_at_level.end()) + { + std::vector<std::string> l; + l.push_back(ii->init_name()); + inits_at_level[ii->priority()] = l; + } + else + it->second.push_back(ii->init_name()); + } + } + this->write_c_string(";\n"); + + // Create the init graph. Start by populating the graph with + // all the edges we inherited from imported packages. + populate_init_graph(&init_graph, imported_init_fns, init_idx); + + // Now add edges from the local init function to each of the + // imported fcns. + if (!import_init_fn.empty()) + { + unsigned src = 0; + go_assert(init_idx[import_init_fn] == 0); + for (Import_init_set::const_iterator p = imported_init_fns.begin(); + p != imported_init_fns.end(); + ++p) + { + const Import_init* ii = *p; + unsigned sink = init_idx[ii->init_name()]; + add_init_graph_edge(&init_graph, src, sink); + } } - if (!imported_init_fns.empty()) + // In the scenario where one or more of the packages we imported + // was written with the legacy export data format, add dummy edges + // to capture the priority relationships. Here is a package import + // graph as an example: + // + // *A + // /| + // / | + // B *C + // /| + // / | + // *D *E + // | /| + // |/ | + // *F *G + // + // Let's suppose that the object for package "C" is from an old + // gccgo, e.g. it has the old export data format. All other + // packages are compiled with the new compiler and have the new + // format. Packages with *'s have init functions. The scenario is + // that we're compiling a package "A"; during this process we'll + // read the export data for "C". It should look something like + // + // init F F..import 1 G G..import 1 D D..import 2 E E..import 2; + // + // To capture this information and convey it to the consumers of + // "A", the code below adds edges to the graph from each priority K + // function to every priority K-1 function for appropriate values + // of K. This will potentially add more edges than we need (for + // example, an edge from D to G), but given that we don't expect + // to see large numbers of old objects, this will hopefully be OK. + + if (inits_at_level.size() > 0) { - // Sort the list of functions for more consistent output. - std::vector<Import_init> v; - for (std::set<Import_init>::const_iterator p = imported_init_fns.begin(); - p != imported_init_fns.end(); - ++p) - v.push_back(*p); - std::sort(v.begin(), v.end()); - - for (std::vector<Import_init>::const_iterator p = v.begin(); - p != v.end(); - ++p) + for (level_map::reverse_iterator it = inits_at_level.rbegin(); + it != inits_at_level.rend(); ++it) + { + int level = it->first; + if (level < 2) break; + const std::vector<std::string>& fcns_at_level = it->second; + for (std::vector<std::string>::const_iterator sit = + fcns_at_level.begin(); + sit != fcns_at_level.end(); ++sit) + { + unsigned src = init_idx[*sit]; + level_map::iterator it2 = inits_at_level.find(level - 1); + if (it2 != inits_at_level.end()) + { + const std::vector<std::string> fcns_at_lm1 = it2->second; + for (std::vector<std::string>::const_iterator mit = + fcns_at_lm1.begin(); + mit != fcns_at_lm1.end(); ++mit) + { + unsigned sink = init_idx[*mit]; + add_init_graph_edge(&init_graph, src, sink); + } + } + } + } + } + + // Write out the resulting graph. + this->write_c_string("init_graph"); + for (Init_graph::const_iterator ki = init_graph.begin(); + ki != init_graph.end(); ++ki) + { + unsigned src = ki->first; + const std::set<unsigned>& successors = ki->second; + for (std::set<unsigned>::const_iterator vi = successors.begin(); + vi != successors.end(); ++vi) { this->write_c_string(" "); - this->write_string(p->package_name()); + this->write_unsigned(src); + unsigned sink = (*vi); this->write_c_string(" "); - this->write_string(p->init_name()); - char buf[100]; - snprintf(buf, sizeof buf, " %d", p->priority()); - this->write_c_string(buf); + this->write_unsigned(sink); } } - this->write_c_string(";\n"); } @@ -339,6 +501,26 @@ Export::write_name(const std::string& name) this->write_string(Gogo::message_name(name)); } +// Write an integer value to the export stream. + +void +Export::write_int(int value) +{ + char buf[100]; + snprintf(buf, sizeof buf, "%d", value); + this->write_c_string(buf); +} + +// Write an integer value to the export stream. + +void +Export::write_unsigned(unsigned value) +{ + char buf[100]; + snprintf(buf, sizeof buf, "%u", value); + this->write_c_string(buf); +} + // Export a type. We have to ensure that on import we create a single // Named_type node for each named type. We do this by keeping a hash // table mapping named types to reference numbers. The first time we @@ -426,6 +608,9 @@ Export::write_type(const Type* type) // definition of the type may refer to the named type via a // pointer. this->type_refs_[type] = index; + + if (named_type != NULL && named_type->is_alias()) + this->write_c_string("= "); } type->export_type(this); @@ -436,15 +621,19 @@ Export::write_type(const Type* type) this->type_refs_[type] = index; } -// Export escape information. +// Export escape note. void -Export::write_escape(const Node::Escapement_lattice& e) +Export::write_escape(std::string* note) { - char buf[30]; - snprintf(buf, sizeof buf, "<escape %d>", e); - this->write_c_string(buf); - return; + if (note != NULL && *note != "esc:0x0") + { + this->write_c_string(" "); + char buf[50]; + go_assert(note->find("esc:") != std::string::npos); + snprintf(buf, sizeof buf, "<%s>", note->c_str()); + this->write_c_string(buf); + } } // Add the builtin types to the export table. @@ -500,9 +689,8 @@ Export::register_builtin_type(Gogo* gogo, const char* name, Builtin_code code) Export::Stream::Stream() { - this->checksum_ = new sha1_ctx; - memset(this->checksum_, 0, sizeof(sha1_ctx)); - sha1_init_ctx(this->checksum_); + this->sha1_helper_ = go_create_sha1_helper(); + go_assert(this->sha1_helper_ != NULL); } Export::Stream::~Stream() @@ -515,7 +703,7 @@ Export::Stream::~Stream() void Export::Stream::write_and_sum_bytes(const char* bytes, size_t length) { - sha1_process_bytes(bytes, length, this->checksum_); + this->sha1_helper_->process_bytes(bytes, length); this->do_write(bytes, length); } @@ -524,14 +712,9 @@ Export::Stream::write_and_sum_bytes(const char* bytes, size_t length) std::string Export::Stream::checksum() { - // Use a union to provide the required alignment. - union - { - char checksum[Export::v1_checksum_len]; - long align; - } u; - sha1_finish_ctx(this->checksum_, u.checksum); - return std::string(u.checksum, Export::v1_checksum_len); + std::string rval = this->sha1_helper_->finish(); + delete this->sha1_helper_; + return rval; } // Write the checksum string to the export data. diff --git a/gcc/go/gofrontend/export.h b/gcc/go/gofrontend/export.h index 92baa722c5..fec73fbd75 100644 --- a/gcc/go/gofrontend/export.h +++ b/gcc/go/gofrontend/export.h @@ -7,15 +7,15 @@ #ifndef GO_EXPORT_H #define GO_EXPORT_H -#include "escape.h" #include "string-dump.h" -struct sha1_ctx; +class Go_sha1_helper; class Gogo; class Import_init; class Bindings; class Type; class Package; +class Import_init_set; // Codes used for the builtin types. These are all negative to make // them easily distinct from the codes assigned by Export::write_type. @@ -48,6 +48,17 @@ enum Builtin_code SMALLEST_BUILTIN_CODE = -21 }; +// Export data version number. New export data is written with the +// "current" version, but there is support for reading files with +// older version export data (at least for now). + +enum Export_data_version { + EXPORT_FORMAT_UNKNOWN = 0, + EXPORT_FORMAT_V1 = 1, + EXPORT_FORMAT_V2 = 2, + EXPORT_FORMAT_CURRENT = EXPORT_FORMAT_V2 +}; + // This class manages exporting Go declarations. It handles the main // loop of exporting. A pointer to this class is also passed to the // various specific export implementations. @@ -98,18 +109,21 @@ class Export : public String_dump void write_and_sum_bytes(const char*, size_t); - // The checksum. - sha1_ctx* checksum_; + // The checksum helper. + Go_sha1_helper* sha1_helper_; }; Export(Stream*); - // The magic code for version 1 export data. - static const int v1_magic_len = 4; - static const char v1_magic[v1_magic_len]; + // Size of export data magic string (which includes version number). + static const int magic_len = 4; - // The length of the v1 checksum string. - static const int v1_checksum_len = 20; + // Magic strings (current version and older v1 version). + static const char cur_magic[magic_len]; + static const char v1_magic[magic_len]; + + // The length of the checksum string. + static const int checksum_len = 20; // Register the builtin types. void @@ -120,7 +134,6 @@ class Export : public String_dump // is nothing to export, this->stream_->write will not be called. // PREFIX is the package prefix. PKGPATH is the package path. // Only one of PREFIX and PKGPATH will be non-empty. - // PACKAGE_PRIORITY is the priority to use for this package. // PACKAGES is all the packages we have seen. // IMPORTS is the explicitly imported packages. // IMPORT_INIT_FN is the name of the import initialization function @@ -131,11 +144,10 @@ class Export : public String_dump export_globals(const std::string& package_name, const std::string& prefix, const std::string& pkgpath, - int package_priority, const std::map<std::string, Package*>& packages, const std::map<std::string, Package*>& imports, const std::string& import_init_fn, - const std::set<Import_init>& imported_init_fns, + const Import_init_set& imported_init_fns, const Bindings* bindings); // Write a string to the export stream. @@ -162,9 +174,18 @@ class Export : public String_dump void write_type(const Type*); - // Write out escape information. + // Write the escape note to the export stream. If NOTE is NULL, write + // nothing. + void + write_escape(std::string* note); + + // Write an integer value. void - write_escape(const Node::Escapement_lattice& e); + write_int(int); + + // Write an unsigned value. + void + write_unsigned(unsigned); private: Export(const Export&); @@ -174,14 +195,24 @@ class Export : public String_dump void write_packages(const std::map<std::string, Package*>& packages); + typedef std::map<unsigned, std::set<unsigned> > Init_graph; + + static void + add_init_graph_edge(Init_graph* init_graph, unsigned src, unsigned sink); + + static void + populate_init_graph(Init_graph* init_graph, + const Import_init_set& imported_init_fns, + const std::map<std::string, unsigned>& init_idx); + // Write out the imported packages. void write_imports(const std::map<std::string, Package*>& imports); - // Write out the imported initialization functions. + // Write out the imported initialization functions and init graph. void - write_imported_init_fns(const std::string& package_name, int priority, - const std::string&, const std::set<Import_init>&); + write_imported_init_fns(const std::string& package_name, + const std::string&, const Import_init_set&); // Register one builtin type. void diff --git a/gcc/go/gofrontend/expressions.cc b/gcc/go/gofrontend/expressions.cc index 09ab5bf8f7..fee3203714 100644 --- a/gcc/go/gofrontend/expressions.cc +++ b/gcc/go/gofrontend/expressions.cc @@ -10,6 +10,8 @@ #include "go-c.h" #include "gogo.h" +#include "go-diagnostics.h" +#include "go-encode-id.h" #include "types.h" #include "export.h" #include "import.h" @@ -107,7 +109,7 @@ Expression::set_is_error() void Expression::report_error(const char* msg) { - error_at(this->location_, "%s", msg); + go_error_at(this->location_, "%s", msg); this->set_is_error(); } @@ -252,7 +254,9 @@ Expression::convert_type_to_interface(Type* lhs_type, Expression* rhs, else { // We are assigning a non-pointer value to the interface; the - // interface gets a copy of the value in the heap. + // interface gets a copy of the value in the heap if it escapes. + // TODO(cmang): Associate escape state state of RHS with newly + // created OBJ. obj = Expression::make_heap_expression(rhs, location); } @@ -320,9 +324,8 @@ Expression::convert_interface_to_interface(Type *lhs_type, Expression* rhs, if (for_type_guard) { // A type assertion fails when converting a nil interface. - first_field = - Runtime::make_call(Runtime::ASSERT_INTERFACE, location, 2, - lhs_type_expr, rhs_type_expr); + first_field = Runtime::make_call(Runtime::ASSERTITAB, location, 2, + lhs_type_expr, rhs_type_expr); } else if (lhs_is_empty) { @@ -334,9 +337,8 @@ Expression::convert_interface_to_interface(Type *lhs_type, Expression* rhs, { // A conversion to a non-empty interface may fail, but unlike a // type assertion converting nil will always succeed. - first_field = - Runtime::make_call(Runtime::CONVERT_INTERFACE, location, 2, - lhs_type_expr, rhs_type_expr); + first_field = Runtime::make_call(Runtime::REQUIREITAB, location, 2, + lhs_type_expr, rhs_type_expr); } // The second field is simply the object pointer. @@ -367,7 +369,7 @@ Expression::convert_interface_to_type(Type *lhs_type, Expression* rhs, Expression* rhs_inter_expr = Expression::make_type_descriptor(rhs_type, location); - Expression* check_iface = Runtime::make_call(Runtime::CHECK_INTERFACE_TYPE, + Expression* check_iface = Runtime::make_call(Runtime::ASSERTI2T, location, 3, lhs_type_expr, rhs_descriptor, rhs_inter_expr); @@ -535,10 +537,6 @@ class Error_expression : public Expression { return true; } bool - do_is_immutable() const - { return true; } - - bool do_numeric_constant_value(Numeric_constant* nc) const { nc->set_unsigned_long(NULL, 0); @@ -729,6 +727,13 @@ Var_expression::do_address_taken(bool escapes) else go_unreachable(); } + + if (this->variable_->is_variable() + && this->variable_->var_value()->is_in_heap()) + { + Node::make_node(this)->set_encoding(Node::ESCAPE_HEAP); + Node::make_node(this->variable_)->set_encoding(Node::ESCAPE_HEAP); + } } // Get the backend representation for a reference to a variable. @@ -755,7 +760,8 @@ Var_expression::do_get_backend(Translate_context* context) else go_unreachable(); - Bexpression* ret = context->backend()->var_expression(bvar, loc); + Bexpression* ret = + context->backend()->var_expression(bvar, this->in_lvalue_pos_, loc); if (is_in_heap) ret = context->backend()->indirect_expression(btype, ret, true, loc); return ret; @@ -782,6 +788,78 @@ Expression::make_var_reference(Named_object* var, Location location) return new Var_expression(var, location); } +// Class Enclosed_var_expression. + +int +Enclosed_var_expression::do_traverse(Traverse*) +{ + return TRAVERSE_CONTINUE; +} + +// Lower the reference to the enclosed variable. + +Expression* +Enclosed_var_expression::do_lower(Gogo* gogo, Named_object* function, + Statement_inserter* inserter, int) +{ + gogo->lower_expression(function, inserter, &this->reference_); + return this; +} + +// Flatten the reference to the enclosed variable. + +Expression* +Enclosed_var_expression::do_flatten(Gogo* gogo, Named_object* function, + Statement_inserter* inserter) +{ + gogo->flatten_expression(function, inserter, &this->reference_); + return this; +} + +void +Enclosed_var_expression::do_address_taken(bool escapes) +{ + if (!escapes) + { + if (this->variable_->is_variable()) + this->variable_->var_value()->set_non_escaping_address_taken(); + else if (this->variable_->is_result_variable()) + this->variable_->result_var_value()->set_non_escaping_address_taken(); + else + go_unreachable(); + } + else + { + if (this->variable_->is_variable()) + this->variable_->var_value()->set_address_taken(); + else if (this->variable_->is_result_variable()) + this->variable_->result_var_value()->set_address_taken(); + else + go_unreachable(); + } + + if (this->variable_->is_variable() + && this->variable_->var_value()->is_in_heap()) + Node::make_node(this->variable_)->set_encoding(Node::ESCAPE_HEAP); +} + +// Ast dump for enclosed variable expression. + +void +Enclosed_var_expression::do_dump_expression(Ast_dump_context* adc) const +{ + adc->ostream() << this->variable_->name(); +} + +// Make a reference to a variable within an enclosing function. + +Expression* +Expression::make_enclosing_var_reference(Expression* reference, + Named_object* var, Location location) +{ + return new Enclosed_var_expression(reference, var, location); +} + // Class Temporary_reference_expression. // The type. @@ -810,7 +888,10 @@ Temporary_reference_expression::do_get_backend(Translate_context* context) { Gogo* gogo = context->gogo(); Bvariable* bvar = this->statement_->get_backend_variable(context); - Bexpression* ret = gogo->backend()->var_expression(bvar, this->location()); + Varexpr_context ve_ctxt = (this->is_lvalue_ ? VE_lvalue : VE_rvalue); + + Bexpression* ret = gogo->backend()->var_expression(bvar, ve_ctxt, + this->location()); // The backend can't always represent the same set of recursive types // that the Go frontend can. In some cases this means that a @@ -881,11 +962,15 @@ Set_and_use_temporary_expression::do_get_backend(Translate_context* context) Location loc = this->location(); Gogo* gogo = context->gogo(); Bvariable* bvar = this->statement_->get_backend_variable(context); - Bexpression* var_ref = gogo->backend()->var_expression(bvar, loc); + Bexpression* lvar_ref = gogo->backend()->var_expression(bvar, VE_rvalue, loc); + Named_object* fn = context->function(); + go_assert(fn != NULL); + Bfunction* bfn = fn->func_value()->get_or_make_decl(gogo, fn); Bexpression* bexpr = this->expr_->get_backend(context); - Bstatement* set = gogo->backend()->assignment_statement(var_ref, bexpr, loc); - var_ref = gogo->backend()->var_expression(bvar, loc); + Bstatement* set = gogo->backend()->assignment_statement(bfn, lvar_ref, + bexpr, loc); + Bexpression* var_ref = gogo->backend()->var_expression(bvar, VE_lvalue, loc); Bexpression* ret = gogo->backend()->compound_expression(set, var_ref, loc); return ret; } @@ -988,11 +1073,12 @@ Sink_expression::do_get_backend(Translate_context* context) this->bvar_ = gogo->backend()->temporary_variable(fn_ctx, context->bblock(), bt, NULL, false, loc, &decl); - Bexpression* var_ref = gogo->backend()->var_expression(this->bvar_, loc); + Bexpression* var_ref = + gogo->backend()->var_expression(this->bvar_, VE_lvalue, loc); var_ref = gogo->backend()->compound_expression(decl, var_ref, loc); return var_ref; } - return gogo->backend()->var_expression(this->bvar_, loc); + return gogo->backend()->var_expression(this->bvar_, VE_lvalue, loc); } // Ast dump for sink expression. @@ -1058,9 +1144,9 @@ Func_expression::get_code_pointer(Gogo* gogo, Named_object* no, Location loc) // can't take their address. if (fntype->is_builtin()) { - error_at(loc, - "invalid use of special builtin function %qs; must be called", - no->message_name().c_str()); + go_error_at(loc, + "invalid use of special builtin function %qs; must be called", + no->message_name().c_str()); return gogo->backend()->error_expression(); } @@ -1097,10 +1183,10 @@ Func_expression::do_get_backend(Translate_context* context) { if (no->func_declaration_value()->type()->is_builtin()) { - error_at(this->location(), - ("invalid use of special builtin function %qs; " - "must be called"), - no->message_name().c_str()); + go_error_at(this->location(), + ("invalid use of special builtin function %qs; " + "must be called"), + no->message_name().c_str()); return gogo->backend()->error_expression(); } descriptor = no->func_declaration_value()->descriptor(gogo, no); @@ -1141,7 +1227,13 @@ Expression* Expression::make_func_reference(Named_object* function, Expression* closure, Location location) { - return new Func_expression(function, closure, location); + Func_expression* fe = new Func_expression(function, closure, location); + + // Detect references to builtin functions and set the runtime code if + // appropriate. + if (function->is_function_declaration()) + fe->set_runtime_code(Runtime::name_to_code(function->name())); + return fe; } // Class Func_descriptor_expression. @@ -1193,7 +1285,7 @@ Func_descriptor_expression::do_get_backend(Translate_context* context) Named_object* no = this->fn_; Location loc = no->location(); if (this->dvar_ != NULL) - return context->backend()->var_expression(this->dvar_, loc); + return context->backend()->var_expression(this->dvar_, VE_rvalue, loc); Gogo* gogo = context->gogo(); std::string var_name; @@ -1202,7 +1294,10 @@ Func_descriptor_expression::do_get_backend(Translate_context* context) && !no->func_declaration_value()->asm_name().empty() && Linemap::is_predeclared_location(no->location())) { - var_name = no->func_declaration_value()->asm_name() + "_descriptor"; + if (no->func_declaration_value()->asm_name().substr(0, 8) != "runtime.") + var_name = no->func_declaration_value()->asm_name() + "_descriptor"; + else + var_name = no->func_declaration_value()->asm_name() + "$descriptor"; is_descriptor = true; } else @@ -1219,16 +1314,18 @@ Func_descriptor_expression::do_get_backend(Translate_context* context) Btype* btype = this->type()->get_backend(gogo); Bvariable* bvar; + std::string asm_name(go_selectively_encode_id(var_name)); if (no->package() != NULL || is_descriptor) - bvar = context->backend()->immutable_struct_reference(var_name, btype, - loc); + bvar = context->backend()->immutable_struct_reference(var_name, asm_name, + btype, loc); else { Location bloc = Linemap::predeclared_location(); bool is_hidden = ((no->is_function() && no->func_value()->enclosing() != NULL) || Gogo::is_thunk(no)); - bvar = context->backend()->immutable_struct(var_name, is_hidden, false, + bvar = context->backend()->immutable_struct(var_name, asm_name, + is_hidden, false, btype, bloc); Expression_list* vals = new Expression_list(); vals->push_back(Expression::make_func_code_reference(this->fn_, bloc)); @@ -1242,7 +1339,7 @@ Func_descriptor_expression::do_get_backend(Translate_context* context) } this->dvar_ = bvar; - return gogo->backend()->var_expression(bvar, loc); + return gogo->backend()->var_expression(bvar, VE_rvalue, loc); } // Print a function descriptor expression. @@ -1285,7 +1382,7 @@ class Func_code_reference_expression : public Expression { return TRAVERSE_CONTINUE; } bool - do_is_immutable() const + do_is_static_initializer() const { return true; } Type* @@ -1360,8 +1457,8 @@ Unknown_expression::do_lower(Gogo*, Named_object*, Statement_inserter*, int) if (this->is_composite_literal_key_) return this; if (!this->no_error_message_) - error_at(location, "reference to undefined name %qs", - this->named_object_->message_name().c_str()); + go_error_at(location, "reference to undefined name %qs", + this->named_object_->message_name().c_str()); return Expression::make_error(location); } } @@ -1375,8 +1472,8 @@ Unknown_expression::do_lower(Gogo*, Named_object*, Statement_inserter*, int) if (this->is_composite_literal_key_) return this; if (!this->no_error_message_) - error_at(location, "reference to undefined type %qs", - real->message_name().c_str()); + go_error_at(location, "reference to undefined type %qs", + real->message_name().c_str()); return Expression::make_error(location); case Named_object::NAMED_OBJECT_VAR: real->var_value()->set_is_used(); @@ -1388,7 +1485,7 @@ Unknown_expression::do_lower(Gogo*, Named_object*, Statement_inserter*, int) if (this->is_composite_literal_key_) return this; if (!this->no_error_message_) - error_at(location, "unexpected reference to package"); + go_error_at(location, "unexpected reference to package"); return Expression::make_error(location); default: go_unreachable(); @@ -1431,7 +1528,7 @@ class Boolean_expression : public Expression { return true; } bool - do_is_immutable() const + do_is_static_initializer() const { return true; } Type* @@ -1639,7 +1736,7 @@ String_expression::do_import(Import* imp) } else { - error_at(imp->location(), "bad string constant"); + go_error_at(imp->location(), "bad string constant"); return Expression::make_error(imp->location()); } } @@ -1800,7 +1897,7 @@ class Integer_expression : public Expression { return true; } bool - do_is_immutable() const + do_is_static_initializer() const { return true; } bool @@ -1949,8 +2046,8 @@ Integer_expression::do_get_backend(Translate_context* context) else { if (!saw_errors()) - error_at(this->location(), - "unknown type for large integer constant"); + go_error_at(this->location(), + "unknown type for large integer constant"); return context->gogo()->backend()->error_expression(); } } @@ -2001,8 +2098,8 @@ Integer_expression::do_import(Import* imp) pos = plus_pos; else { - error_at(imp->location(), "bad number in import data: %qs", - num.c_str()); + go_error_at(imp->location(), "bad number in import data: %qs", + num.c_str()); return Expression::make_error(imp->location()); } if (pos == std::string::npos) @@ -2012,8 +2109,8 @@ Integer_expression::do_import(Import* imp) std::string real_str = num.substr(0, pos); if (mpfr_init_set_str(real, real_str.c_str(), 10, GMP_RNDN) != 0) { - error_at(imp->location(), "bad number in import data: %qs", - real_str.c_str()); + go_error_at(imp->location(), "bad number in import data: %qs", + real_str.c_str()); return Expression::make_error(imp->location()); } } @@ -2027,8 +2124,8 @@ Integer_expression::do_import(Import* imp) mpfr_t imag; if (mpfr_init_set_str(imag, imag_str.c_str(), 10, GMP_RNDN) != 0) { - error_at(imp->location(), "bad number in import data: %qs", - imag_str.c_str()); + go_error_at(imp->location(), "bad number in import data: %qs", + imag_str.c_str()); return Expression::make_error(imp->location()); } mpc_t cval; @@ -2050,8 +2147,8 @@ Integer_expression::do_import(Import* imp) mpz_t val; if (mpz_init_set_str(val, num.c_str(), 10) != 0) { - error_at(imp->location(), "bad number in import data: %qs", - num.c_str()); + go_error_at(imp->location(), "bad number in import data: %qs", + num.c_str()); return Expression::make_error(imp->location()); } Expression* ret; @@ -2067,8 +2164,8 @@ Integer_expression::do_import(Import* imp) mpfr_t val; if (mpfr_init_set_str(val, num.c_str(), 10, GMP_RNDN) != 0) { - error_at(imp->location(), "bad number in import data: %qs", - num.c_str()); + go_error_at(imp->location(), "bad number in import data: %qs", + num.c_str()); return Expression::make_error(imp->location()); } Expression* ret = Expression::make_float(&val, NULL, imp->location()); @@ -2196,7 +2293,7 @@ class Float_expression : public Expression { return true; } bool - do_is_immutable() const + do_is_static_initializer() const { return true; } bool @@ -2386,7 +2483,7 @@ class Complex_expression : public Expression { return true; } bool - do_is_immutable() const + do_is_static_initializer() const { return true; } bool @@ -2602,7 +2699,7 @@ class Const_expression : public Expression { return true; } bool - do_is_immutable() const + do_is_static_initializer() const { return true; } bool @@ -2671,8 +2768,8 @@ Const_expression::do_lower(Gogo* gogo, Named_object*, { if (iota_value == -1) { - error_at(this->location(), - "iota is only defined in const declarations"); + go_error_at(this->location(), + "iota is only defined in const declarations"); iota_value = 0; } return Expression::make_integer_ul(iota_value, NULL, this->location()); @@ -2958,7 +3055,7 @@ class Nil_expression : public Expression { return true; } bool - do_is_immutable() const + do_is_static_initializer() const { return true; } Type* @@ -3128,7 +3225,7 @@ Type_conversion_expression::do_lower(Gogo*, Named_object*, int adv = Lex::fetch_char(p, &c); if (adv == 0) { - warning_at(this->location(), 0, + go_warning_at(this->location(), 0, "invalid UTF-8 encoding"); adv = 1; } @@ -3195,10 +3292,11 @@ Type_conversion_expression::do_is_constant() const return true; } -// Return whether a type conversion is immutable. +// Return whether a type conversion can be used in a constant +// initializer. bool -Type_conversion_expression::do_is_immutable() const +Type_conversion_expression::do_is_static_initializer() const { Type* type = this->type_; Type* expr_type = this->expr_->type(); @@ -3207,7 +3305,7 @@ Type_conversion_expression::do_is_immutable() const || expr_type->interface_type() != NULL) return false; - if (!this->expr_->is_immutable()) + if (!this->expr_->is_static_initializer()) return false; if (Type::are_identical(type, expr_type, false, NULL)) @@ -3287,7 +3385,7 @@ Type_conversion_expression::do_check_types(Gogo*) if (Type::are_convertible(type, expr_type, &reason)) return; - error_at(this->location(), "%s", reason.c_str()); + go_error_at(this->location(), "%s", reason.c_str()); this->set_is_error(); } @@ -3331,7 +3429,8 @@ Type_conversion_expression::do_get_backend(Translate_context* context) } Expression* i2s_expr = - Runtime::make_call(Runtime::INT_TO_STRING, loc, 1, this->expr_); + Runtime::make_call(Runtime::INTSTRING, loc, 2, + Expression::make_nil(loc), this->expr_); return Expression::make_cast(type, i2s_expr, loc)->get_backend(context); } else if (type->is_string_type() && expr_type->is_slice_type()) @@ -3343,16 +3442,14 @@ Type_conversion_expression::do_get_backend(Translate_context* context) Runtime::Function code; if (e->integer_type()->is_byte()) - code = Runtime::BYTE_ARRAY_TO_STRING; + code = Runtime::SLICEBYTETOSTRING; else { go_assert(e->integer_type()->is_rune()); - code = Runtime::INT_ARRAY_TO_STRING; + code = Runtime::SLICERUNETOSTRING; } - Expression* valptr = a->get_value_pointer(gogo, this->expr_); - Expression* len = a->get_length(gogo, this->expr_); - return Runtime::make_call(code, loc, 2, valptr, - len)->get_backend(context); + return Runtime::make_call(code, loc, 2, Expression::make_nil(loc), + this->expr_)->get_backend(context); } else if (type->is_slice_type() && expr_type->is_string_type()) { @@ -3361,13 +3458,15 @@ Type_conversion_expression::do_get_backend(Translate_context* context) Runtime::Function code; if (e->integer_type()->is_byte()) - code = Runtime::STRING_TO_BYTE_ARRAY; + code = Runtime::STRINGTOSLICEBYTE; else { go_assert(e->integer_type()->is_rune()); - code = Runtime::STRING_TO_INT_ARRAY; + code = Runtime::STRINGTOSLICERUNE; } - Expression* s2a = Runtime::make_call(code, loc, 1, this->expr_); + Expression* s2a = Runtime::make_call(code, loc, 2, + Expression::make_nil(loc), + this->expr_); return Expression::make_unsafe_cast(type, s2a, loc)->get_backend(context); } else if (type->is_numeric_type()) @@ -3452,10 +3551,11 @@ Unsafe_type_conversion_expression::do_traverse(Traverse* traverse) return TRAVERSE_CONTINUE; } -// Return whether an unsafe type conversion is immutable. +// Return whether an unsafe type conversion can be used as a constant +// initializer. bool -Unsafe_type_conversion_expression::do_is_immutable() const +Unsafe_type_conversion_expression::do_is_static_initializer() const { Type* type = this->type_; Type* expr_type = this->expr_->type(); @@ -3464,7 +3564,7 @@ Unsafe_type_conversion_expression::do_is_immutable() const || expr_type->interface_type() != NULL) return false; - if (!this->expr_->is_immutable()) + if (!this->expr_->is_static_initializer()) return false; if (Type::are_convertible(type, expr_type, NULL)) @@ -3516,6 +3616,7 @@ Unsafe_type_conversion_expression::do_get_backend(Translate_context* context) || et->channel_type() != NULL || et->map_type() != NULL || et->function_type() != NULL + || et->integer_type() != NULL || et->is_nil_type()); else if (et->is_unsafe_pointer_type()) go_assert(t->points_to() != NULL); @@ -3533,6 +3634,8 @@ Unsafe_type_conversion_expression::do_get_backend(Translate_context* context) || et->map_type() != NULL || et->channel_type() != NULL || et->is_nil_type()); + else if (t->function_type() != NULL) + go_assert(et->points_to() != NULL); else go_unreachable(); @@ -3604,8 +3707,8 @@ Unary_expression::do_lower(Gogo*, Named_object*, Statement_inserter*, int) // *&x == x. if (!ue->expr_->is_addressable() && !ue->create_temp_) { - error_at(ue->location(), - "invalid operand for unary %<&%>"); + go_error_at(ue->location(), + "invalid operand for unary %<&%>"); this->set_is_error(); } return ue->expr_; @@ -3619,7 +3722,7 @@ Unary_expression::do_lower(Gogo*, Named_object*, Statement_inserter*, int) // having to deal with TYPE_VOID in other places. if (op == OPERATOR_MULT && expr->type()->is_unsafe_pointer_type()) { - error_at(this->location(), "invalid indirect of %<unsafe.Pointer%>"); + go_error_at(this->location(), "invalid indirect of %<unsafe.Pointer%>"); return Expression::make_error(this->location()); } @@ -3639,8 +3742,12 @@ Unary_expression::do_lower(Gogo*, Named_object*, Statement_inserter*, int) if (expr->numeric_constant_value(&nc)) { Numeric_constant result; - if (Unary_expression::eval_constant(op, &nc, loc, &result)) + bool issued_error; + if (Unary_expression::eval_constant(op, &nc, loc, &result, + &issued_error)) return result.expression(loc); + else if (issued_error) + return Expression::make_error(this->location()); } } @@ -3695,9 +3802,25 @@ Unary_expression::do_flatten(Gogo* gogo, Named_object*, // value does not escape. If this->escapes_ is true, we may be // able to set it to false if taking the address of a variable // that does not escape. - if (this->escapes_ && this->expr_->var_expression() != NULL) + Node* n = Node::make_node(this); + if ((n->encoding() & ESCAPE_MASK) == int(Node::ESCAPE_NONE)) + this->escapes_ = false; + + // When compiling the runtime, the address operator does not + // cause local variables to escape. When escape analysis + // becomes the default, this should be changed to make it an + // error if we have an address operator that escapes. + if (gogo->compiling_runtime() && gogo->package_name() == "runtime") + this->escapes_ = false; + + Named_object* var = NULL; + if (this->expr_->var_expression() != NULL) + var = this->expr_->var_expression()->named_object(); + else if (this->expr_->enclosed_var_expression() != NULL) + var = this->expr_->enclosed_var_expression()->variable(); + + if (this->escapes_ && var != NULL) { - Named_object* var = this->expr_->var_expression()->named_object(); if (var->is_variable()) this->escapes_ = var->var_value()->escapes(); if (var->is_result_variable()) @@ -3746,13 +3869,73 @@ Unary_expression::do_is_constant() const return this->expr_->is_constant(); } +// Return whether a unary expression can be used as a constant +// initializer. + +bool +Unary_expression::do_is_static_initializer() const +{ + if (this->op_ == OPERATOR_MULT) + return false; + else if (this->op_ == OPERATOR_AND) + return Unary_expression::base_is_static_initializer(this->expr_); + else + return this->expr_->is_static_initializer(); +} + +// Return whether the address of EXPR can be used as a static +// initializer. + +bool +Unary_expression::base_is_static_initializer(Expression* expr) +{ + // The address of a field reference can be a static initializer if + // the base can be a static initializer. + Field_reference_expression* fre = expr->field_reference_expression(); + if (fre != NULL) + return Unary_expression::base_is_static_initializer(fre->expr()); + + // The address of an index expression can be a static initializer if + // the base can be a static initializer and the index is constant. + Array_index_expression* aind = expr->array_index_expression(); + if (aind != NULL) + return (aind->end() == NULL + && aind->start()->is_constant() + && Unary_expression::base_is_static_initializer(aind->array())); + + // The address of a global variable can be a static initializer. + Var_expression* ve = expr->var_expression(); + if (ve != NULL) + { + Named_object* no = ve->named_object(); + return no->is_variable() && no->var_value()->is_global(); + } + + // The address of a composite literal can be used as a static + // initializer if the composite literal is itself usable as a + // static initializer. + if (expr->is_composite_literal() && expr->is_static_initializer()) + return true; + + // The address of a string constant can be used as a static + // initializer. This can not be written in Go itself but this is + // used when building a type descriptor. + if (expr->string_expression() != NULL) + return true; + + return false; +} + // Apply unary opcode OP to UNC, setting NC. Return true if this -// could be done, false if not. Issue errors for overflow. +// could be done, false if not. On overflow, issues an error and sets +// *ISSUED_ERROR. bool Unary_expression::eval_constant(Operator op, const Numeric_constant* unc, - Location location, Numeric_constant* nc) + Location location, Numeric_constant* nc, + bool* issued_error) { + *issued_error = false; switch (op) { case OPERATOR_PLUS: @@ -3897,7 +4080,12 @@ Unary_expression::eval_constant(Operator op, const Numeric_constant* unc, mpz_clear(uval); mpz_clear(val); - return nc->set_type(unc->type(), true, location); + if (!nc->set_type(unc->type(), true, location)) + { + *issued_error = true; + return false; + } + return true; } // Return the integral constant value of a unary expression, if it has one. @@ -3908,8 +4096,9 @@ Unary_expression::do_numeric_constant_value(Numeric_constant* nc) const Numeric_constant unc; if (!this->expr_->numeric_constant_value(&unc)) return false; + bool issued_error; return Unary_expression::eval_constant(this->op_, &unc, this->location(), - nc); + nc, &issued_error); } // Return the type of a unary expression. @@ -4020,7 +4209,7 @@ Unary_expression::do_check_types(Gogo*) { if (!this->create_temp_) { - error_at(this->location(), "invalid operand for unary %<&%>"); + go_error_at(this->location(), "invalid operand for unary %<&%>"); this->set_is_error(); } } @@ -4059,11 +4248,16 @@ Unary_expression::do_get_backend(Translate_context* context) { Temporary_statement* temp = sut->temporary(); Bvariable* bvar = temp->get_backend_variable(context); - Bexpression* bvar_expr = gogo->backend()->var_expression(bvar, loc); + Bexpression* bvar_expr = + gogo->backend()->var_expression(bvar, VE_lvalue, loc); Bexpression* bval = sut->expression()->get_backend(context); + Named_object* fn = context->function(); + go_assert(fn != NULL); + Bfunction* bfn = + fn->func_value()->get_or_make_decl(gogo, fn); Bstatement* bassign = - gogo->backend()->assignment_statement(bvar_expr, bval, loc); + gogo->backend()->assignment_statement(bfn, bvar_expr, bval, loc); Bexpression* bvar_addr = gogo->backend()->address_expression(bvar_expr, loc); return gogo->backend()->compound_expression(bassign, bvar_addr, loc); @@ -4098,7 +4292,7 @@ Unary_expression::do_get_backend(Translate_context* context) // constructor will not do what the programmer expects. go_assert(!this->expr_->is_composite_literal() - || this->expr_->is_immutable()); + || this->expr_->is_static_initializer()); if (this->expr_->classification() == EXPRESSION_UNARY) { Unary_expression* ue = @@ -4136,30 +4330,47 @@ Unary_expression::do_get_backend(Translate_context* context) // initialize the value once, so we can use this directly // rather than copying it. In that case we can't make it // read-only, because the program is permitted to change it. - copy_to_heap = (at->element_type()->has_pointer() - && !context->is_const()); + copy_to_heap = context->function() != NULL; } + std::string asm_name(go_selectively_encode_id(buf)); Bvariable* implicit = - gogo->backend()->implicit_variable(buf, btype, true, copy_to_heap, - false, 0); + gogo->backend()->implicit_variable(buf, asm_name, + btype, true, copy_to_heap, + false, 0); gogo->backend()->implicit_variable_set_init(implicit, buf, btype, true, copy_to_heap, false, bexpr); - bexpr = gogo->backend()->var_expression(implicit, loc); + bexpr = gogo->backend()->var_expression(implicit, VE_lvalue, loc); + + // If we are not copying a slice initializer to the heap, + // then it can be changed by the program, so if it can + // contain pointers we must register it as a GC root. + if (this->is_slice_init_ + && !copy_to_heap + && this->expr_->type()->has_pointer()) + { + Bexpression* root = + gogo->backend()->var_expression(implicit, VE_lvalue, loc); + root = gogo->backend()->address_expression(root, loc); + Type* type = Type::make_pointer_type(this->expr_->type()); + gogo->add_gc_root(Expression::make_backend(root, type, loc)); + } } else if ((this->expr_->is_composite_literal() - || this->expr_->string_expression() != NULL) - && this->expr_->is_immutable()) + || this->expr_->string_expression() != NULL) + && this->expr_->is_static_initializer()) { // Build a decl for a constant constructor. snprintf(buf, sizeof buf, "C%u", counter); ++counter; + std::string asm_name(go_selectively_encode_id(buf)); Bvariable* decl = - gogo->backend()->immutable_struct(buf, true, false, btype, loc); + gogo->backend()->immutable_struct(buf, asm_name, + true, false, btype, loc); gogo->backend()->immutable_struct_set_init(decl, buf, true, false, btype, loc, bexpr); - bexpr = gogo->backend()->var_expression(decl, loc); + bexpr = gogo->backend()->var_expression(decl, VE_lvalue, loc); } go_assert(!this->create_temp_ || this->expr_->is_variable()); @@ -4198,7 +4409,9 @@ Unary_expression::do_get_backend(Translate_context* context) Bexpression* crash = gogo->runtime_error(RUNTIME_ERROR_NIL_DEREFERENCE, loc)->get_backend(context); - bexpr = gogo->backend()->conditional_expression(btype, compare, + Bfunction* bfn = context->function()->func_value()->get_decl(); + bexpr = gogo->backend()->conditional_expression(bfn, btype, + compare, crash, bexpr, loc); @@ -4317,6 +4530,33 @@ Binary_expression::do_traverse(Traverse* traverse) return Expression::traverse(&this->right_, traverse); } +// Return whether this expression may be used as a static initializer. + +bool +Binary_expression::do_is_static_initializer() const +{ + if (!this->left_->is_static_initializer() + || !this->right_->is_static_initializer()) + return false; + + // Addresses can be static initializers, but we can't implement + // arbitray binary expressions of them. + Unary_expression* lu = this->left_->unary_expression(); + Unary_expression* ru = this->right_->unary_expression(); + if (lu != NULL && lu->op() == OPERATOR_AND) + { + if (ru != NULL && ru->op() == OPERATOR_AND) + return this->op_ == OPERATOR_MINUS; + else + return this->op_ == OPERATOR_PLUS || this->op_ == OPERATOR_MINUS; + } + else if (ru != NULL && ru->op() == OPERATOR_AND) + return this->op_ == OPERATOR_PLUS || this->op_ == OPERATOR_MINUS; + + // Other cases should resolve in the backend. + return true; +} + // Return the type to use for a binary operation on operands of // LEFT_TYPE and RIGHT_TYPE. These are the types of constants and as // such may be NULL or abstract. @@ -4539,13 +4779,15 @@ Binary_expression::compare_complex(const Numeric_constant* left_nc, // Apply binary opcode OP to LEFT_NC and RIGHT_NC, setting NC. Return // true if this could be done, false if not. Issue errors at LOCATION -// as appropriate. +// as appropriate, and sets *ISSUED_ERROR if it did. bool Binary_expression::eval_constant(Operator op, Numeric_constant* left_nc, Numeric_constant* right_nc, - Location location, Numeric_constant* nc) + Location location, Numeric_constant* nc, + bool* issued_error) { + *issued_error = false; switch (op) { case OPERATOR_OROR: @@ -4594,7 +4836,11 @@ Binary_expression::eval_constant(Operator op, Numeric_constant* left_nc, r = Binary_expression::eval_integer(op, left_nc, right_nc, location, nc); if (r) - r = nc->set_type(type, true, location); + { + r = nc->set_type(type, true, location); + if (!r) + *issued_error = true; + } return r; } @@ -4627,7 +4873,7 @@ Binary_expression::eval_integer(Operator op, const Numeric_constant* left_nc, mpz_add(val, left_val, right_val); if (mpz_sizeinbase(val, 2) > 0x100000) { - error_at(location, "constant addition overflow"); + go_error_at(location, "constant addition overflow"); nc->set_invalid(); mpz_set_ui(val, 1); } @@ -4636,7 +4882,7 @@ Binary_expression::eval_integer(Operator op, const Numeric_constant* left_nc, mpz_sub(val, left_val, right_val); if (mpz_sizeinbase(val, 2) > 0x100000) { - error_at(location, "constant subtraction overflow"); + go_error_at(location, "constant subtraction overflow"); nc->set_invalid(); mpz_set_ui(val, 1); } @@ -4651,7 +4897,7 @@ Binary_expression::eval_integer(Operator op, const Numeric_constant* left_nc, mpz_mul(val, left_val, right_val); if (mpz_sizeinbase(val, 2) > 0x100000) { - error_at(location, "constant multiplication overflow"); + go_error_at(location, "constant multiplication overflow"); nc->set_invalid(); mpz_set_ui(val, 1); } @@ -4661,7 +4907,7 @@ Binary_expression::eval_integer(Operator op, const Numeric_constant* left_nc, mpz_tdiv_q(val, left_val, right_val); else { - error_at(location, "division by zero"); + go_error_at(location, "division by zero"); nc->set_invalid(); mpz_set_ui(val, 0); } @@ -4671,7 +4917,7 @@ Binary_expression::eval_integer(Operator op, const Numeric_constant* left_nc, mpz_tdiv_r(val, left_val, right_val); else { - error_at(location, "division by zero"); + go_error_at(location, "division by zero"); nc->set_invalid(); mpz_set_ui(val, 0); } @@ -4683,7 +4929,7 @@ Binary_expression::eval_integer(Operator op, const Numeric_constant* left_nc, mpz_mul_2exp(val, left_val, shift); else { - error_at(location, "shift count overflow"); + go_error_at(location, "shift count overflow"); nc->set_invalid(); mpz_set_ui(val, 1); } @@ -4695,7 +4941,7 @@ Binary_expression::eval_integer(Operator op, const Numeric_constant* left_nc, unsigned long shift = mpz_get_ui(right_val); if (mpz_cmp_ui(right_val, shift) != 0) { - error_at(location, "shift count overflow"); + go_error_at(location, "shift count overflow"); nc->set_invalid(); mpz_set_ui(val, 1); } @@ -4790,7 +5036,7 @@ Binary_expression::eval_float(Operator op, const Numeric_constant* left_nc, mpfr_div(val, left_val, right_val, GMP_RNDN); else { - error_at(location, "division by zero"); + go_error_at(location, "division by zero"); nc->set_invalid(); mpfr_set_ui(val, 0, GMP_RNDN); } @@ -4855,7 +5101,7 @@ Binary_expression::eval_complex(Operator op, const Numeric_constant* left_nc, case OPERATOR_DIV: if (mpc_cmp_si(right_val, 0) == 0) { - error_at(location, "division by zero"); + go_error_at(location, "division by zero"); nc->set_invalid(); mpc_set_ui(val, 0, MPC_RNDNN); break; @@ -4917,9 +5163,15 @@ Binary_expression::do_lower(Gogo* gogo, Named_object*, else { Numeric_constant nc; + bool issued_error; if (!Binary_expression::eval_constant(op, &left_nc, &right_nc, - location, &nc)) + location, &nc, + &issued_error)) + { + if (issued_error) + return Expression::make_error(location); return this; + } return nc.expression(location); } } @@ -4963,6 +5215,31 @@ Binary_expression::do_lower(Gogo* gogo, Named_object*, return this->lower_interface_value_comparison(gogo, inserter); } + // Lower string concatenation to String_concat_expression, so that + // we can group sequences of string additions. + if (this->left_->type()->is_string_type() && this->op_ == OPERATOR_PLUS) + { + Expression_list* exprs; + String_concat_expression* left_sce = + this->left_->string_concat_expression(); + if (left_sce != NULL) + exprs = left_sce->exprs(); + else + { + exprs = new Expression_list(); + exprs->push_back(this->left_); + } + + String_concat_expression* right_sce = + this->right_->string_concat_expression(); + if (right_sce != NULL) + exprs->append(right_sce->exprs()); + else + exprs->push_back(this->right_); + + return Expression::make_string_concat(exprs); + } + return this; } @@ -5079,7 +5356,6 @@ Binary_expression::lower_array_comparison(Gogo* gogo, Expression_list* args = new Expression_list(); args->push_back(this->operand_address(inserter, this->left_)); args->push_back(this->operand_address(inserter, this->right_)); - args->push_back(Expression::make_type_info(at, TYPE_INFO_SIZE)); Expression* ret = Expression::make_call(func, args, false, loc); @@ -5172,25 +5448,6 @@ Binary_expression::do_flatten(Gogo* gogo, Named_object*, } Temporary_statement* temp; - if (this->left_->type()->is_string_type() - && this->op_ == OPERATOR_PLUS) - { - if (!this->left_->is_variable() - && !this->left_->is_constant()) - { - temp = Statement::make_temporary(NULL, this->left_, loc); - inserter->insert(temp); - this->left_ = Expression::make_temporary_reference(temp, loc); - } - if (!this->right_->is_variable() - && !this->right_->is_constant()) - { - temp = - Statement::make_temporary(this->left_->type(), this->right_, loc); - this->right_ = Expression::make_temporary_reference(temp, loc); - inserter->insert(temp); - } - } Type* left_type = this->left_->type(); bool is_shift_op = (this->op_ == OPERATOR_LSHIFT @@ -5254,8 +5511,9 @@ Binary_expression::do_numeric_constant_value(Numeric_constant* nc) const Numeric_constant right_nc; if (!this->right_->numeric_constant_value(&right_nc)) return false; + bool issued_error; return Binary_expression::eval_constant(this->op_, &left_nc, &right_nc, - this->location(), nc); + this->location(), nc, &issued_error); } // Note that the value is being discarded. @@ -5354,7 +5612,12 @@ Binary_expression::do_determine_type(const Type_context* context) Type_context subcontext(*context); - if (is_comparison) + if (is_constant_expr) + { + subcontext.type = NULL; + subcontext.may_be_abstract = true; + } + else if (is_comparison) { // In a comparison, the context does not determine the types of // the operands. @@ -5396,8 +5659,7 @@ Binary_expression::do_determine_type(const Type_context* context) subcontext.type = subcontext.type->make_non_abstract_type(); } - if (!is_constant_expr) - this->left_->determine_type(&subcontext); + this->left_->determine_type(&subcontext); if (is_shift_op) { @@ -5417,8 +5679,7 @@ Binary_expression::do_determine_type(const Type_context* context) subcontext.may_be_abstract = false; } - if (!is_constant_expr) - this->right_->determine_type(&subcontext); + this->right_->determine_type(&subcontext); if (is_comparison) { @@ -5446,7 +5707,7 @@ Binary_expression::check_operator_type(Operator op, Type* type, Type* otype, if (!type->is_boolean_type() || !otype->is_boolean_type()) { - error_at(location, "expected boolean type"); + go_error_at(location, "expected boolean type"); return false; } break; @@ -5457,7 +5718,7 @@ Binary_expression::check_operator_type(Operator op, Type* type, Type* otype, std::string reason; if (!Type::are_compatible_for_comparison(true, type, otype, &reason)) { - error_at(location, "%s", reason.c_str()); + go_error_at(location, "%s", reason.c_str()); return false; } } @@ -5471,7 +5732,7 @@ Binary_expression::check_operator_type(Operator op, Type* type, Type* otype, std::string reason; if (!Type::are_compatible_for_comparison(false, type, otype, &reason)) { - error_at(location, "%s", reason.c_str()); + go_error_at(location, "%s", reason.c_str()); return false; } } @@ -5482,7 +5743,7 @@ Binary_expression::check_operator_type(Operator op, Type* type, Type* otype, if ((!type->is_numeric_type() && !type->is_string_type()) || (!otype->is_numeric_type() && !otype->is_string_type())) { - error_at(location, + go_error_at(location, "expected integer, floating, complex, or string type"); return false; } @@ -5496,7 +5757,7 @@ Binary_expression::check_operator_type(Operator op, Type* type, Type* otype, case OPERATOR_DIVEQ: if (!type->is_numeric_type() || !otype->is_numeric_type()) { - error_at(location, "expected integer, floating, or complex type"); + go_error_at(location, "expected integer, floating, or complex type"); return false; } break; @@ -5513,7 +5774,7 @@ Binary_expression::check_operator_type(Operator op, Type* type, Type* otype, case OPERATOR_BITCLEAREQ: if (type->integer_type() == NULL || otype->integer_type() == NULL) { - error_at(location, "expected integer type"); + go_error_at(location, "expected integer type"); return false; } break; @@ -5671,6 +5932,7 @@ Binary_expression::do_get_backend(Translate_context* context) case OPERATOR_DIV: if (left_type->float_type() != NULL || left_type->complex_type() != NULL) break; + // Fall through. case OPERATOR_MOD: is_idiv_op = true; break; @@ -5686,14 +5948,9 @@ Binary_expression::do_get_backend(Translate_context* context) go_unreachable(); } - if (left_type->is_string_type()) - { - go_assert(this->op_ == OPERATOR_PLUS); - Expression* string_plus = - Runtime::make_call(Runtime::STRING_PLUS, loc, 2, - this->left_, this->right_); - return string_plus->get_backend(context); - } + // The only binary operation for string is +, and that should have + // been converted to a String_concat_expression in do_lower. + go_assert(!left_type->is_string_type()); // For complex division Go might want slightly different results than the // backend implementation provides, so we have our own runtime routine. @@ -5756,6 +6013,7 @@ Binary_expression::do_get_backend(Translate_context* context) Bexpression* zero_expr = gogo->backend()->integer_constant_expression(left_btype, zero); overflow = zero_expr; + Bfunction* bfn = context->function()->func_value()->get_decl(); if (this->op_ == OPERATOR_RSHIFT && !left_type->integer_type()->is_unsigned()) { @@ -5764,11 +6022,12 @@ Binary_expression::do_get_backend(Translate_context* context) zero_expr, loc); Bexpression* neg_one_expr = gogo->backend()->integer_constant_expression(left_btype, neg_one); - overflow = gogo->backend()->conditional_expression(btype, neg_expr, + overflow = gogo->backend()->conditional_expression(bfn, + btype, neg_expr, neg_one_expr, zero_expr, loc); } - ret = gogo->backend()->conditional_expression(btype, compare, ret, + ret = gogo->backend()->conditional_expression(bfn, btype, compare, ret, overflow, loc); mpz_clear(bitsval); } @@ -5791,7 +6050,9 @@ Binary_expression::do_get_backend(Translate_context* context) loc)->get_backend(context); // right == 0 ? (__go_runtime_error(...), 0) : ret - ret = gogo->backend()->conditional_expression(btype, check, crash, + Bfunction* bfn = context->function()->func_value()->get_decl(); + ret = gogo->backend()->conditional_expression(bfn, btype, + check, crash, ret, loc); } @@ -5811,6 +6072,7 @@ Binary_expression::do_get_backend(Translate_context* context) gogo->backend()->integer_constant_expression(btype, zero); Bexpression* one_expr = gogo->backend()->integer_constant_expression(btype, one); + Bfunction* bfn = context->function()->func_value()->get_decl(); if (type->integer_type()->is_unsigned()) { @@ -5822,12 +6084,12 @@ Binary_expression::do_get_backend(Translate_context* context) left, right, loc); if (this->op_ == OPERATOR_DIV) overflow = - gogo->backend()->conditional_expression(btype, cmp, + gogo->backend()->conditional_expression(bfn, btype, cmp, one_expr, zero_expr, loc); else overflow = - gogo->backend()->conditional_expression(btype, cmp, + gogo->backend()->conditional_expression(bfn, btype, cmp, zero_expr, left, loc); } @@ -5847,7 +6109,8 @@ Binary_expression::do_get_backend(Translate_context* context) overflow = gogo->backend()->convert_expression(btype, overflow, loc); // right == -1 ? - left : ret - ret = gogo->backend()->conditional_expression(btype, check, overflow, + ret = gogo->backend()->conditional_expression(bfn, btype, + check, overflow, ret, loc); } } @@ -6038,7 +6301,7 @@ Binary_expression::do_import(Import* imp) } else { - error_at(imp->location(), "unrecognized binary operator"); + go_error_at(imp->location(), "unrecognized binary operator"); return Expression::make_error(imp->location()); } @@ -6086,9 +6349,18 @@ Expression::comparison(Translate_context* context, Type* result_type, if (left_type->is_string_type() && right_type->is_string_type()) { - left = Runtime::make_call(Runtime::STRCMP, location, 2, - left, right); - right = zexpr; + if (op == OPERATOR_EQEQ || op == OPERATOR_NOTEQ) + { + left = Runtime::make_call(Runtime::EQSTRING, location, 2, + left, right); + right = Expression::make_boolean(true, location); + } + else + { + left = Runtime::make_call(Runtime::CMPSTRING, location, 2, + left, right); + right = zexpr; + } } else if ((left_type->interface_type() != NULL && right_type->interface_type() == NULL @@ -6120,11 +6392,12 @@ Expression::comparison(Translate_context* context, Type* result_type, Expression::make_type_descriptor(right_type, location); left = Runtime::make_call((left_type->interface_type()->is_empty() - ? Runtime::EMPTY_INTERFACE_VALUE_COMPARE - : Runtime::INTERFACE_VALUE_COMPARE), + ? Runtime::EFACEVALEQ + : Runtime::IFACEVALEQ), location, 3, left, descriptor, pointer_arg); - right = zexpr; + go_assert(op == OPERATOR_EQEQ || op == OPERATOR_NOTEQ); + right = Expression::make_boolean(true, location); } else if (left_type->interface_type() != NULL && right_type->interface_type() != NULL) @@ -6132,25 +6405,25 @@ Expression::comparison(Translate_context* context, Type* result_type, Runtime::Function compare_function; if (left_type->interface_type()->is_empty() && right_type->interface_type()->is_empty()) - compare_function = Runtime::EMPTY_INTERFACE_COMPARE; + compare_function = Runtime::EFACEEQ; else if (!left_type->interface_type()->is_empty() && !right_type->interface_type()->is_empty()) - compare_function = Runtime::INTERFACE_COMPARE; + compare_function = Runtime::IFACEEQ; else { if (left_type->interface_type()->is_empty()) { - go_assert(op == OPERATOR_EQEQ || op == OPERATOR_NOTEQ); std::swap(left_type, right_type); std::swap(left, right); } go_assert(!left_type->interface_type()->is_empty()); go_assert(right_type->interface_type()->is_empty()); - compare_function = Runtime::INTERFACE_EMPTY_COMPARE; + compare_function = Runtime::IFACEEFACEEQ; } left = Runtime::make_call(compare_function, location, 2, left, right); - right = zexpr; + go_assert(op == OPERATOR_EQEQ || op == OPERATOR_NOTEQ); + right = Expression::make_boolean(true, location); } if (left_type->is_nil_type() @@ -6188,42 +6461,190 @@ Expression::comparison(Translate_context* context, Type* result_type, return ret; } -// Class Bound_method_expression. +// Class String_concat_expression. -// Traversal. +bool +String_concat_expression::do_is_constant() const +{ + for (Expression_list::const_iterator pe = this->exprs_->begin(); + pe != this->exprs_->end(); + ++pe) + { + if (!(*pe)->is_constant()) + return false; + } + return true; +} -int -Bound_method_expression::do_traverse(Traverse* traverse) +bool +String_concat_expression::do_is_static_initializer() const { - return Expression::traverse(&this->expr_, traverse); + for (Expression_list::const_iterator pe = this->exprs_->begin(); + pe != this->exprs_->end(); + ++pe) + { + if (!(*pe)->is_static_initializer()) + return false; + } + return true; +} + +Type* +String_concat_expression::do_type() +{ + Type* t = this->exprs_->front()->type(); + Expression_list::iterator pe = this->exprs_->begin(); + ++pe; + for (; pe != this->exprs_->end(); ++pe) + { + Type* t1; + if (!Binary_expression::operation_type(OPERATOR_PLUS, t, + (*pe)->type(), + &t1)) + return Type::make_error_type(); + t = t1; + } + return t; } -// Lower the expression. If this is a method value rather than being -// called, and the method is accessed via a pointer, we may need to -// add nil checks. Introduce a temporary variable so that those nil -// checks do not cause multiple evaluation. +void +String_concat_expression::do_determine_type(const Type_context* context) +{ + Type_context subcontext(*context); + for (Expression_list::iterator pe = this->exprs_->begin(); + pe != this->exprs_->end(); + ++pe) + { + Type* t = (*pe)->type(); + if (!t->is_abstract()) + { + subcontext.type = t; + break; + } + } + if (subcontext.type == NULL) + subcontext.type = this->exprs_->front()->type(); + for (Expression_list::iterator pe = this->exprs_->begin(); + pe != this->exprs_->end(); + ++pe) + (*pe)->determine_type(&subcontext); +} + +void +String_concat_expression::do_check_types(Gogo*) +{ + if (this->is_error_expression()) + return; + Type* t = this->exprs_->front()->type(); + if (t->is_error()) + { + this->set_is_error(); + return; + } + Expression_list::iterator pe = this->exprs_->begin(); + ++pe; + for (; pe != this->exprs_->end(); ++pe) + { + Type* t1 = (*pe)->type(); + if (!Type::are_compatible_for_binop(t, t1)) + { + this->report_error("incompatible types in binary expression"); + return; + } + if (!Binary_expression::check_operator_type(OPERATOR_PLUS, t, t1, + this->location())) + { + this->set_is_error(); + return; + } + } +} Expression* -Bound_method_expression::do_lower(Gogo*, Named_object*, - Statement_inserter* inserter, int) +String_concat_expression::do_flatten(Gogo*, Named_object*, + Statement_inserter*) { - // For simplicity we use a temporary for every call to an embedded - // method, even though some of them might be pure value methods and - // not require a temporary. - if (this->expr_->var_expression() == NULL - && this->expr_->temporary_reference_expression() == NULL - && this->expr_->set_and_use_temporary_expression() == NULL - && (this->method_->field_indexes() != NULL - || (this->method_->is_value_method() - && this->expr_->type()->points_to() != NULL))) + if (this->is_error_expression()) + return this; + Location loc = this->location(); + Type* type = this->type(); + Expression* nil_arg = Expression::make_nil(loc); + Expression* call; + switch (this->exprs_->size()) { - Temporary_statement* temp = - Statement::make_temporary(this->expr_->type(), NULL, this->location()); - inserter->insert(temp); - this->expr_ = Expression::make_set_and_use_temporary(temp, this->expr_, - this->location()); + case 0: case 1: + go_unreachable(); + + case 2: case 3: case 4: case 5: + { + Expression* len = Expression::make_integer_ul(this->exprs_->size(), + NULL, loc); + Array_type* arg_type = Type::make_array_type(type, len); + arg_type->set_is_array_incomparable(); + Expression* arg = + Expression::make_array_composite_literal(arg_type, this->exprs_, + loc); + Runtime::Function code; + switch (this->exprs_->size()) + { + default: + go_unreachable(); + case 2: + code = Runtime::CONCATSTRING2; + break; + case 3: + code = Runtime::CONCATSTRING3; + break; + case 4: + code = Runtime::CONCATSTRING4; + break; + case 5: + code = Runtime::CONCATSTRING5; + break; + } + call = Runtime::make_call(code, loc, 2, nil_arg, arg); + } + break; + + default: + { + Type* arg_type = Type::make_array_type(type, NULL); + Slice_construction_expression* sce = + Expression::make_slice_composite_literal(arg_type, this->exprs_, + loc); + sce->set_storage_does_not_escape(); + call = Runtime::make_call(Runtime::CONCATSTRINGS, loc, 2, nil_arg, + sce); + } + break; } - return this; + + return Expression::make_cast(type, call, loc); +} + +void +String_concat_expression::do_dump_expression( + Ast_dump_context* ast_dump_context) const +{ + ast_dump_context->ostream() << "concat("; + ast_dump_context->dump_expression_list(this->exprs_, false); + ast_dump_context->ostream() << ")"; +} + +Expression* +Expression::make_string_concat(Expression_list* exprs) +{ + return new String_concat_expression(exprs); +} + +// Class Bound_method_expression. + +// Traversal. + +int +Bound_method_expression::do_traverse(Traverse* traverse) +{ + return Expression::traverse(&this->expr_, traverse); } // Return the type of a bound method expression. The type of this @@ -6347,8 +6768,9 @@ Bound_method_expression::create_thunk(Gogo* gogo, const Method* method, sfl->push_back(Struct_field(Typed_identifier("val.1", orig_fntype->receiver()->type(), loc))); - Type* closure_type = Type::make_struct_type(sfl, loc); - closure_type = Type::make_pointer_type(closure_type); + Struct_type* st = Type::make_struct_type(sfl, loc); + st->set_is_struct_incomparable(); + Type* closure_type = Type::make_pointer_type(st); Function_type* new_fntype = orig_fntype->copy_with_names(); @@ -6440,32 +6862,43 @@ bme_check_nil(const Method::Field_indexes* field_indexes, Location loc, return cond; } -// Get the backend representation for a method value. +// Flatten a method value into a struct with nil checks. We can't do +// this in the lowering phase, because if the method value is called +// directly we don't need a thunk. That case will have been handled +// by Call_expression::do_lower, so if we get here then we do need a +// thunk. -Bexpression* -Bound_method_expression::do_get_backend(Translate_context* context) +Expression* +Bound_method_expression::do_flatten(Gogo* gogo, Named_object*, + Statement_inserter* inserter) { - Named_object* thunk = Bound_method_expression::create_thunk(context->gogo(), + Location loc = this->location(); + + Named_object* thunk = Bound_method_expression::create_thunk(gogo, this->method_, this->function_); if (thunk->is_erroneous()) { go_assert(saw_errors()); - return context->backend()->error_expression(); + return Expression::make_error(loc); } - // FIXME: We should lower this earlier, but we can't lower it in the - // lowering pass because at that point we don't know whether we need - // to create the thunk or not. If the expression is called, we - // don't need the thunk. - - Location loc = this->location(); + // Force the expression into a variable. This is only necessary if + // we are going to do nil checks below, but it's easy enough to + // always do it. + Expression* expr = this->expr_; + if (!expr->is_variable()) + { + Temporary_statement* etemp = Statement::make_temporary(NULL, expr, loc); + inserter->insert(etemp); + expr = Expression::make_temporary_reference(etemp, loc); + } // If the method expects a value, and we have a pointer, we need to // dereference the pointer. Named_object* fn = this->method_->named_object(); - Function_type* fntype; + Function_type *fntype; if (fn->is_function()) fntype = fn->func_value()->type(); else if (fn->is_function_declaration()) @@ -6473,7 +6906,7 @@ Bound_method_expression::do_get_backend(Translate_context* context) else go_unreachable(); - Expression* val = this->expr_; + Expression* val = expr; if (fntype->receiver()->type()->points_to() == NULL && val->type()->points_to() != NULL) val = Expression::make_unary(OPERATOR_MULT, val, loc); @@ -6491,23 +6924,35 @@ Bound_method_expression::do_get_backend(Translate_context* context) loc))); fields->push_back(Struct_field(Typed_identifier("val.1", val->type(), loc))); Struct_type* st = Type::make_struct_type(fields, loc); + st->set_is_struct_incomparable(); Expression_list* vals = new Expression_list(); vals->push_back(Expression::make_func_code_reference(thunk, loc)); vals->push_back(val); Expression* ret = Expression::make_struct_composite_literal(st, vals, loc); - ret = Expression::make_heap_expression(ret, loc); - // See whether the expression or any embedded pointers are nil. + if (!gogo->compiling_runtime() || gogo->package_name() != "runtime") + ret = Expression::make_heap_expression(ret, loc); + else + { + // When compiling the runtime, method closures do not escape. + // When escape analysis becomes the default, and applies to + // method closures, this should be changed to make it an error + // if a method closure escapes. + Temporary_statement* ctemp = Statement::make_temporary(st, ret, loc); + inserter->insert(ctemp); + ret = Expression::make_temporary_reference(ctemp, loc); + ret = Expression::make_unary(OPERATOR_AND, ret, loc); + ret->unary_expression()->set_does_not_escape(); + } + + // If necessary, check whether the expression or any embedded + // pointers are nil. Expression* nil_check = NULL; - Expression* expr = this->expr_; if (this->method_->field_indexes() != NULL) { - // Note that we are evaluating this->expr_ twice, but that is OK - // because in the lowering pass we forced it into a temporary - // variable. Expression* ref = expr; nil_check = bme_check_nil(this->method_->field_indexes(), loc, &ref); expr = ref; @@ -6524,19 +6969,20 @@ Bound_method_expression::do_get_backend(Translate_context* context) nil_check = Expression::make_binary(OPERATOR_OROR, nil_check, n, loc); } - Bexpression* bme = ret->get_backend(context); if (nil_check != NULL) { - Gogo* gogo = context->gogo(); - Bexpression* crash = - gogo->runtime_error(RUNTIME_ERROR_NIL_DEREFERENCE, - loc)->get_backend(context); - Btype* btype = ret->type()->get_backend(gogo); - Bexpression* bcheck = nil_check->get_backend(context); - bme = gogo->backend()->conditional_expression(btype, bcheck, crash, - bme, loc); + Expression* crash = gogo->runtime_error(RUNTIME_ERROR_NIL_DEREFERENCE, + loc); + // Fix the type of the conditional expression by pretending to + // evaluate to RET either way through the conditional. + crash = Expression::make_compound(crash, ret, loc); + ret = Expression::make_conditional(nil_check, crash, ret, loc); } - return bme; + + // RET is a pointer to a struct, but we want a function type. + ret = Expression::make_unsafe_cast(this->type(), ret, loc); + + return ret; } // Dump ast representation of a bound method expression. @@ -6659,10 +7105,12 @@ class Builtin_call_expression : public Call_expression complex_type(Type*); Expression* - lower_make(); + lower_make(Statement_inserter*); + + Expression* flatten_append(Gogo*, Named_object*, Statement_inserter*); bool - check_int_value(Expression*, bool is_length); + check_int_value(Expression*, bool is_length, bool* small); // A pointer back to the general IR structure. This avoids a global // variable, or passing it around everywhere. @@ -6760,7 +7208,7 @@ Builtin_call_expression::do_set_recover_arg(Expression* arg) // specific expressions. We also convert to a constant if we can. Expression* -Builtin_call_expression::do_lower(Gogo* gogo, Named_object* function, +Builtin_call_expression::do_lower(Gogo*, Named_object* function, Statement_inserter* inserter, int) { if (this->is_error_expression()) @@ -6828,7 +7276,7 @@ Builtin_call_expression::do_lower(Gogo* gogo, Named_object* function, Expression* arg = args->front(); if (!arg->is_type_expression()) { - error_at(arg->location(), "expected type"); + go_error_at(arg->location(), "expected type"); this->set_is_error(); } else @@ -6838,7 +7286,7 @@ Builtin_call_expression::do_lower(Gogo* gogo, Named_object* function, break; case BUILTIN_MAKE: - return this->lower_make(); + return this->lower_make(inserter); case BUILTIN_RECOVER: if (function != NULL) @@ -6852,30 +7300,6 @@ Builtin_call_expression::do_lower(Gogo* gogo, Named_object* function, } break; - case 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_slice_type()) - { - if (slice_type->is_nil_type()) - error_at(args->front()->location(), "use of untyped nil"); - else - error_at(args->front()->location(), - "argument 1 must be a slice"); - this->set_is_error(); - return this; - } - Type* element_type = slice_type->array_type()->element_type(); - this->lower_varargs(gogo, function, inserter, - Type::make_array_type(element_type, NULL), - 2); - } - break; - case BUILTIN_DELETE: { // Lower to a runtime function call. @@ -6901,16 +7325,37 @@ Builtin_call_expression::do_lower(Gogo* gogo, Named_object* function, Statement::make_temporary(mt->key_type(), args->back(), loc); inserter->insert(key_temp); - Expression* e1 = Expression::make_temporary_reference(map_temp, + Expression* e1 = Expression::make_type_descriptor(mt, loc); + Expression* e2 = Expression::make_temporary_reference(map_temp, loc); - Expression* e2 = Expression::make_temporary_reference(key_temp, + Expression* e3 = Expression::make_temporary_reference(key_temp, loc); - e2 = Expression::make_unary(OPERATOR_AND, e2, loc); + e3 = Expression::make_unary(OPERATOR_AND, e3, loc); return Runtime::make_call(Runtime::MAPDELETE, this->location(), - 2, e1, e2); + 3, e1, e2, e3); } } break; + + case BUILTIN_PRINT: + case BUILTIN_PRINTLN: + // Force all the arguments into temporary variables, so that we + // don't try to evaluate something while holding the print lock. + if (this->args() == NULL) + break; + for (Expression_list::iterator pa = this->args()->begin(); + pa != this->args()->end(); + ++pa) + { + if (!(*pa)->is_variable() && !(*pa)->is_constant()) + { + Temporary_statement* temp = + Statement::make_temporary(NULL, *pa, loc); + inserter->insert(temp); + *pa = Expression::make_temporary_reference(temp, loc); + } + } + break; } return this; @@ -6920,7 +7365,7 @@ Builtin_call_expression::do_lower(Gogo* gogo, Named_object* function, // append into temporary expressions. Expression* -Builtin_call_expression::do_flatten(Gogo*, Named_object*, +Builtin_call_expression::do_flatten(Gogo* gogo, Named_object* function, Statement_inserter* inserter) { Location loc = this->location(); @@ -6931,6 +7376,8 @@ Builtin_call_expression::do_flatten(Gogo*, Named_object*, break; case BUILTIN_APPEND: + return this->flatten_append(gogo, function, inserter); + case BUILTIN_COPY: { Type* at = this->args()->front()->type(); @@ -6968,6 +7415,23 @@ Builtin_call_expression::do_flatten(Gogo*, Named_object*, *pa = Expression::make_temporary_reference(temp, loc); } } + break; + + case BUILTIN_LEN: + case BUILTIN_CAP: + { + Expression_list::iterator pa = this->args()->begin(); + if (!(*pa)->is_variable() + && ((*pa)->type()->map_type() != NULL + || (*pa)->type()->channel_type() != NULL)) + { + Temporary_statement* temp = + Statement::make_temporary(NULL, *pa, loc); + inserter->insert(temp); + *pa = Expression::make_temporary_reference(temp, loc); + } + } + break; } return this; @@ -6976,7 +7440,7 @@ Builtin_call_expression::do_flatten(Gogo*, Named_object*, // Lower a make expression. Expression* -Builtin_call_expression::lower_make() +Builtin_call_expression::lower_make(Statement_inserter* inserter) { Location loc = this->location(); @@ -6992,7 +7456,7 @@ Builtin_call_expression::lower_make() Expression* first_arg = *parg; if (!first_arg->is_type_expression()) { - error_at(first_arg->location(), "expected type"); + go_error_at(first_arg->location(), "expected type"); this->set_is_error(); return Expression::make_error(this->location()); } @@ -7013,14 +7477,11 @@ Builtin_call_expression::lower_make() return Expression::make_error(this->location()); } - bool have_big_args = false; - Type* uintptr_type = Type::lookup_integer_type("uintptr"); - int uintptr_bits = uintptr_type->integer_type()->bits(); - Type_context int_context(Type::lookup_integer_type("int"), false); ++parg; Expression* len_arg; + bool len_small = false; if (parg == args->end()) { if (is_slice) @@ -7034,20 +7495,18 @@ Builtin_call_expression::lower_make() { len_arg = *parg; len_arg->determine_type(&int_context); - if (!this->check_int_value(len_arg, true)) + if (!this->check_int_value(len_arg, true, &len_small)) return Expression::make_error(this->location()); - if (len_arg->type()->integer_type() != NULL - && len_arg->type()->integer_type()->bits() > uintptr_bits) - have_big_args = true; ++parg; } Expression* cap_arg = NULL; + bool cap_small = false; if (is_slice && parg != args->end()) { cap_arg = *parg; cap_arg->determine_type(&int_context); - if (!this->check_int_value(cap_arg, false)) + if (!this->check_int_value(cap_arg, false, &cap_small)) return Expression::make_error(this->location()); Numeric_constant nclen; @@ -7064,9 +7523,6 @@ Builtin_call_expression::lower_make() return Expression::make_error(this->location()); } - if (cap_arg->type()->integer_type() != NULL - && cap_arg->type()->integer_type()->bits() > uintptr_bits) - have_big_args = true; ++parg; } @@ -7077,51 +7533,250 @@ Builtin_call_expression::lower_make() } Location type_loc = first_arg->location(); - Expression* type_arg; - if (is_slice || is_chan) - type_arg = Expression::make_type_descriptor(type, type_loc); - else if (is_map) - type_arg = Expression::make_map_descriptor(type->map_type(), type_loc); - else - go_unreachable(); Expression* call; if (is_slice) { + Type* et = type->array_type()->element_type(); + Expression* type_arg = Expression::make_type_descriptor(et, type_loc); if (cap_arg == NULL) - call = Runtime::make_call((have_big_args - ? Runtime::MAKESLICE1BIG - : Runtime::MAKESLICE1), - loc, 2, type_arg, len_arg); - else - call = Runtime::make_call((have_big_args - ? Runtime::MAKESLICE2BIG - : Runtime::MAKESLICE2), - loc, 3, type_arg, len_arg, cap_arg); + { + Temporary_statement* temp = Statement::make_temporary(NULL, + len_arg, + loc); + inserter->insert(temp); + len_arg = Expression::make_temporary_reference(temp, loc); + cap_arg = Expression::make_temporary_reference(temp, loc); + cap_small = len_small; + } + + Runtime::Function code = Runtime::MAKESLICE; + if (!len_small || !cap_small) + code = Runtime::MAKESLICE64; + call = Runtime::make_call(code, loc, 3, type_arg, len_arg, cap_arg); } else if (is_map) - call = Runtime::make_call((have_big_args - ? Runtime::MAKEMAPBIG - : Runtime::MAKEMAP), - loc, 2, type_arg, len_arg); + { + Expression* type_arg = Expression::make_type_descriptor(type, type_loc); + call = Runtime::make_call(Runtime::MAKEMAP, loc, 4, type_arg, len_arg, + Expression::make_nil(loc), + Expression::make_nil(loc)); + } else if (is_chan) - call = Runtime::make_call((have_big_args - ? Runtime::MAKECHANBIG - : Runtime::MAKECHAN), - loc, 2, type_arg, len_arg); + { + Expression* type_arg = Expression::make_type_descriptor(type, type_loc); + call = Runtime::make_call(Runtime::MAKECHAN, loc, 2, type_arg, len_arg); + } else go_unreachable(); return Expression::make_unsafe_cast(type, call, loc); } +// Flatten a call to the predeclared append function. We do this in +// the flatten phase, not the lowering phase, so that we run after +// type checking and after order_evaluations. + +Expression* +Builtin_call_expression::flatten_append(Gogo* gogo, Named_object* function, + Statement_inserter* inserter) +{ + if (this->is_error_expression()) + return this; + + Location loc = this->location(); + + const Expression_list* args = this->args(); + go_assert(args != NULL && !args->empty()); + + Type* slice_type = args->front()->type(); + go_assert(slice_type->is_slice_type()); + Type* element_type = slice_type->array_type()->element_type(); + + if (args->size() == 1) + { + // append(s) evaluates to s. + return args->front(); + } + + Type* int_type = Type::lookup_integer_type("int"); + Type* uint_type = Type::lookup_integer_type("uint"); + + // Implementing + // append(s1, s2...) + // or + // append(s1, a1, a2, a3, ...) + + // s1tmp := s1 + Temporary_statement* s1tmp = Statement::make_temporary(NULL, args->front(), + loc); + inserter->insert(s1tmp); + + // l1tmp := len(s1tmp) + Named_object* lenfn = gogo->lookup_global("len"); + Expression* lenref = Expression::make_func_reference(lenfn, NULL, loc); + Expression_list* call_args = new Expression_list(); + call_args->push_back(Expression::make_temporary_reference(s1tmp, loc)); + Expression* len = Expression::make_call(lenref, call_args, false, loc); + gogo->lower_expression(function, inserter, &len); + gogo->flatten_expression(function, inserter, &len); + Temporary_statement* l1tmp = Statement::make_temporary(int_type, len, loc); + inserter->insert(l1tmp); + + Temporary_statement* s2tmp = NULL; + Temporary_statement* l2tmp = NULL; + Expression_list* add = NULL; + Expression* len2; + if (this->is_varargs()) + { + go_assert(args->size() == 2); + + // s2tmp := s2 + s2tmp = Statement::make_temporary(NULL, args->back(), loc); + inserter->insert(s2tmp); + + // l2tmp := len(s2tmp) + lenref = Expression::make_func_reference(lenfn, NULL, loc); + call_args = new Expression_list(); + call_args->push_back(Expression::make_temporary_reference(s2tmp, loc)); + len = Expression::make_call(lenref, call_args, false, loc); + gogo->lower_expression(function, inserter, &len); + gogo->flatten_expression(function, inserter, &len); + l2tmp = Statement::make_temporary(int_type, len, loc); + inserter->insert(l2tmp); + + // len2 = l2tmp + len2 = Expression::make_temporary_reference(l2tmp, loc); + } + else + { + // We have to ensure that all the arguments are in variables + // now, because otherwise if one of them is an index expression + // into the current slice we could overwrite it before we fetch + // it. + add = new Expression_list(); + Expression_list::const_iterator pa = args->begin(); + for (++pa; pa != args->end(); ++pa) + { + if ((*pa)->is_variable()) + add->push_back(*pa); + else + { + Temporary_statement* tmp = Statement::make_temporary(NULL, *pa, + loc); + inserter->insert(tmp); + add->push_back(Expression::make_temporary_reference(tmp, loc)); + } + } + + // len2 = len(add) + len2 = Expression::make_integer_ul(add->size(), int_type, loc); + } + + // ntmp := l1tmp + len2 + Expression* ref = Expression::make_temporary_reference(l1tmp, loc); + Expression* sum = Expression::make_binary(OPERATOR_PLUS, ref, len2, loc); + gogo->lower_expression(function, inserter, &sum); + gogo->flatten_expression(function, inserter, &sum); + Temporary_statement* ntmp = Statement::make_temporary(int_type, sum, loc); + inserter->insert(ntmp); + + // s1tmp = uint(ntmp) > uint(cap(s1tmp)) ? + // growslice(type, s1tmp, ntmp) : + // s1tmp[:ntmp] + // Using uint here means that if the computation of ntmp overflowed, + // we will call growslice which will panic. + + Expression* left = Expression::make_temporary_reference(ntmp, loc); + left = Expression::make_cast(uint_type, left, loc); + + Named_object* capfn = gogo->lookup_global("cap"); + Expression* capref = Expression::make_func_reference(capfn, NULL, loc); + call_args = new Expression_list(); + call_args->push_back(Expression::make_temporary_reference(s1tmp, loc)); + Expression* right = Expression::make_call(capref, call_args, false, loc); + right = Expression::make_cast(uint_type, right, loc); + + Expression* cond = Expression::make_binary(OPERATOR_GT, left, right, loc); + + Expression* a1 = Expression::make_type_descriptor(element_type, loc); + Expression* a2 = Expression::make_temporary_reference(s1tmp, loc); + Expression* a3 = Expression::make_temporary_reference(ntmp, loc); + Expression* call = Runtime::make_call(Runtime::GROWSLICE, loc, 3, + a1, a2, a3); + call = Expression::make_unsafe_cast(slice_type, call, loc); + + ref = Expression::make_temporary_reference(s1tmp, loc); + Expression* zero = Expression::make_integer_ul(0, int_type, loc); + Expression* ref2 = Expression::make_temporary_reference(ntmp, loc); + // FIXME: Mark this index as not requiring bounds checks. + ref = Expression::make_index(ref, zero, ref2, NULL, loc); + + Expression* rhs = Expression::make_conditional(cond, call, ref, loc); + + gogo->lower_expression(function, inserter, &rhs); + gogo->flatten_expression(function, inserter, &rhs); + + Expression* lhs = Expression::make_temporary_reference(s1tmp, loc); + Statement* assign = Statement::make_assignment(lhs, rhs, loc); + inserter->insert(assign); + + if (this->is_varargs()) + { + // copy(s1tmp[l1tmp:], s2tmp) + a1 = Expression::make_temporary_reference(s1tmp, loc); + ref = Expression::make_temporary_reference(l1tmp, loc); + Expression* nil = Expression::make_nil(loc); + // FIXME: Mark this index as not requiring bounds checks. + a1 = Expression::make_index(a1, ref, nil, NULL, loc); + + a2 = Expression::make_temporary_reference(s2tmp, loc); + + Named_object* copyfn = gogo->lookup_global("copy"); + Expression* copyref = Expression::make_func_reference(copyfn, NULL, loc); + call_args = new Expression_list(); + call_args->push_back(a1); + call_args->push_back(a2); + call = Expression::make_call(copyref, call_args, false, loc); + gogo->lower_expression(function, inserter, &call); + gogo->flatten_expression(function, inserter, &call); + inserter->insert(Statement::make_statement(call, false)); + } + else + { + // For each argument: + // s1tmp[l1tmp+i] = a + unsigned long i = 0; + for (Expression_list::const_iterator pa = add->begin(); + pa != add->end(); + ++pa, ++i) + { + ref = Expression::make_temporary_reference(s1tmp, loc); + ref2 = Expression::make_temporary_reference(l1tmp, loc); + Expression* off = Expression::make_integer_ul(i, int_type, loc); + ref2 = Expression::make_binary(OPERATOR_PLUS, ref2, off, loc); + // FIXME: Mark this index as not requiring bounds checks. + lhs = Expression::make_index(ref, ref2, NULL, NULL, loc); + gogo->lower_expression(function, inserter, &lhs); + gogo->flatten_expression(function, inserter, &lhs); + assign = Statement::make_assignment(lhs, *pa, loc); + inserter->insert(assign); + } + } + + return Expression::make_temporary_reference(s1tmp, loc); +} + // Return whether an expression has an integer value. Report an error // if not. This is used when handling calls to the predeclared make -// function. +// function. Set *SMALL if the value is known to fit in type "int". bool -Builtin_call_expression::check_int_value(Expression* e, bool is_length) +Builtin_call_expression::check_int_value(Expression* e, bool is_length, + bool *small) { + *small = false; + Numeric_constant nc; if (e->numeric_constant_value(&nc)) { @@ -7131,12 +7786,12 @@ Builtin_call_expression::check_int_value(Expression* e, bool is_length) case Numeric_constant::NC_UL_VALID: break; case Numeric_constant::NC_UL_NOTINT: - error_at(e->location(), "non-integer %s argument to make", - is_length ? "len" : "cap"); + go_error_at(e->location(), "non-integer %s argument to make", + is_length ? "len" : "cap"); return false; case Numeric_constant::NC_UL_NEGATIVE: - error_at(e->location(), "negative %s argument to make", - is_length ? "len" : "cap"); + go_error_at(e->location(), "negative %s argument to make", + is_length ? "len" : "cap"); return false; case Numeric_constant::NC_UL_BIG: // We don't want to give a compile-time error for a 64-bit @@ -7152,19 +7807,30 @@ Builtin_call_expression::check_int_value(Expression* e, bool is_length) Type* int_type = Type::lookup_integer_type("int"); if (bits >= int_type->integer_type()->bits()) { - error_at(e->location(), "%s argument too large for make", - is_length ? "len" : "cap"); + go_error_at(e->location(), "%s argument too large for make", + is_length ? "len" : "cap"); return false; } + *small = true; return true; } if (e->type()->integer_type() != NULL) - return true; + { + int ebits = e->type()->integer_type()->bits(); + int intbits = Type::lookup_integer_type("int")->integer_type()->bits(); - error_at(e->location(), "non-integer %s argument to make", - is_length ? "len" : "cap"); + // We can treat ebits == intbits as small even for an unsigned + // integer type, because we will convert the value to int and + // then reject it in the runtime if it is negative. + *small = ebits <= intbits; + + return true; + } + + go_error_at(e->location(), "non-integer %s argument to make", + is_length ? "len" : "cap"); return false; } @@ -7694,6 +8360,7 @@ Builtin_call_expression::do_determine_type(const Type_context* context) bool is_print; Type* arg_type = NULL; + Type* trailing_arg_types = NULL; switch (this->code_) { case BUILTIN_PRINT: @@ -7730,6 +8397,16 @@ Builtin_call_expression::do_determine_type(const Type_context* context) } break; + case BUILTIN_APPEND: + if (!this->is_varargs() + && args != NULL + && !args->empty() + && args->front()->type()->is_slice_type()) + trailing_arg_types = + args->front()->type()->array_type()->element_type(); + is_print = false; + break; + default: is_print = false; break; @@ -7786,6 +8463,12 @@ Builtin_call_expression::do_determine_type(const Type_context* context) } (*pa)->determine_type(&subcontext); + + if (trailing_arg_types != NULL) + { + arg_type = trailing_arg_types; + trailing_arg_types = NULL; + } } } } @@ -7872,7 +8555,7 @@ Builtin_call_expression::do_check_types(Gogo*) if (args == NULL) { if (this->code_ == BUILTIN_PRINT) - warning_at(this->location(), 0, + go_warning_at(this->location(), 0, "no arguments for builtin function %<%s%>", (this->code_ == BUILTIN_PRINT ? "print" @@ -7992,53 +8675,102 @@ Builtin_call_expression::do_check_types(Gogo*) case BUILTIN_APPEND: { const Expression_list* args = this->args(); - if (args == NULL || args->size() < 2) + if (args == NULL || args->empty()) { this->report_error(_("not enough arguments")); break; } - if (args->size() > 2) - { - this->report_error(_("too many arguments")); - break; - } - if (args->front()->type()->is_error() - || args->back()->type()->is_error()) + + Type* slice_type = args->front()->type(); + if (!slice_type->is_slice_type()) { + if (slice_type->is_error_type()) + break; + if (slice_type->is_nil_type()) + go_error_at(args->front()->location(), "use of untyped nil"); + else + go_error_at(args->front()->location(), + "argument 1 must be a slice"); this->set_is_error(); break; } - Array_type* at = args->front()->type()->array_type(); - Type* e = at->element_type(); - - // The language permits appending a string to a []byte, as a - // special case. - if (args->back()->type()->is_string_type()) + Type* element_type = slice_type->array_type()->element_type(); + if (this->is_varargs()) { - if (e->integer_type() != NULL && e->integer_type()->is_byte()) - break; - } + if (!args->back()->type()->is_slice_type() + && !args->back()->type()->is_string_type()) + { + go_error_at(args->back()->location(), + "invalid use of %<...%> with non-slice/non-string"); + this->set_is_error(); + break; + } - // The language says that the second argument must be - // assignable to a slice of the element type of the first - // argument. We already know the first argument is a slice - // type. - Type* arg2_type = Type::make_array_type(e, NULL); - std::string reason; - if (!Type::are_assignable(arg2_type, args->back()->type(), &reason)) - { - if (reason.empty()) - this->report_error(_("argument 2 has invalid type")); + if (args->size() < 2) + { + this->report_error(_("not enough arguments")); + break; + } + if (args->size() > 2) + { + this->report_error(_("too many arguments")); + break; + } + + if (args->back()->type()->is_string_type() + && element_type->integer_type() != NULL + && element_type->integer_type()->is_byte()) + { + // Permit append(s1, s2...) when s1 is a slice of + // bytes and s2 is a string type. + } else { - error_at(this->location(), "argument 2 has invalid type (%s)", - reason.c_str()); - this->set_is_error(); + // We have to test for assignment compatibility to a + // slice of the element type, which is not necessarily + // the same as the type of the first argument: the + // first argument might have a named type. + Type* check_type = Type::make_array_type(element_type, NULL); + std::string reason; + if (!Type::are_assignable(check_type, args->back()->type(), + &reason)) + { + if (reason.empty()) + go_error_at(args->back()->location(), + "argument 2 has invalid type"); + else + go_error_at(args->back()->location(), + "argument 2 has invalid type (%s)", + reason.c_str()); + this->set_is_error(); + break; + } + } + } + else + { + Expression_list::const_iterator pa = args->begin(); + int i = 2; + for (++pa; pa != args->end(); ++pa, ++i) + { + std::string reason; + if (!Type::are_assignable(element_type, (*pa)->type(), + &reason)) + { + if (reason.empty()) + go_error_at((*pa)->location(), + "argument %d has incompatible type", i); + else + go_error_at((*pa)->location(), + "argument %d has incompatible type (%s)", + i, reason.c_str()); + this->set_is_error(); + } } } - break; } + break; case BUILTIN_REAL: case BUILTIN_IMAG: @@ -8153,10 +8885,23 @@ Builtin_call_expression::do_get_backend(Translate_context* context) val = arg_type->array_type()->get_length(gogo, arg); this->seen_ = false; } - else if (arg_type->map_type() != NULL) - val = Runtime::make_call(Runtime::MAP_LEN, location, 1, arg); - else if (arg_type->channel_type() != NULL) - val = Runtime::make_call(Runtime::CHAN_LEN, location, 1, arg); + else if (arg_type->map_type() != NULL + || arg_type->channel_type() != NULL) + { + // The first field is the length. If the pointer is + // nil, the length is zero. + Type* pint_type = Type::make_pointer_type(int_type); + arg = Expression::make_unsafe_cast(pint_type, arg, location); + Expression* nil = Expression::make_nil(location); + nil = Expression::make_cast(pint_type, nil, location); + Expression* cmp = Expression::make_binary(OPERATOR_EQEQ, + arg, nil, location); + Expression* zero = Expression::make_integer_ul(0, int_type, + location); + Expression* indir = Expression::make_unary(OPERATOR_MULT, + arg, location); + val = Expression::make_conditional(cmp, zero, indir, location); + } else go_unreachable(); } @@ -8174,7 +8919,31 @@ Builtin_call_expression::do_get_backend(Translate_context* context) this->seen_ = false; } else if (arg_type->channel_type() != NULL) - val = Runtime::make_call(Runtime::CHAN_CAP, location, 1, arg); + { + // The second field is the capacity. If the pointer + // is nil, the capacity is zero. + Type* uintptr_type = Type::lookup_integer_type("uintptr"); + Type* pint_type = Type::make_pointer_type(int_type); + Expression* parg = Expression::make_unsafe_cast(uintptr_type, + arg, + location); + int off = int_type->integer_type()->bits() / 8; + Expression* eoff = Expression::make_integer_ul(off, + uintptr_type, + location); + parg = Expression::make_binary(OPERATOR_PLUS, parg, eoff, + location); + parg = Expression::make_unsafe_cast(pint_type, parg, location); + Expression* nil = Expression::make_nil(location); + nil = Expression::make_cast(pint_type, nil, location); + Expression* cmp = Expression::make_binary(OPERATOR_EQEQ, + arg, nil, location); + Expression* zero = Expression::make_integer_ul(0, int_type, + location); + Expression* indir = Expression::make_unary(OPERATOR_MULT, + parg, location); + val = Expression::make_conditional(cmp, zero, indir, location); + } else go_unreachable(); } @@ -8187,7 +8956,9 @@ Builtin_call_expression::do_get_backend(Translate_context* context) case BUILTIN_PRINTLN: { const bool is_ln = this->code_ == BUILTIN_PRINTLN; - Expression* print_stmts = NULL; + + Expression* print_stmts = Runtime::make_call(Runtime::PRINTLOCK, + location, 0); const Expression_list* call_args = this->args(); if (call_args != NULL) @@ -8199,8 +8970,7 @@ Builtin_call_expression::do_get_backend(Translate_context* context) if (is_ln && p != call_args->begin()) { Expression* print_space = - Runtime::make_call(Runtime::PRINT_SPACE, - this->location(), 0); + Runtime::make_call(Runtime::PRINTSP, location, 0); print_stmts = Expression::make_compound(print_stmts, print_space, @@ -8211,51 +8981,51 @@ Builtin_call_expression::do_get_backend(Translate_context* context) Type* type = arg->type(); Runtime::Function code; if (type->is_string_type()) - code = Runtime::PRINT_STRING; + code = Runtime::PRINTSTRING; else if (type->integer_type() != NULL && type->integer_type()->is_unsigned()) { Type* itype = Type::lookup_integer_type("uint64"); arg = Expression::make_cast(itype, arg, location); - code = Runtime::PRINT_UINT64; + code = Runtime::PRINTUINT; } else if (type->integer_type() != NULL) { Type* itype = Type::lookup_integer_type("int64"); arg = Expression::make_cast(itype, arg, location); - code = Runtime::PRINT_INT64; + code = Runtime::PRINTINT; } else if (type->float_type() != NULL) { Type* dtype = Type::lookup_float_type("float64"); arg = Expression::make_cast(dtype, arg, location); - code = Runtime::PRINT_DOUBLE; + code = Runtime::PRINTFLOAT; } else if (type->complex_type() != NULL) { Type* ctype = Type::lookup_complex_type("complex128"); arg = Expression::make_cast(ctype, arg, location); - code = Runtime::PRINT_COMPLEX; + code = Runtime::PRINTCOMPLEX; } else if (type->is_boolean_type()) - code = Runtime::PRINT_BOOL; + code = Runtime::PRINTBOOL; else if (type->points_to() != NULL || type->channel_type() != NULL || type->map_type() != NULL || type->function_type() != NULL) { arg = Expression::make_cast(type, arg, location); - code = Runtime::PRINT_POINTER; + code = Runtime::PRINTPOINTER; } else if (type->interface_type() != NULL) { if (type->interface_type()->is_empty()) - code = Runtime::PRINT_EMPTY_INTERFACE; + code = Runtime::PRINTEFACE; else - code = Runtime::PRINT_INTERFACE; + code = Runtime::PRINTIFACE; } else if (type->is_slice_type()) - code = Runtime::PRINT_SLICE; + code = Runtime::PRINTSLICE; else { go_assert(saw_errors()); @@ -8263,30 +9033,22 @@ Builtin_call_expression::do_get_backend(Translate_context* context) } Expression* call = Runtime::make_call(code, location, 1, arg); - if (print_stmts == NULL) - print_stmts = call; - else - print_stmts = Expression::make_compound(print_stmts, call, - location); + print_stmts = Expression::make_compound(print_stmts, call, + location); } } if (is_ln) { Expression* print_nl = - Runtime::make_call(Runtime::PRINT_NL, location, 0); - if (print_stmts == NULL) - print_stmts = print_nl; - else - print_stmts = Expression::make_compound(print_stmts, print_nl, - location); + Runtime::make_call(Runtime::PRINTNL, location, 0); + print_stmts = Expression::make_compound(print_stmts, print_nl, + location); } - // There aren't any arguments to the print builtin. The compiler - // issues a warning for this so we should avoid getting the backend - // representation for this call. Instead, perform a no-op. - if (print_stmts == NULL) - return context->backend()->boolean_constant_expression(false); + Expression* unlock = Runtime::make_call(Runtime::PRINTUNLOCK, + location, 0); + print_stmts = Expression::make_compound(print_stmts, unlock, location); return print_stmts->get_backend(context); } @@ -8301,7 +9063,7 @@ Builtin_call_expression::do_get_backend(Translate_context* context) arg = Expression::convert_for_assignment(gogo, empty, arg, location); Expression* panic = - Runtime::make_call(Runtime::PANIC, location, 1, arg); + Runtime::make_call(Runtime::GOPANIC, location, 1, arg); return panic->get_backend(context); } @@ -8322,8 +9084,8 @@ Builtin_call_expression::do_get_backend(Translate_context* context) // because it changes whether it can recover a panic or not. // See test7 in test/recover1.go. Expression* recover = Runtime::make_call((this->is_deferred() - ? Runtime::DEFERRED_RECOVER - : Runtime::RECOVER), + ? Runtime::DEFERREDRECOVER + : Runtime::GORECOVER), location, 0); Expression* cond = Expression::make_conditional(arg, recover, nil, location); @@ -8371,97 +9133,39 @@ Builtin_call_expression::do_get_backend(Translate_context* context) Type* arg1_type = arg1->type(); Array_type* at = arg1_type->array_type(); go_assert(arg1->is_variable()); - Expression* arg1_val = at->get_value_pointer(gogo, arg1); - Expression* arg1_len = at->get_length(gogo, arg1); + + Expression* call; Type* arg2_type = arg2->type(); go_assert(arg2->is_variable()); - Expression* arg2_val; - Expression* arg2_len; - if (arg2_type->is_slice_type()) - { - at = arg2_type->array_type(); - arg2_val = at->get_value_pointer(gogo, arg2); - arg2_len = at->get_length(gogo, arg2); - } + if (arg2_type->is_string_type()) + call = Runtime::make_call(Runtime::SLICESTRINGCOPY, location, + 2, arg1, arg2); else { - go_assert(arg2->is_variable()); - arg2_val = Expression::make_string_info(arg2, STRING_INFO_DATA, - location); - arg2_len = Expression::make_string_info(arg2, STRING_INFO_LENGTH, - location); + Type* et = at->element_type(); + if (et->has_pointer()) + { + Expression* td = Expression::make_type_descriptor(et, + location); + call = Runtime::make_call(Runtime::TYPEDSLICECOPY, location, + 3, td, arg1, arg2); + } + else + { + Expression* sz = Expression::make_type_info(et, + TYPE_INFO_SIZE); + call = Runtime::make_call(Runtime::SLICECOPY, location, 3, + arg1, arg2, sz); + } } - Expression* cond = - Expression::make_binary(OPERATOR_LT, arg1_len, arg2_len, location); - Expression* length = - Expression::make_conditional(cond, arg1_len, arg2_len, location); - - Type* element_type = at->element_type(); - int64_t element_size; - bool ok = element_type->backend_type_size(gogo, &element_size); - if (!ok) - { - go_assert(saw_errors()); - return gogo->backend()->error_expression(); - } - Expression* size_expr = Expression::make_integer_int64(element_size, - length->type(), - location); - Expression* bytecount = - Expression::make_binary(OPERATOR_MULT, size_expr, length, location); - Expression* copy = Runtime::make_call(Runtime::COPY, location, 3, - arg1_val, arg2_val, bytecount); - - Expression* compound = Expression::make_compound(copy, length, location); - return compound->get_backend(context); + return call->get_backend(context); } case BUILTIN_APPEND: - { - const Expression_list* args = this->args(); - go_assert(args != NULL && args->size() == 2); - Expression* arg1 = args->front(); - Expression* arg2 = args->back(); - - Array_type* at = arg1->type()->array_type(); - Type* element_type = at->element_type()->forwarded(); - - go_assert(arg2->is_variable()); - Expression* arg2_val; - Expression* arg2_len; - int64_t size; - if (arg2->type()->is_string_type() - && element_type->integer_type() != NULL - && element_type->integer_type()->is_byte()) - { - arg2_val = Expression::make_string_info(arg2, STRING_INFO_DATA, - location); - arg2_len = Expression::make_string_info(arg2, STRING_INFO_LENGTH, - location); - size = 1; - } - else - { - arg2_val = at->get_value_pointer(gogo, arg2); - arg2_len = at->get_length(gogo, arg2); - bool ok = element_type->backend_type_size(gogo, &size); - if (!ok) - { - go_assert(saw_errors()); - return gogo->backend()->error_expression(); - } - } - Expression* element_size = - Expression::make_integer_int64(size, NULL, location); - - Expression* append = Runtime::make_call(Runtime::APPEND, location, 4, - arg1, arg2_val, arg2_len, - element_size); - append = Expression::make_unsafe_cast(arg1->type(), append, location); - return append->get_backend(context); - } + // Handled in Builtin_call_expression::flatten_append. + go_unreachable(); case BUILTIN_REAL: case BUILTIN_IMAG: @@ -8501,7 +9205,7 @@ Builtin_call_expression::do_export(Export* exp) const Numeric_constant nc; if (!this->numeric_constant_value(&nc)) { - error_at(this->location(), "value is not constant"); + go_error_at(this->location(), "value is not constant"); return; } @@ -8677,8 +9381,8 @@ Call_expression::do_lower(Gogo* gogo, Named_object* function, // the ellipsis operator should be applied to. If we unpack the // the call into its individual results here, the ellipsis will be // applied to the last result. - error_at(call->location(), - _("multiple-value argument in single-value context")); + go_error_at(call->location(), + _("multiple-value argument in single-value context")); return Expression::make_error(call->location()); } @@ -8727,7 +9431,7 @@ Call_expression::do_lower(Gogo* gogo, Named_object* function, go_assert(parameters != NULL && !parameters->empty()); Type* varargs_type = parameters->back().type(); this->lower_varargs(gogo, function, inserter, varargs_type, - parameters->size()); + parameters->size(), SLICE_STORAGE_MAY_ESCAPE); } // If this is call to a method, call the method directly passing the @@ -8780,6 +9484,45 @@ Call_expression::do_lower(Gogo* gogo, Named_object* function, bme->location()); } + // Handle a couple of special runtime functions. In the runtime + // package, getcallerpc returns the PC of the caller, and + // getcallersp returns the frame pointer of the caller. Implement + // these by turning them into calls to GCC builtin functions. We + // could implement them in normal code, but then we would have to + // explicitly unwind the stack. These functions are intended to be + // efficient. Note that this technique obviously only works for + // direct calls, but that is the only way they are used. The actual + // argument to these functions is always the address of a parameter; + // we don't need that for the GCC builtin functions, so we just + // ignore it. + if (gogo->compiling_runtime() + && this->args_ != NULL + && this->args_->size() == 1 + && gogo->package_name() == "runtime") + { + Func_expression* fe = this->fn_->func_expression(); + if (fe != NULL + && fe->named_object()->is_function_declaration() + && fe->named_object()->package() == NULL) + { + std::string n = Gogo::unpack_hidden_name(fe->named_object()->name()); + if (n == "getcallerpc") + { + static Named_object* builtin_return_address; + return this->lower_to_builtin(&builtin_return_address, + "__builtin_return_address", + 0); + } + else if (n == "getcallersp") + { + static Named_object* builtin_frame_address; + return this->lower_to_builtin(&builtin_frame_address, + "__builtin_frame_address", + 1); + } + } + } + return this; } @@ -8793,7 +9536,8 @@ Call_expression::do_lower(Gogo* gogo, Named_object* function, void Call_expression::lower_varargs(Gogo* gogo, Named_object* function, Statement_inserter* inserter, - Type* varargs_type, size_t param_count) + Type* varargs_type, size_t param_count, + Slice_storage_escape_disp escape_disp) { if (this->varargs_are_lowered_) return; @@ -8842,8 +9586,8 @@ Call_expression::lower_varargs(Gogo* gogo, Named_object* function, this->report_error(_("too many arguments")); else { - error_at(this->location(), - _("invalid use of %<...%> with non-slice")); + go_error_at(this->location(), + _("invalid use of %<...%> with non-slice")); this->set_is_error(); } return; @@ -8862,8 +9606,11 @@ Call_expression::lower_varargs(Gogo* gogo, Named_object* function, continue; vals->push_back(*pa); } - Expression* val = + Slice_construction_expression* sce = Expression::make_slice_composite_literal(varargs_type, vals, loc); + if (escape_disp == SLICE_STORAGE_DOES_NOT_ESCAPE) + sce->set_storage_does_not_escape(); + Expression* val = sce; gogo->lower_expression(function, inserter, &val); new_args->push_back(val); } @@ -8880,6 +9627,28 @@ Call_expression::lower_varargs(Gogo* gogo, Named_object* function, this->varargs_are_lowered_ = true; } +// Return a call to __builtin_return_address or __builtin_frame_address. + +Expression* +Call_expression::lower_to_builtin(Named_object** pno, const char* name, + int arg) +{ + if (*pno == NULL) + *pno = Gogo::declare_builtin_rf_address(name); + + Location loc = this->location(); + + Expression* fn = Expression::make_func_reference(*pno, NULL, loc); + Expression* a = Expression::make_integer_ul(arg, NULL, loc); + Expression_list *args = new Expression_list(); + args->push_back(a); + Expression* call = Expression::make_call(fn, args, false, loc); + + // The builtin functions return void*, but the Go functions return uintptr. + Type* uintptr_type = Type::lookup_integer_type("uintptr"); + return Expression::make_cast(uintptr_type, call, loc); +} + // Flatten a call with multiple results into a temporary. Expression* @@ -8953,7 +9722,7 @@ Call_expression::do_flatten(Gogo* gogo, Named_object*, Location loc = this->location(); int i = 0; - char buf[10]; + char buf[20]; for (Typed_identifier_list::const_iterator p = results->begin(); p != results->end(); ++p, ++i) @@ -8963,6 +9732,7 @@ Call_expression::do_flatten(Gogo* gogo, Named_object*, } Struct_type* st = Type::make_struct_type(sfl, loc); + st->set_is_struct_incomparable(); this->call_temp_ = Statement::make_temporary(st, NULL, loc); inserter->insert(this->call_temp_); } @@ -9187,11 +9957,11 @@ Call_expression::check_argument_type(int i, const Type* parameter_type, if (!issued_error) { if (reason.empty()) - error_at(argument_location, "argument %d has incompatible type", i); + go_error_at(argument_location, "argument %d has incompatible type", i); else - error_at(argument_location, - "argument %d has incompatible type (%s)", - i, reason.c_str()); + go_error_at(argument_location, + "argument %d has incompatible type (%s)", + i, reason.c_str()); } this->set_is_error(); return false; @@ -9240,9 +10010,9 @@ Call_expression::do_check_types(Gogo*) this->report_error(_("incompatible type for receiver")); else { - error_at(this->location(), - "incompatible type for receiver (%s)", - reason.c_str()); + go_error_at(this->location(), + "incompatible type for receiver (%s)", + reason.c_str()); this->set_is_error(); } } @@ -9254,8 +10024,8 @@ Call_expression::do_check_types(Gogo*) { if (!fntype->is_varargs()) { - error_at(this->location(), - _("invalid use of %<...%> calling non-variadic function")); + go_error_at(this->location(), + _("invalid use of %<...%> calling non-variadic function")); this->set_is_error(); return; } @@ -9484,8 +10254,10 @@ Call_expression::do_get_backend(Translate_context* context) Expression* call_ref = Expression::make_temporary_reference(this->call_temp_, location); Bexpression* bcall_ref = call_ref->get_backend(context); + Bfunction* bfunction = context->function()->func_value()->get_decl(); Bstatement* assn_stmt = - gogo->backend()->assignment_statement(bcall_ref, call, location); + gogo->backend()->assignment_statement(bfunction, + bcall_ref, call, location); this->call_ = this->set_results(context, bcall_ref); @@ -9522,11 +10294,13 @@ Call_expression::set_results(Translate_context* context, Bexpression* call) Expression::make_temporary_reference(temp, loc); ref->set_is_lvalue(); + Bfunction* bfunction = context->function()->func_value()->get_decl(); Bexpression* result_ref = ref->get_backend(context); Bexpression* call_result = gogo->backend()->struct_field_expression(call, i, loc); Bstatement* assn_stmt = - gogo->backend()->assignment_statement(result_ref, call_result, loc); + gogo->backend()->assignment_statement(bfunction, + result_ref, call_result, loc); Bexpression* result = gogo->backend()->compound_expression(assn_stmt, call_result, loc); @@ -9535,7 +10309,8 @@ Call_expression::set_results(Translate_context* context, Bexpression* call) results = result; else { - Bstatement* expr_stmt = gogo->backend()->expression_statement(result); + Bstatement* expr_stmt = + gogo->backend()->expression_statement(bfunction, result); results = gogo->backend()->compound_expression(expr_stmt, results, loc); } @@ -9732,7 +10507,7 @@ Index_expression::do_lower(Gogo*, Named_object*, Statement_inserter*, int) } else if (left->is_type_expression()) { - error_at(location, "attempt to index type expression"); + go_error_at(location, "attempt to index type expression"); return Expression::make_error(location); } else if (type->array_type() != NULL) @@ -9757,7 +10532,7 @@ Index_expression::do_lower(Gogo*, Named_object*, Statement_inserter*, int) { if (cap != NULL) { - error_at(location, "invalid 3-index slice of string"); + go_error_at(location, "invalid 3-index slice of string"); return Expression::make_error(location); } return Expression::make_string_index(left, start, end, location); @@ -9766,19 +10541,15 @@ Index_expression::do_lower(Gogo*, Named_object*, Statement_inserter*, int) { if (end != NULL || cap != NULL) { - error_at(location, "invalid slice of map"); + go_error_at(location, "invalid slice of map"); return Expression::make_error(location); } - Map_index_expression* ret = Expression::make_map_index(left, start, - location); - if (this->is_lvalue_) - ret->set_is_lvalue(); - return ret; + return Expression::make_map_index(left, start, location); } else { - error_at(location, - "attempt to index object which is not array, string, or map"); + go_error_at(location, + "attempt to index object which is not array, string, or map"); return Expression::make_error(location); } } @@ -9910,7 +10681,7 @@ Array_index_expression::do_determine_type(const Type_context*) // Check types of an array index. void -Array_index_expression::do_check_types(Gogo*) +Array_index_expression::do_check_types(Gogo* gogo) { Numeric_constant nc; unsigned long v; @@ -9964,7 +10735,7 @@ Array_index_expression::do_check_types(Gogo*) ? mpz_cmp(ival, lval) >= 0 : mpz_cmp(ival, lval) > 0))) { - error_at(this->start_->location(), "array index out of bounds"); + go_error_at(this->start_->location(), "array index out of bounds"); this->set_is_error(); } } @@ -9980,7 +10751,7 @@ Array_index_expression::do_check_types(Gogo*) || mpz_sizeinbase(eval, 2) >= int_bits || (lval_valid && mpz_cmp(eval, lval) > 0)) { - error_at(this->end_->location(), "array index out of bounds"); + go_error_at(this->end_->location(), "array index out of bounds"); this->set_is_error(); } else if (ival_valid && mpz_cmp(ival, eval) > 0) @@ -9996,19 +10767,19 @@ Array_index_expression::do_check_types(Gogo*) || mpz_sizeinbase(cval, 2) >= int_bits || (lval_valid && mpz_cmp(cval, lval) > 0)) { - error_at(this->cap_->location(), "array index out of bounds"); + go_error_at(this->cap_->location(), "array index out of bounds"); this->set_is_error(); } else if (ival_valid && mpz_cmp(ival, cval) > 0) { - error_at(this->cap_->location(), - "invalid slice index: capacity less than start"); + go_error_at(this->cap_->location(), + "invalid slice index: capacity less than start"); this->set_is_error(); } else if (eval_valid && mpz_cmp(eval, cval) > 0) { - error_at(this->cap_->location(), - "invalid slice index: capacity less than length"); + go_error_at(this->cap_->location(), + "invalid slice index: capacity less than length"); this->set_is_error(); } mpz_clear(cval); @@ -10029,7 +10800,18 @@ Array_index_expression::do_check_types(Gogo*) if (!this->array_->is_addressable()) this->report_error(_("slice of unaddressable value")); else - this->array_->address_taken(true); + { + bool escapes = true; + + // When compiling the runtime, a slice operation does not + // cause local variables to escape. When escape analysis + // becomes the default, this should be changed to make it an + // error if we have a slice operation that escapes. + if (gogo->compiling_runtime() && gogo->package_name() == "runtime") + escapes = false; + + this->array_->address_taken(escapes); + } } } @@ -10188,12 +10970,13 @@ Array_index_expression::do_get_backend(Translate_context* context) bad_index = gogo->backend()->binary_expression(OPERATOR_OROR, start_too_large, bad_index, loc); + Bfunction* bfn = context->function()->func_value()->get_decl(); if (this->end_ == NULL) { // Simple array indexing. This has to return an l-value, so // wrap the index check into START. start = - gogo->backend()->conditional_expression(int_btype, bad_index, + gogo->backend()->conditional_expression(bfn, int_btype, bad_index, crash, start, loc); Bexpression* ret; @@ -10282,7 +11065,7 @@ Array_index_expression::do_get_backend(Translate_context* context) Bexpression* ctor = gogo->backend()->constructor_expression(struct_btype, init, loc); - return gogo->backend()->conditional_expression(struct_btype, bad_index, + return gogo->backend()->conditional_expression(bfn, struct_btype, bad_index, crash, ctor, loc); } @@ -10306,66 +11089,7 @@ Expression::make_array_index(Expression* array, Expression* start, return new Array_index_expression(array, start, end, cap, location); } -// A string index. This is used for both indexing and slicing. - -class String_index_expression : public Expression -{ - public: - String_index_expression(Expression* string, Expression* start, - Expression* end, Location location) - : Expression(EXPRESSION_STRING_INDEX, location), - string_(string), start_(start), end_(end) - { } - - protected: - int - do_traverse(Traverse*); - - Expression* - do_flatten(Gogo*, Named_object*, Statement_inserter*); - - Type* - do_type(); - - void - do_determine_type(const Type_context*); - - void - do_check_types(Gogo*); - - Expression* - do_copy() - { - return Expression::make_string_index(this->string_->copy(), - this->start_->copy(), - (this->end_ == NULL - ? NULL - : this->end_->copy()), - this->location()); - } - - bool - do_must_eval_subexpressions_in_order(int* skip) const - { - *skip = 1; - return true; - } - - Bexpression* - do_get_backend(Translate_context*); - - void - do_dump_expression(Ast_dump_context*) const; - - private: - // The string we are getting a value from. - Expression* string_; - // The start or only index. - Expression* start_; - // The end index of a slice. This may be NULL for a single index, - // or it may be a nil expression for the length of the string. - Expression* end_; -}; +// Class String_index_expression. // String index traversal. @@ -10496,7 +11220,7 @@ String_index_expression::do_check_types(Gogo*) ? mpz_cmp_ui(ival, sval.length()) >= 0 : mpz_cmp_ui(ival, sval.length()) > 0))) { - error_at(this->start_->location(), "string index out of bounds"); + go_error_at(this->start_->location(), "string index out of bounds"); this->set_is_error(); } } @@ -10509,7 +11233,7 @@ String_index_expression::do_check_types(Gogo*) if (mpz_sgn(eval) < 0 || (sval_valid && mpz_cmp_ui(eval, sval.length()) > 0)) { - error_at(this->end_->location(), "string index out of bounds"); + go_error_at(this->end_->location(), "string index out of bounds"); this->set_is_error(); } else if (ival_valid && mpz_cmp(ival, eval) > 0) @@ -10555,6 +11279,7 @@ String_index_expression::do_get_backend(Translate_context* context) } Expression* start = Expression::make_cast(int_type, this->start_, loc); + Bfunction* bfn = context->function()->func_value()->get_decl(); if (this->end_ == NULL) { @@ -10577,8 +11302,9 @@ String_index_expression::do_get_backend(Translate_context* context) Btype* byte_btype = bytes->type()->points_to()->get_backend(gogo); Bexpression* index_error = bad_index->get_backend(context); - return gogo->backend()->conditional_expression(byte_btype, index_error, - crash, index, loc); + return gogo->backend()->conditional_expression(bfn, byte_btype, + index_error, crash, + index, loc); } Expression* end = NULL; @@ -10598,7 +11324,7 @@ String_index_expression::do_get_backend(Translate_context* context) Btype* str_btype = strslice->type()->get_backend(gogo); Bexpression* index_error = bad_index->get_backend(context); - return gogo->backend()->conditional_expression(str_btype, index_error, + return gogo->backend()->conditional_expression(bfn, str_btype, index_error, crash, bstrslice, loc); } @@ -10628,7 +11354,7 @@ Expression::make_string_index(Expression* string, Expression* start, Map_type* Map_index_expression::get_map_type() const { - Map_type* mt = this->map_->type()->deref()->map_type(); + Map_type* mt = this->map_->type()->map_type(); if (mt == NULL) go_assert(saw_errors()); return mt; @@ -10686,7 +11412,7 @@ Map_index_expression::do_flatten(Gogo* gogo, Named_object*, } if (this->value_pointer_ == NULL) - this->get_value_pointer(this->is_lvalue_); + this->get_value_pointer(gogo); if (this->value_pointer_->is_error_expression() || this->value_pointer_->type()->is_error_type()) return Expression::make_error(loc); @@ -10709,14 +11435,7 @@ Map_index_expression::do_type() Map_type* mt = this->get_map_type(); if (mt == NULL) return Type::make_error_type(); - Type* type = mt->val_type(); - // If this map index is in a tuple assignment, we actually return a - // pointer to the value type. Tuple_map_assignment_statement is - // responsible for handling this correctly. We need to get the type - // right in case this gets assigned to a temporary variable. - if (this->is_in_tuple_assignment_) - type = Type::make_pointer_type(type); - return type; + return mt->val_type(); } // Fix the type of a map index. @@ -10746,8 +11465,8 @@ Map_index_expression::do_check_types(Gogo*) this->report_error(_("incompatible type for map index")); else { - error_at(this->location(), "incompatible type for map index (%s)", - reason.c_str()); + go_error_at(this->location(), "incompatible type for map index (%s)", + reason.c_str()); this->set_is_error(); } } @@ -10768,47 +11487,17 @@ Map_index_expression::do_get_backend(Translate_context* context) go_assert(this->value_pointer_ != NULL && this->value_pointer_->is_variable()); - Bexpression* ret; - if (this->is_lvalue_) - { - Expression* val = - Expression::make_unary(OPERATOR_MULT, this->value_pointer_, - this->location()); - ret = val->get_backend(context); - } - else if (this->is_in_tuple_assignment_) - { - // Tuple_map_assignment_statement is responsible for using this - // appropriately. - ret = this->value_pointer_->get_backend(context); - } - else - { - Location loc = this->location(); - - Expression* nil_check = - Expression::make_binary(OPERATOR_EQEQ, this->value_pointer_, - Expression::make_nil(loc), loc); - Bexpression* bnil_check = nil_check->get_backend(context); - Expression* val = - Expression::make_unary(OPERATOR_MULT, this->value_pointer_, loc); - Bexpression* bval = val->get_backend(context); - - Gogo* gogo = context->gogo(); - Btype* val_btype = type->val_type()->get_backend(gogo); - Bexpression* val_zero = gogo->backend()->zero_expression(val_btype); - ret = gogo->backend()->conditional_expression(val_btype, bnil_check, - val_zero, bval, loc); - } - return ret; + Expression* val = Expression::make_unary(OPERATOR_MULT, this->value_pointer_, + this->location()); + return val->get_backend(context); } -// Get an expression for the map index. This returns an expression which -// evaluates to a pointer to a value. The pointer will be NULL if the key is -// not in the map. +// Get an expression for the map index. This returns an expression +// that evaluates to a pointer to a value. If the key is not in the +// map, the pointer will point to a zero value. Expression* -Map_index_expression::get_value_pointer(bool insert) +Map_index_expression::get_value_pointer(Gogo* gogo) { if (this->value_pointer_ == NULL) { @@ -10821,21 +11510,32 @@ Map_index_expression::get_value_pointer(bool insert) Location loc = this->location(); Expression* map_ref = this->map_; - if (this->map_->type()->points_to() != NULL) - map_ref = Expression::make_unary(OPERATOR_MULT, map_ref, loc); - Expression* index_ptr = Expression::make_unary(OPERATOR_AND, this->index_, + Expression* index_ptr = Expression::make_unary(OPERATOR_AND, + this->index_, loc); - Expression* map_index = - Runtime::make_call(Runtime::MAP_INDEX, loc, 3, - map_ref, index_ptr, - Expression::make_boolean(insert, loc)); + + Expression* zero = type->fat_zero_value(gogo); + + Expression* map_index; + + if (zero == NULL) + map_index = + Runtime::make_call(Runtime::MAPACCESS1, loc, 3, + Expression::make_type_descriptor(type, loc), + map_ref, index_ptr); + else + map_index = + Runtime::make_call(Runtime::MAPACCESS1_FAT, loc, 4, + Expression::make_type_descriptor(type, loc), + map_ref, index_ptr, zero); Type* val_type = type->val_type(); this->value_pointer_ = Expression::make_unsafe_cast(Type::make_pointer_type(val_type), map_index, this->location()); } + return this->value_pointer_; } @@ -10918,7 +11618,8 @@ Field_reference_expression::do_lower(Gogo* gogo, Named_object* function, Expression* length_expr = Expression::make_integer_ul(s.length(), NULL, loc); Type* byte_type = gogo->lookup_global("byte")->type_value(); - Type* array_type = Type::make_array_type(byte_type, length_expr); + Array_type* array_type = Type::make_array_type(byte_type, length_expr); + array_type->set_is_array_incomparable(); Expression_list* bytes = new Expression_list(); for (std::string::const_iterator p = s.begin(); p != s.end(); p++) @@ -11137,8 +11838,8 @@ Interface_field_reference_expression::do_check_types(Gogo*) interface_type->find_method(this->name_); if (method == NULL) { - error_at(this->location(), "method %qs not in interface", - Gogo::message_name(this->name_).c_str()); + go_error_at(this->location(), "method %qs not in interface", + Gogo::message_name(this->name_).c_str()); this->set_is_error(); } } @@ -11196,8 +11897,9 @@ Interface_field_reference_expression::create_thunk(Gogo* gogo, Type* vt = Type::make_pointer_type(Type::make_void_type()); sfl->push_back(Struct_field(Typed_identifier("fn.0", vt, loc))); sfl->push_back(Struct_field(Typed_identifier("val.1", type, loc))); - Type* closure_type = Type::make_struct_type(sfl, loc); - closure_type = Type::make_pointer_type(closure_type); + Struct_type* st = Type::make_struct_type(sfl, loc); + st->set_is_struct_incomparable(); + Type* closure_type = Type::make_pointer_type(st); Function_type* new_fntype = orig_fntype->copy_with_names(); @@ -11296,6 +11998,7 @@ Interface_field_reference_expression::do_get_backend(Translate_context* context) this->expr_->type(), loc))); Struct_type* st = Type::make_struct_type(fields, loc); + st->set_is_struct_incomparable(); Expression_list* vals = new Expression_list(); vals->push_back(Expression::make_func_code_reference(thunk, loc)); @@ -11314,9 +12017,13 @@ Interface_field_reference_expression::do_get_backend(Translate_context* context) Bexpression* bcrash = gogo->runtime_error(RUNTIME_ERROR_NIL_DEREFERENCE, loc)->get_backend(context); + Bfunction* bfn = context->function()->func_value()->get_decl(); Bexpression* bcond = - gogo->backend()->conditional_expression(NULL, bnil_check, bcrash, NULL, loc); - Bstatement* cond_statement = gogo->backend()->expression_statement(bcond); + gogo->backend()->conditional_expression(bfn, NULL, + bnil_check, bcrash, NULL, loc); + Bfunction* bfunction = context->function()->func_value()->get_decl(); + Bstatement* cond_statement = + gogo->backend()->expression_statement(bfunction, bcond); return gogo->backend()->compound_expression(cond_statement, bclosure, loc); } @@ -11401,7 +12108,8 @@ Expression* Selector_expression::lower_method_expression(Gogo* gogo) { Location location = this->location(); - Type* type = this->left_->type(); + Type* left_type = this->left_->type(); + Type* type = left_type; const std::string& name(this->name_); bool is_pointer; @@ -11415,9 +12123,9 @@ Selector_expression::lower_method_expression(Gogo* gogo) Named_type* nt = type->named_type(); if (nt == NULL) { - error_at(location, - ("method expression requires named type or " - "pointer to named type")); + go_error_at(location, + ("method expression requires named type or " + "pointer to named type")); return Expression::make_error(location); } @@ -11431,26 +12139,27 @@ Selector_expression::lower_method_expression(Gogo* gogo) imethod = it->find_method(name); } - if (method == NULL && imethod == NULL) + if ((method == NULL && imethod == NULL) + || (left_type->named_type() != NULL && left_type->points_to() != NULL)) { if (!is_ambiguous) - error_at(location, "type %<%s%s%> has no method %<%s%>", - is_pointer ? "*" : "", - nt->message_name().c_str(), - Gogo::message_name(name).c_str()); + go_error_at(location, "type %<%s%s%> has no method %<%s%>", + is_pointer ? "*" : "", + nt->message_name().c_str(), + Gogo::message_name(name).c_str()); else - error_at(location, "method %<%s%s%> is ambiguous in type %<%s%>", - Gogo::message_name(name).c_str(), - is_pointer ? "*" : "", - nt->message_name().c_str()); + go_error_at(location, "method %<%s%s%> is ambiguous in type %<%s%>", + Gogo::message_name(name).c_str(), + is_pointer ? "*" : "", + nt->message_name().c_str()); return Expression::make_error(location); } if (method != NULL && !is_pointer && !method->is_value_method()) { - error_at(location, "method requires pointer (use %<(*%s).%s)%>", - nt->message_name().c_str(), - Gogo::message_name(name).c_str()); + go_error_at(location, "method requires pointer (use %<(*%s).%s%>)", + nt->message_name().c_str(), + Gogo::message_name(name).c_str()); return Expression::make_error(location); } @@ -11643,7 +12352,9 @@ Allocation_expression::do_get_backend(Translate_context* context) Gogo* gogo = context->gogo(); Location loc = this->location(); - if (this->allocate_on_stack_) + Node* n = Node::make_node(this); + if (this->allocate_on_stack_ + || (n->encoding() & ESCAPE_MASK) == int(Node::ESCAPE_NONE)) { int64_t size; bool ok = this->type_->backend_type_size(gogo, &size); @@ -11681,12 +12392,10 @@ Expression::make_allocation(Type* type, Location location) return new Allocation_expression(type, location); } -// Class Struct_construction_expression. - -// Traversal. +// Class Ordered_value_list. int -Struct_construction_expression::do_traverse(Traverse* traverse) +Ordered_value_list::traverse_vals(Traverse* traverse) { if (this->vals_ != NULL) { @@ -11697,8 +12406,8 @@ Struct_construction_expression::do_traverse(Traverse* traverse) } else { - for (std::vector<int>::const_iterator p = - this->traverse_order_->begin(); + for (std::vector<unsigned long>::const_iterator p = + this->traverse_order_->begin(); p != this->traverse_order_->end(); ++p) { @@ -11708,6 +12417,18 @@ Struct_construction_expression::do_traverse(Traverse* traverse) } } } + return TRAVERSE_CONTINUE; +} + +// Class Struct_construction_expression. + +// Traversal. + +int +Struct_construction_expression::do_traverse(Traverse* traverse) +{ + if (this->traverse_vals(traverse) == TRAVERSE_EXIT) + return TRAVERSE_EXIT; if (Type::traverse(this->type_, traverse) == TRAVERSE_EXIT) return TRAVERSE_EXIT; return TRAVERSE_CONTINUE; @@ -11718,10 +12439,10 @@ Struct_construction_expression::do_traverse(Traverse* traverse) bool Struct_construction_expression::is_constant_struct() const { - if (this->vals_ == NULL) + if (this->vals() == NULL) return true; - for (Expression_list::const_iterator pv = this->vals_->begin(); - pv != this->vals_->end(); + for (Expression_list::const_iterator pv = this->vals()->begin(); + pv != this->vals()->end(); ++pv) { if (*pv != NULL @@ -11744,20 +12465,31 @@ Struct_construction_expression::is_constant_struct() const return true; } -// Return whether this struct is immutable. +// Return whether this struct can be used as a constant initializer. bool -Struct_construction_expression::do_is_immutable() const +Struct_construction_expression::do_is_static_initializer() const { - if (this->vals_ == NULL) + if (this->vals() == NULL) return true; - for (Expression_list::const_iterator pv = this->vals_->begin(); - pv != this->vals_->end(); + for (Expression_list::const_iterator pv = this->vals()->begin(); + pv != this->vals()->end(); ++pv) { - if (*pv != NULL && !(*pv)->is_immutable()) + if (*pv != NULL && !(*pv)->is_static_initializer()) + return false; + } + + const Struct_field_list* fields = this->type_->struct_type()->fields(); + for (Struct_field_list::const_iterator pf = fields->begin(); + pf != fields->end(); + ++pf) + { + // There are no constant constructors for interfaces. + if (pf->type()->interface_type() != NULL) return false; } + return true; } @@ -11766,15 +12498,15 @@ Struct_construction_expression::do_is_immutable() const void Struct_construction_expression::do_determine_type(const Type_context*) { - if (this->vals_ == NULL) + if (this->vals() == NULL) return; const Struct_field_list* fields = this->type_->struct_type()->fields(); - Expression_list::const_iterator pv = this->vals_->begin(); + Expression_list::const_iterator pv = this->vals()->begin(); for (Struct_field_list::const_iterator pf = fields->begin(); pf != fields->end(); ++pf, ++pv) { - if (pv == this->vals_->end()) + if (pv == this->vals()->end()) return; if (*pv != NULL) { @@ -11784,7 +12516,7 @@ Struct_construction_expression::do_determine_type(const Type_context*) } // Extra values are an error we will report elsewhere; we still want // to determine the type to avoid knockon errors. - for (; pv != this->vals_->end(); ++pv) + for (; pv != this->vals()->end(); ++pv) (*pv)->determine_type_no_context(); } @@ -11793,24 +12525,24 @@ Struct_construction_expression::do_determine_type(const Type_context*) void Struct_construction_expression::do_check_types(Gogo*) { - if (this->vals_ == NULL) + if (this->vals() == NULL) return; Struct_type* st = this->type_->struct_type(); - if (this->vals_->size() > st->field_count()) + if (this->vals()->size() > st->field_count()) { this->report_error(_("too many expressions for struct")); return; } const Struct_field_list* fields = st->fields(); - Expression_list::const_iterator pv = this->vals_->begin(); + Expression_list::const_iterator pv = this->vals()->begin(); int i = 0; for (Struct_field_list::const_iterator pf = fields->begin(); pf != fields->end(); ++pf, ++pv, ++i) { - if (pv == this->vals_->end()) + if (pv == this->vals()->end()) { this->report_error(_("too few expressions for struct")); break; @@ -11823,18 +12555,18 @@ Struct_construction_expression::do_check_types(Gogo*) if (!Type::are_assignable(pf->type(), (*pv)->type(), &reason)) { if (reason.empty()) - error_at((*pv)->location(), - "incompatible type for field %d in struct construction", - i + 1); + go_error_at((*pv)->location(), + "incompatible type for field %d in struct construction", + i + 1); else - error_at((*pv)->location(), - ("incompatible type for field %d in " - "struct construction (%s)"), - i + 1, reason.c_str()); + go_error_at((*pv)->location(), + ("incompatible type for field %d in " + "struct construction (%s)"), + i + 1, reason.c_str()); this->set_is_error(); } } - go_assert(pv == this->vals_->end()); + go_assert(pv == this->vals()->end()); } // Flatten a struct construction expression. Store the values into @@ -11844,16 +12576,16 @@ Expression* Struct_construction_expression::do_flatten(Gogo*, Named_object*, Statement_inserter* inserter) { - if (this->vals_ == NULL) + if (this->vals() == NULL) return this; // If this is a constant struct, we don't need temporaries. - if (this->is_constant_struct()) + if (this->is_constant_struct() || this->is_static_initializer()) return this; Location loc = this->location(); - for (Expression_list::iterator pv = this->vals_->begin(); - pv != this->vals_->end(); + for (Expression_list::iterator pv = this->vals()->begin(); + pv != this->vals()->end(); ++pv) { if (*pv != NULL) @@ -11883,18 +12615,18 @@ Struct_construction_expression::do_get_backend(Translate_context* context) Gogo* gogo = context->gogo(); Btype* btype = this->type_->get_backend(gogo); - if (this->vals_ == NULL) + if (this->vals() == NULL) return gogo->backend()->zero_expression(btype); const Struct_field_list* fields = this->type_->struct_type()->fields(); - Expression_list::const_iterator pv = this->vals_->begin(); + Expression_list::const_iterator pv = this->vals()->begin(); std::vector<Bexpression*> init; for (Struct_field_list::const_iterator pf = fields->begin(); pf != fields->end(); ++pf) { Btype* fbtype = pf->type()->get_backend(gogo); - if (pv == this->vals_->end()) + if (pv == this->vals()->end()) init.push_back(gogo->backend()->zero_expression(fbtype)); else if (*pv == NULL) { @@ -11920,8 +12652,8 @@ Struct_construction_expression::do_export(Export* exp) const { exp->write_c_string("convert("); exp->write_type(this->type_); - for (Expression_list::const_iterator pv = this->vals_->begin(); - pv != this->vals_->end(); + for (Expression_list::const_iterator pv = this->vals()->begin(); + pv != this->vals()->end(); ++pv) { exp->write_c_string(", "); @@ -11939,7 +12671,7 @@ Struct_construction_expression::do_dump_expression( { ast_dump_context->dump_type(this->type_); ast_dump_context->ostream() << "{"; - ast_dump_context->dump_expression_list(this->vals_); + ast_dump_context->dump_expression_list(this->vals()); ast_dump_context->ostream() << "}"; } @@ -11960,8 +12692,7 @@ Expression::make_struct_composite_literal(Type* type, Expression_list* vals, int Array_construction_expression::do_traverse(Traverse* traverse) { - if (this->vals_ != NULL - && this->vals_->traverse(traverse) == TRAVERSE_EXIT) + if (this->traverse_vals(traverse) == TRAVERSE_EXIT) return TRAVERSE_EXIT; if (Type::traverse(this->type_, traverse) == TRAVERSE_EXIT) return TRAVERSE_EXIT; @@ -11973,15 +12704,15 @@ Array_construction_expression::do_traverse(Traverse* traverse) bool Array_construction_expression::is_constant_array() const { - if (this->vals_ == NULL) + if (this->vals() == NULL) return true; // There are no constant constructors for interfaces. if (this->type_->array_type()->element_type()->interface_type() != NULL) return false; - for (Expression_list::const_iterator pv = this->vals_->begin(); - pv != this->vals_->end(); + for (Expression_list::const_iterator pv = this->vals()->begin(); + pv != this->vals()->end(); ++pv) { if (*pv != NULL @@ -11993,18 +12724,23 @@ Array_construction_expression::is_constant_array() const return true; } -// Return whether this is an immutable array initializer. +// Return whether this can be used a constant initializer. bool -Array_construction_expression::do_is_immutable() const +Array_construction_expression::do_is_static_initializer() const { - if (this->vals_ == NULL) + if (this->vals() == NULL) return true; - for (Expression_list::const_iterator pv = this->vals_->begin(); - pv != this->vals_->end(); + + // There are no constant constructors for interfaces. + if (this->type_->array_type()->element_type()->interface_type() != NULL) + return false; + + for (Expression_list::const_iterator pv = this->vals()->begin(); + pv != this->vals()->end(); ++pv) { - if (*pv != NULL && !(*pv)->is_immutable()) + if (*pv != NULL && !(*pv)->is_static_initializer()) return false; } return true; @@ -12015,11 +12751,11 @@ Array_construction_expression::do_is_immutable() const void Array_construction_expression::do_determine_type(const Type_context*) { - if (this->vals_ == NULL) + if (this->vals() == NULL) return; Type_context subcontext(this->type_->array_type()->element_type(), false); - for (Expression_list::const_iterator pv = this->vals_->begin(); - pv != this->vals_->end(); + for (Expression_list::const_iterator pv = this->vals()->begin(); + pv != this->vals()->end(); ++pv) { if (*pv != NULL) @@ -12032,22 +12768,22 @@ Array_construction_expression::do_determine_type(const Type_context*) void Array_construction_expression::do_check_types(Gogo*) { - if (this->vals_ == NULL) + if (this->vals() == NULL) return; Array_type* at = this->type_->array_type(); int i = 0; Type* element_type = at->element_type(); - for (Expression_list::const_iterator pv = this->vals_->begin(); - pv != this->vals_->end(); + for (Expression_list::const_iterator pv = this->vals()->begin(); + pv != this->vals()->end(); ++pv, ++i) { if (*pv != NULL && !Type::are_assignable(element_type, (*pv)->type(), NULL)) { - error_at((*pv)->location(), - "incompatible type for element %d in composite literal", - i + 1); + go_error_at((*pv)->location(), + "incompatible type for element %d in composite literal", + i + 1); this->set_is_error(); } } @@ -12060,16 +12796,16 @@ Expression* Array_construction_expression::do_flatten(Gogo*, Named_object*, Statement_inserter* inserter) { - if (this->vals_ == NULL) + if (this->vals() == NULL) return this; // If this is a constant array, we don't need temporaries. - if (this->is_constant_array()) + if (this->is_constant_array() || this->is_static_initializer()) return this; Location loc = this->location(); - for (Expression_list::iterator pv = this->vals_->begin(); - pv != this->vals_->end(); + for (Expression_list::iterator pv = this->vals()->begin(); + pv != this->vals()->end(); ++pv) { if (*pv != NULL) @@ -12102,14 +12838,14 @@ Array_construction_expression::get_constructor(Translate_context* context, std::vector<unsigned long> indexes; std::vector<Bexpression*> vals; Gogo* gogo = context->gogo(); - if (this->vals_ != NULL) + if (this->vals() != NULL) { size_t i = 0; std::vector<unsigned long>::const_iterator pi; if (this->indexes_ != NULL) pi = this->indexes_->begin(); - for (Expression_list::const_iterator pv = this->vals_->begin(); - pv != this->vals_->end(); + for (Expression_list::const_iterator pv = this->vals()->begin(); + pv != this->vals()->end(); ++pv, ++i) { if (this->indexes_ != NULL) @@ -12149,13 +12885,13 @@ Array_construction_expression::do_export(Export* exp) const { exp->write_c_string("convert("); exp->write_type(this->type_); - if (this->vals_ != NULL) + if (this->vals() != NULL) { std::vector<unsigned long>::const_iterator pi; if (this->indexes_ != NULL) pi = this->indexes_->begin(); - for (Expression_list::const_iterator pv = this->vals_->begin(); - pv != this->vals_->end(); + for (Expression_list::const_iterator pv = this->vals()->begin(); + pv != this->vals()->end(); ++pv) { exp->write_c_string(", "); @@ -12178,7 +12914,7 @@ Array_construction_expression::do_export(Export* exp) const exp->write_c_string(")"); } -// Dump ast representation of an array construction expressin. +// Dump ast representation of an array construction expression. void Array_construction_expression::do_dump_expression( @@ -12193,12 +12929,13 @@ Array_construction_expression::do_dump_expression( } ast_dump_context->ostream() << "]" ; ast_dump_context->dump_type(this->type_); + this->dump_slice_storage_expression(ast_dump_context); ast_dump_context->ostream() << "{" ; if (this->indexes_ == NULL) - ast_dump_context->dump_expression_list(this->vals_); + ast_dump_context->dump_expression_list(this->vals()); else { - Expression_list::const_iterator pv = this->vals_->begin(); + Expression_list::const_iterator pv = this->vals()->begin(); for (std::vector<unsigned long>::const_iterator pi = this->indexes_->begin(); pi != this->indexes_->end(); @@ -12248,7 +12985,8 @@ Slice_construction_expression::Slice_construction_expression( Expression_list* vals, Location location) : Array_construction_expression(EXPRESSION_SLICE_CONSTRUCTION, type, indexes, vals, location), - valtype_(NULL) + valtype_(NULL), array_val_(NULL), slice_storage_(NULL), + storage_escapes_(true) { go_assert(type->is_slice_type()); @@ -12266,10 +13004,11 @@ Slice_construction_expression::Slice_construction_expression( Type* int_type = Type::lookup_integer_type("int"); length = Expression::make_integer_ul(lenval, int_type, location); Type* element_type = type->array_type()->element_type(); - this->valtype_ = Type::make_array_type(element_type, length); + Array_type* array_type = Type::make_array_type(element_type, length); + array_type->set_is_array_incomparable(); + this->valtype_ = array_type; } - // Traversal. int @@ -12280,59 +13019,125 @@ Slice_construction_expression::do_traverse(Traverse* traverse) return TRAVERSE_EXIT; if (Type::traverse(this->valtype_, traverse) == TRAVERSE_EXIT) return TRAVERSE_EXIT; + if (this->array_val_ != NULL + && Expression::traverse(&this->array_val_, traverse) == TRAVERSE_EXIT) + return TRAVERSE_EXIT; + if (this->slice_storage_ != NULL + && Expression::traverse(&this->slice_storage_, traverse) == TRAVERSE_EXIT) + return TRAVERSE_EXIT; return TRAVERSE_CONTINUE; } -// Return the backend representation for constructing a slice. +// Helper routine to create fixed array value underlying the slice literal. +// May be called during flattening, or later during do_get_backend(). -Bexpression* -Slice_construction_expression::do_get_backend(Translate_context* context) +Expression* +Slice_construction_expression::create_array_val() { Array_type* array_type = this->type()->array_type(); if (array_type == NULL) { go_assert(this->type()->is_error()); - return context->backend()->error_expression(); + return NULL; } Location loc = this->location(); - Type* element_type = array_type->element_type(); go_assert(this->valtype_ != NULL); Expression_list* vals = this->vals(); - if (this->vals() == NULL || this->vals()->empty()) + return new Fixed_array_construction_expression( + this->valtype_, this->indexes(), vals, loc); +} + +// If we're previous established that the slice storage does not +// escape, then create a separate array temp val here for it. We +// need to do this as part of flattening so as to be able to insert +// the new temp statement. + +Expression* +Slice_construction_expression::do_flatten(Gogo* gogo, Named_object* no, + Statement_inserter* inserter) +{ + if (this->type()->array_type() == NULL) + return NULL; + + // Base class flattening first + this->Array_construction_expression::do_flatten(gogo, no, inserter); + + // Create a stack-allocated storage temp if storage won't escape + if (!this->storage_escapes_ && this->slice_storage_ == NULL) + { + Location loc = this->location(); + this->array_val_ = create_array_val(); + go_assert(this->array_val_); + Temporary_statement* temp = + Statement::make_temporary(this->valtype_, this->array_val_, loc); + inserter->insert(temp); + this->slice_storage_ = Expression::make_temporary_reference(temp, loc); + } + return this; +} + +// When dumping a slice construction expression that has an explicit +// storeage temp, emit the temp here (if we don't do this the storage +// temp appears unused in the AST dump). + +void +Slice_construction_expression:: +dump_slice_storage_expression(Ast_dump_context* ast_dump_context) const +{ + if (this->slice_storage_ == NULL) + return; + ast_dump_context->ostream() << "storage=" ; + ast_dump_context->dump_expression(this->slice_storage_); +} + +// Return the backend representation for constructing a slice. + +Bexpression* +Slice_construction_expression::do_get_backend(Translate_context* context) +{ + if (this->array_val_ == NULL) + this->array_val_ = create_array_val(); + if (this->array_val_ == NULL) { - // We need to create a unique value for the empty array literal. - vals = new Expression_list; - vals->push_back(NULL); + go_assert(this->type()->is_error()); + return context->backend()->error_expression(); } - Expression* array_val = - new Fixed_array_construction_expression(this->valtype_, this->indexes(), - vals, loc); - bool is_constant_initializer = array_val->is_immutable(); + Location loc = this->location(); + + bool is_static_initializer = this->array_val_->is_static_initializer(); // We have to copy the initial values into heap memory if we are in - // a function or if the values are not constants. We also have to - // copy them if they may contain pointers in a non-constant context, - // as otherwise the garbage collector won't see them. - bool copy_to_heap = (context->function() != NULL - || !is_constant_initializer - || (element_type->has_pointer() - && !context->is_const())); + // a function or if the values are not constants. + bool copy_to_heap = context->function() != NULL || !is_static_initializer; Expression* space; - if (!copy_to_heap) + + if (this->slice_storage_ != NULL) + { + go_assert(!this->storage_escapes_); + space = Expression::make_unary(OPERATOR_AND, this->slice_storage_, loc); + } + else if (!copy_to_heap) { // The initializer will only run once. - space = Expression::make_unary(OPERATOR_AND, array_val, loc); + space = Expression::make_unary(OPERATOR_AND, this->array_val_, loc); space->unary_expression()->set_is_slice_init(); } else - space = Expression::make_heap_expression(array_val, loc); + { + space = Expression::make_heap_expression(this->array_val_, loc); + Node* n = Node::make_node(this); + if ((n->encoding() & ESCAPE_MASK) == int(Node::ESCAPE_NONE)) + { + n = Node::make_node(space); + n->set_encoding(Node::ESCAPE_NONE); + } + } // Build a constructor for the slice. - Expression* len = this->valtype_->array_type()->length(); Expression* slice_val = Expression::make_slice_value(this->type(), space, len, len, loc); @@ -12342,7 +13147,7 @@ Slice_construction_expression::do_get_backend(Translate_context* context) // Make a slice composite literal. This is used by the type // descriptor code. -Expression* +Slice_construction_expression* Expression::make_slice_composite_literal(Type* type, Expression_list* vals, Location location) { @@ -12432,8 +13237,9 @@ Map_construction_expression::do_flatten(Gogo* gogo, Named_object*, } Expression* element_count = Expression::make_integer_ul(i, NULL, loc); - Type* ctor_type = + Array_type* ctor_type = Type::make_array_type(this->element_type_, element_count); + ctor_type->set_is_array_incomparable(); Expression* constructor = new Fixed_array_construction_expression(ctor_type, NULL, value_pairs, loc); @@ -12487,17 +13293,17 @@ Map_construction_expression::do_check_types(Gogo*) { if (!Type::are_assignable(key_type, (*pv)->type(), NULL)) { - error_at((*pv)->location(), - "incompatible type for element %d key in map construction", - i + 1); + go_error_at((*pv)->location(), + "incompatible type for element %d key in map construction", + i + 1); this->set_is_error(); } ++pv; if (!Type::are_assignable(val_type, (*pv)->type(), NULL)) { - error_at((*pv)->location(), - ("incompatible type for element %d value " - "in map construction"), + go_error_at((*pv)->location(), + ("incompatible type for element %d value " + "in map construction"), i + 1); this->set_is_error(); } @@ -12533,7 +13339,7 @@ Map_construction_expression::do_get_backend(Translate_context* context) Type::make_builtin_struct_type(2, "__key", mt->key_type(), "__val", mt->val_type()); - Expression* descriptor = Expression::make_map_descriptor(mt, loc); + Expression* descriptor = Expression::make_type_descriptor(mt, loc); Type* uintptr_t = Type::lookup_integer_type("uintptr"); Expression* count = Expression::make_integer_ul(i, uintptr_t, loc); @@ -12546,12 +13352,10 @@ Map_construction_expression::do_get_backend(Translate_context* context) this->element_type_->find_local_field("__val", &field_index); Expression* val_offset = Expression::make_struct_field_offset(this->element_type_, valfield); - Expression* val_size = - Expression::make_type_info(mt->val_type(), TYPE_INFO_SIZE); Expression* map_ctor = - Runtime::make_call(Runtime::CONSTRUCT_MAP, loc, 6, descriptor, count, - entry_size, val_offset, val_size, ventries); + Runtime::make_call(Runtime::CONSTRUCT_MAP, loc, 5, descriptor, count, + entry_size, val_offset, ventries); return map_ctor->get_backend(context); } @@ -12680,9 +13484,9 @@ Composite_literal_expression::do_lower(Gogo* gogo, Named_object* function, else { if (!type->is_error()) - error_at(this->location(), - ("may only omit types within composite literals " - "of slice, array, or map type")); + go_error_at(this->location(), + ("may only omit types within composite literals " + "of slice, array, or map type")); return Expression::make_error(this->location()); } } @@ -12706,9 +13510,9 @@ Composite_literal_expression::do_lower(Gogo* gogo, Named_object* function, ret = this->lower_map(gogo, function, inserter, type); else { - error_at(this->location(), - ("expected struct, slice, array, or map type " - "for composite literal")); + go_error_at(this->location(), + ("expected struct, slice, array, or map type " + "for composite literal")); return Expression::make_error(this->location()); } @@ -12738,10 +13542,10 @@ Composite_literal_expression::lower_struct(Gogo* gogo, Type* type) { if (Gogo::is_hidden_name(pf->field_name()) || pf->is_embedded_builtin(gogo)) - error_at(this->location(), - "assignment of unexported field %qs in %qs literal", - Gogo::message_name(pf->field_name()).c_str(), - type->named_type()->message_name().c_str()); + go_error_at(this->location(), + "assignment of unexported field %qs in %qs literal", + Gogo::message_name(pf->field_name()).c_str(), + type->named_type()->message_name().c_str()); } } @@ -12750,7 +13554,7 @@ Composite_literal_expression::lower_struct(Gogo* gogo, Type* type) size_t field_count = st->field_count(); std::vector<Expression*> vals(field_count); - std::vector<int>* traverse_order = new(std::vector<int>); + std::vector<unsigned long>* traverse_order = new(std::vector<unsigned long>); Expression_list::const_iterator p = this->vals_->begin(); Expression* external_expr = NULL; const Named_object* external_no = NULL; @@ -12766,7 +13570,8 @@ Composite_literal_expression::lower_struct(Gogo* gogo, Type* type) if (name_expr == NULL) { - error_at(val->location(), "mixture of field and value initializers"); + go_error_at(val->location(), + "mixture of field and value initializers"); return Expression::make_error(location); } @@ -12808,53 +13613,12 @@ Composite_literal_expression::lower_struct(Gogo* gogo, Type* type) no = name_expr->var_expression()->named_object(); break; - case EXPRESSION_FUNC_REFERENCE: - no = name_expr->func_expression()->named_object(); + case EXPRESSION_ENCLOSED_VAR_REFERENCE: + no = name_expr->enclosed_var_expression()->variable(); break; - case EXPRESSION_UNARY: - // If there is a local variable around with the same name as - // the field, and this occurs in the closure, then the - // parser may turn the field reference into an indirection - // through the closure. FIXME: This is a mess. - { - bad_key = true; - Unary_expression* ue = static_cast<Unary_expression*>(name_expr); - if (ue->op() == OPERATOR_MULT) - { - Field_reference_expression* fre = - ue->operand()->field_reference_expression(); - if (fre != NULL) - { - Struct_type* st = - fre->expr()->type()->deref()->struct_type(); - if (st != NULL) - { - const Struct_field* sf = st->field(fre->field_index()); - name = sf->field_name(); - - // See below. FIXME. - if (!Gogo::is_hidden_name(name) - && name[0] >= 'a' - && name[0] <= 'z') - { - if (gogo->lookup_global(name.c_str()) != NULL) - name = gogo->pack_hidden_name(name, false); - } - - char buf[20]; - snprintf(buf, sizeof buf, "%u", fre->field_index()); - size_t buflen = strlen(buf); - if (name.compare(name.length() - buflen, buflen, buf) - == 0) - { - name = name.substr(0, name.length() - buflen); - bad_key = false; - } - } - } - } - } + case EXPRESSION_FUNC_REFERENCE: + no = name_expr->func_expression()->named_object(); break; default: @@ -12863,7 +13627,7 @@ Composite_literal_expression::lower_struct(Gogo* gogo, Type* type) } if (bad_key) { - error_at(name_expr->location(), "expected struct field name"); + go_error_at(name_expr->location(), "expected struct field name"); return Expression::make_error(location); } @@ -12894,21 +13658,21 @@ Composite_literal_expression::lower_struct(Gogo* gogo, Type* type) const Struct_field* sf = st->find_local_field(name, &index); if (sf == NULL) { - error_at(name_expr->location(), "unknown field %qs in %qs", - Gogo::message_name(name).c_str(), - (type->named_type() != NULL - ? type->named_type()->message_name().c_str() - : "unnamed struct")); + go_error_at(name_expr->location(), "unknown field %qs in %qs", + Gogo::message_name(name).c_str(), + (type->named_type() != NULL + ? type->named_type()->message_name().c_str() + : "unnamed struct")); return Expression::make_error(location); } if (vals[index] != NULL) { - error_at(name_expr->location(), - "duplicate value for field %qs in %qs", - Gogo::message_name(name).c_str(), - (type->named_type() != NULL - ? type->named_type()->message_name().c_str() - : "unnamed struct")); + go_error_at(name_expr->location(), + "duplicate value for field %qs in %qs", + Gogo::message_name(name).c_str(), + (type->named_type() != NULL + ? type->named_type()->message_name().c_str() + : "unnamed struct")); return Expression::make_error(location); } @@ -12916,29 +13680,29 @@ Composite_literal_expression::lower_struct(Gogo* gogo, Type* type) && type->named_type()->named_object()->package() != NULL && (Gogo::is_hidden_name(sf->field_name()) || sf->is_embedded_builtin(gogo))) - error_at(name_expr->location(), - "assignment of unexported field %qs in %qs literal", - Gogo::message_name(sf->field_name()).c_str(), - type->named_type()->message_name().c_str()); + go_error_at(name_expr->location(), + "assignment of unexported field %qs in %qs literal", + Gogo::message_name(sf->field_name()).c_str(), + type->named_type()->message_name().c_str()); vals[index] = val; - traverse_order->push_back(index); + traverse_order->push_back(static_cast<unsigned long>(index)); } if (!this->all_are_names_) { // This is a weird case like bug462 in the testsuite. if (external_expr == NULL) - error_at(this->location(), "unknown field in %qs literal", - (type->named_type() != NULL - ? type->named_type()->message_name().c_str() - : "unnamed struct")); + go_error_at(this->location(), "unknown field in %qs literal", + (type->named_type() != NULL + ? type->named_type()->message_name().c_str() + : "unnamed struct")); else - error_at(external_expr->location(), "unknown field %qs in %qs", - external_no->message_name().c_str(), - (type->named_type() != NULL - ? type->named_type()->message_name().c_str() - : "unnamed struct")); + go_error_at(external_expr->location(), "unknown field %qs in %qs", + external_no->message_name().c_str(), + (type->named_type() != NULL + ? type->named_type()->message_name().c_str() + : "unnamed struct")); return Expression::make_error(location); } @@ -12953,15 +13717,16 @@ Composite_literal_expression::lower_struct(Gogo* gogo, Type* type) return ret; } -// Used to sort an index/value array. +// Index/value/traversal-order triple. -class Index_value_compare -{ - public: - bool - operator()(const std::pair<unsigned long, Expression*>& a, - const std::pair<unsigned long, Expression*>& b) - { return a.first < b.first; } +struct IVT_triple { + unsigned long index; + unsigned long traversal_order; + Expression* expr; + IVT_triple(unsigned long i, unsigned long to, Expression *e) + : index(i), traversal_order(to), expr(e) { } + bool operator<(const IVT_triple& other) const + { return this->index < other.index; } }; // Lower an array composite literal. @@ -13006,8 +13771,8 @@ Composite_literal_expression::lower_array(Type* type) Numeric_constant nc; if (!index_expr->numeric_constant_value(&nc)) { - error_at(index_expr->location(), - "index expression is not integer constant"); + go_error_at(index_expr->location(), + "index expression is not integer constant"); return Expression::make_error(location); } @@ -13016,14 +13781,15 @@ Composite_literal_expression::lower_array(Type* type) case Numeric_constant::NC_UL_VALID: break; case Numeric_constant::NC_UL_NOTINT: - error_at(index_expr->location(), - "index expression is not integer constant"); + go_error_at(index_expr->location(), + "index expression is not integer constant"); return Expression::make_error(location); case Numeric_constant::NC_UL_NEGATIVE: - error_at(index_expr->location(), "index expression is negative"); + go_error_at(index_expr->location(), + "index expression is negative"); return Expression::make_error(location); case Numeric_constant::NC_UL_BIG: - error_at(index_expr->location(), "index value overflow"); + go_error_at(index_expr->location(), "index value overflow"); return Expression::make_error(location); default: go_unreachable(); @@ -13034,15 +13800,16 @@ Composite_literal_expression::lower_array(Type* type) if (sizeof(index) <= static_cast<size_t>(inttype->bits() * 8) && index >> (inttype->bits() - 1) != 0) { - error_at(index_expr->location(), "index value overflow"); + go_error_at(index_expr->location(), "index value overflow"); return Expression::make_error(location); } if (std::find(indexes->begin(), indexes->end(), index) != indexes->end()) { - error_at(index_expr->location(), "duplicate value for index %lu", - index); + go_error_at(index_expr->location(), + "duplicate value for index %lu", + index); return Expression::make_error(location); } @@ -13063,35 +13830,45 @@ Composite_literal_expression::lower_array(Type* type) indexes = NULL; } + std::vector<unsigned long>* traverse_order = NULL; if (indexes_out_of_order) { - typedef std::vector<std::pair<unsigned long, Expression*> > V; + typedef std::vector<IVT_triple> V; V v; v.reserve(indexes->size()); std::vector<unsigned long>::const_iterator pi = indexes->begin(); + unsigned long torder = 0; for (Expression_list::const_iterator pe = vals->begin(); pe != vals->end(); - ++pe, ++pi) - v.push_back(std::make_pair(*pi, *pe)); + ++pe, ++pi, ++torder) + v.push_back(IVT_triple(*pi, torder, *pe)); - std::sort(v.begin(), v.end(), Index_value_compare()); + std::sort(v.begin(), v.end()); delete indexes; delete vals; + indexes = new std::vector<unsigned long>(); indexes->reserve(v.size()); vals = new Expression_list(); vals->reserve(v.size()); + traverse_order = new std::vector<unsigned long>(); + traverse_order->reserve(v.size()); for (V::const_iterator p = v.begin(); p != v.end(); ++p) { - indexes->push_back(p->first); - vals->push_back(p->second); + indexes->push_back(p->index); + vals->push_back(p->expr); + traverse_order->push_back(p->traversal_order); } } - return this->make_array(type, indexes, vals); + Expression* ret = this->make_array(type, indexes, vals); + Array_construction_expression* ace = ret->array_literal(); + if (ace != NULL && traverse_order != NULL) + ace->set_traverse_order(traverse_order); + return ret; } // Actually build the array composite literal. This handles @@ -13120,7 +13897,7 @@ Composite_literal_expression::make_array( if (sizeof(size) <= static_cast<size_t>(it->bits() * 8) && size >> (it->bits() - 1) != 0) { - error_at(location, "too many elements in composite literal"); + go_error_at(location, "too many elements in composite literal"); return Expression::make_error(location); } } @@ -13142,7 +13919,8 @@ Composite_literal_expression::make_array( { if (this->vals_->size() > val) { - error_at(location, "too many elements in composite literal"); + go_error_at(location, + "too many elements in composite literal"); return Expression::make_error(location); } } @@ -13151,9 +13929,9 @@ Composite_literal_expression::make_array( unsigned long max = indexes->back(); if (max >= val) { - error_at(location, - ("some element keys in composite literal " - "are out of range")); + go_error_at(location, + ("some element keys in composite literal " + "are out of range")); return Expression::make_error(location); } } @@ -13179,7 +13957,7 @@ Composite_literal_expression::lower_map(Gogo* gogo, Named_object* function, { if (!this->has_keys_) { - error_at(location, "map composite literal must have keys"); + go_error_at(location, "map composite literal must have keys"); return Expression::make_error(location); } @@ -13190,8 +13968,9 @@ Composite_literal_expression::lower_map(Gogo* gogo, Named_object* function, if (*p == NULL) { ++p; - error_at((*p)->location(), - "map composite literal must have keys for every value"); + go_error_at((*p)->location(), + ("map composite literal must " + "have keys for every value")); return Expression::make_error(location); } // Make sure we have lowered the key; it may not have been @@ -13295,6 +14074,7 @@ Expression::is_variable() const case EXPRESSION_VAR_REFERENCE: case EXPRESSION_TEMPORARY_REFERENCE: case EXPRESSION_SET_AND_USE_TEMPORARY: + case EXPRESSION_ENCLOSED_VAR_REFERENCE: return true; default: return false; @@ -13375,10 +14155,10 @@ Type_guard_expression::do_check_types(Gogo*) this->report_error(_("impossible type assertion: " "type does not implement interface")); else - error_at(this->location(), - ("impossible type assertion: " - "type does not implement interface (%s)"), - reason.c_str()); + go_error_at(this->location(), + ("impossible type assertion: " + "type does not implement interface (%s)"), + reason.c_str()); } this->set_is_error(); } @@ -13442,8 +14222,12 @@ Heap_expression::do_get_backend(Translate_context* context) Location loc = this->location(); Gogo* gogo = context->gogo(); Btype* btype = this->type()->get_backend(gogo); - Bexpression* space = Expression::make_allocation(this->expr_->type(), - loc)->get_backend(context); + + Expression* alloc = Expression::make_allocation(this->expr_->type(), loc); + Node* n = Node::make_node(this); + if ((n->encoding() & ESCAPE_MASK) == int(Node::ESCAPE_NONE)) + alloc->allocation_expression()->set_allocate_on_stack(); + Bexpression* space = alloc->get_backend(context); Bstatement* decl; Named_object* fn = context->function(); @@ -13452,15 +14236,16 @@ Heap_expression::do_get_backend(Translate_context* context) Bvariable* space_temp = gogo->backend()->temporary_variable(fndecl, context->bblock(), btype, space, true, loc, &decl); - space = gogo->backend()->var_expression(space_temp, loc); + space = gogo->backend()->var_expression(space_temp, VE_lvalue, loc); Btype* expr_btype = this->expr_->type()->get_backend(gogo); Bexpression* ref = gogo->backend()->indirect_expression(expr_btype, space, true, loc); Bexpression* bexpr = this->expr_->get_backend(context); - Bstatement* assn = gogo->backend()->assignment_statement(ref, bexpr, loc); + Bstatement* assn = gogo->backend()->assignment_statement(fndecl, ref, + bexpr, loc); decl = gogo->backend()->compound_statement(decl, assn); - space = gogo->backend()->var_expression(space_temp, loc); + space = gogo->backend()->var_expression(space_temp, VE_rvalue, loc); return gogo->backend()->compound_expression(decl, space, loc); } @@ -13576,9 +14361,8 @@ Receive_expression::do_get_backend(Translate_context* context) Expression* recv_addr = Expression::make_temporary_reference(this->temp_receiver_, loc); recv_addr = Expression::make_unary(OPERATOR_AND, recv_addr, loc); - Expression* recv = - Runtime::make_call(Runtime::RECEIVE, loc, 3, - td, this->channel_, recv_addr); + Expression* recv = Runtime::make_call(Runtime::CHANRECV1, loc, 3, + td, this->channel_, recv_addr); return Expression::make_compound(recv, recv_ref, loc)->get_backend(context); } @@ -13619,7 +14403,7 @@ class Type_descriptor_expression : public Expression { return Type::make_type_descriptor_ptr_type(); } bool - do_is_immutable() const + do_is_static_initializer() const { return true; } void @@ -13687,7 +14471,7 @@ class GC_symbol_expression : public Expression { return Type::lookup_integer_type("uintptr"); } bool - do_is_immutable() const + do_is_static_initializer() const { return true; } void @@ -13745,7 +14529,7 @@ class Type_info_expression : public Expression protected: bool - do_is_immutable() const + do_is_static_initializer() const { return true; } Type* @@ -14103,16 +14887,27 @@ Interface_info_expression::do_type() { case INTERFACE_INFO_METHODS: { + typedef Unordered_map(Interface_type*, Type*) Hashtable; + static Hashtable result_types; + + Interface_type* itype = this->iface_->type()->interface_type(); + + Hashtable::const_iterator p = result_types.find(itype); + if (p != result_types.end()) + return p->second; + Type* pdt = Type::make_type_descriptor_ptr_type(); - if (this->iface_->type()->interface_type()->is_empty()) - return pdt; + if (itype->is_empty()) + { + result_types[itype] = pdt; + return pdt; + } Location loc = this->location(); Struct_field_list* sfl = new Struct_field_list(); sfl->push_back( Struct_field(Typed_identifier("__type_descriptor", pdt, loc))); - Interface_type* itype = this->iface_->type()->interface_type(); for (Typed_identifier_list::const_iterator p = itype->methods()->begin(); p != itype->methods()->end(); ++p) @@ -14145,7 +14940,11 @@ Interface_info_expression::do_type() sfl->push_back(Struct_field(Typed_identifier(fname, mft, loc))); } - return Type::make_pointer_type(Type::make_struct_type(sfl, loc)); + Struct_type* st = Type::make_struct_type(sfl, loc); + st->set_is_struct_incomparable(); + Pointer_type *pt = Type::make_pointer_type(st); + result_types[itype] = pt; + return pt; } case INTERFACE_INFO_OBJECT: return Type::make_pointer_type(Type::make_void_type()); @@ -14313,7 +15112,7 @@ class Interface_mtable_expression : public Expression do_type(); bool - is_immutable() const + do_is_static_initializer() const { return true; } void @@ -14377,7 +15176,9 @@ Interface_mtable_expression::do_type() p != interface_methods->end(); ++p) sfl->push_back(Struct_field(*p)); - this->method_table_type_ = Type::make_struct_type(sfl, this->location()); + Struct_type* st = Type::make_struct_type(sfl, this->location()); + st->set_is_struct_incomparable(); + this->method_table_type_ = st; return this->method_table_type_; } @@ -14387,7 +15188,8 @@ Interface_mtable_expression::do_get_backend(Translate_context* context) Gogo* gogo = context->gogo(); Location loc = Linemap::predeclared_location(); if (this->bvar_ != NULL) - return gogo->backend()->var_expression(this->bvar_, this->location()); + return gogo->backend()->var_expression(this->bvar_, VE_rvalue, + this->location()); const Typed_identifier_list* interface_methods = this->itype_->methods(); go_assert(!interface_methods->empty()); @@ -14419,9 +15221,12 @@ Interface_mtable_expression::do_get_backend(Translate_context* context) && this->type_->named_type()->named_object()->package() != NULL) { Btype* btype = this->type()->get_backend(gogo); + std::string asm_name(go_selectively_encode_id(mangled_name)); this->bvar_ = - gogo->backend()->immutable_struct_reference(mangled_name, btype, loc); - return gogo->backend()->var_expression(this->bvar_, this->location()); + gogo->backend()->immutable_struct_reference(mangled_name, asm_name, + btype, loc); + return gogo->backend()->var_expression(this->bvar_, VE_rvalue, + this->location()); } // The first element is the type descriptor. @@ -14464,11 +15269,12 @@ Interface_mtable_expression::do_get_backend(Translate_context* context) Bexpression* ctor = mtable->get_backend(context); bool is_public = has_hidden_methods && this->type_->named_type() != NULL; - this->bvar_ = gogo->backend()->immutable_struct(mangled_name, false, + std::string asm_name(go_selectively_encode_id(mangled_name)); + this->bvar_ = gogo->backend()->immutable_struct(mangled_name, asm_name, false, !is_public, btype, loc); gogo->backend()->immutable_struct_set_init(this->bvar_, mangled_name, false, !is_public, btype, loc, ctor); - return gogo->backend()->var_expression(this->bvar_, loc); + return gogo->backend()->var_expression(this->bvar_, VE_lvalue, loc); } void @@ -14504,7 +15310,7 @@ class Struct_field_offset_expression : public Expression protected: bool - do_is_immutable() const + do_is_static_initializer() const { return true; } Type* @@ -14581,64 +15387,6 @@ Expression::make_struct_field_offset(Struct_type* type, return new Struct_field_offset_expression(type, field); } -// An expression which evaluates to a pointer to the map descriptor of -// a map type. - -class Map_descriptor_expression : public Expression -{ - public: - Map_descriptor_expression(Map_type* type, Location location) - : Expression(EXPRESSION_MAP_DESCRIPTOR, location), - type_(type) - { } - - protected: - Type* - do_type() - { return Type::make_pointer_type(Map_type::make_map_descriptor_type()); } - - void - do_determine_type(const Type_context*) - { } - - Expression* - do_copy() - { return this; } - - Bexpression* - do_get_backend(Translate_context* context) - { - return this->type_->map_descriptor_pointer(context->gogo(), - this->location()); - } - - void - do_dump_expression(Ast_dump_context*) const; - - private: - // The type for which this is the descriptor. - Map_type* type_; -}; - -// Dump ast representation for a map descriptor expression. - -void -Map_descriptor_expression::do_dump_expression( - Ast_dump_context* ast_dump_context) const -{ - ast_dump_context->ostream() << "map_descriptor("; - ast_dump_context->dump_type(this->type_); - ast_dump_context->ostream() << ")"; -} - -// Make a map descriptor expression. - -Expression* -Expression::make_map_descriptor(Map_type* type, Location location) -{ - return new Map_descriptor_expression(type, location); -} - // An expression which evaluates to the address of an unnamed label. class Label_addr_expression : public Expression @@ -14734,7 +15482,8 @@ Conditional_expression::do_get_backend(Translate_context* context) Bexpression* cond = this->cond_->get_backend(context); Bexpression* then = this->then_->get_backend(context); Bexpression* belse = this->else_->get_backend(context); - return gogo->backend()->conditional_expression(result_btype, cond, then, + Bfunction* bfn = context->function()->func_value()->get_decl(); + return gogo->backend()->conditional_expression(bfn, result_btype, cond, then, belse, this->location()); } @@ -14799,7 +15548,9 @@ Compound_expression::do_get_backend(Translate_context* context) { Gogo* gogo = context->gogo(); Bexpression* binit = this->init_->get_backend(context); - Bstatement* init_stmt = gogo->backend()->expression_statement(binit); + Bfunction* bfunction = context->function()->func_value()->get_decl(); + Bstatement* init_stmt = gogo->backend()->expression_statement(bfunction, + binit); Bexpression* bexpr = this->expr_->get_backend(context); return gogo->backend()->compound_expression(init_stmt, bexpr, this->location()); @@ -14826,6 +15577,28 @@ Expression::make_compound(Expression* init, Expression* expr, Location location) return new Compound_expression(init, expr, location); } +// Class Backend_expression. + +int +Backend_expression::do_traverse(Traverse*) +{ + return TRAVERSE_CONTINUE; +} + +void +Backend_expression::do_dump_expression(Ast_dump_context* ast_dump_context) const +{ + ast_dump_context->ostream() << "backend_expression<"; + ast_dump_context->dump_type(this->type_); + ast_dump_context->ostream() << ">"; +} + +Expression* +Expression::make_backend(Bexpression* bexpr, Type* type, Location location) +{ + return new Backend_expression(bexpr, type, location); +} + // Import an expression. This comes at the end in order to see the // various class definitions. @@ -14855,7 +15628,7 @@ Expression::import_expression(Import* imp) return Type_conversion_expression::do_import(imp); else { - error_at(imp->location(), "import error: expected expression"); + go_error_at(imp->location(), "import error: expected expression"); return Expression::make_error(imp->location()); } } @@ -15295,8 +16068,8 @@ Numeric_constant::check_int_type(Integer_type* type, bool issue_error, { if (issue_error) { - error_at(location, - "floating point constant truncated to integer"); + go_error_at(location, + "floating point constant truncated to integer"); this->set_invalid(); } return false; @@ -15311,7 +16084,7 @@ Numeric_constant::check_int_type(Integer_type* type, bool issue_error, { if (issue_error) { - error_at(location, "complex constant truncated to integer"); + go_error_at(location, "complex constant truncated to integer"); this->set_invalid(); } return false; @@ -15352,7 +16125,7 @@ Numeric_constant::check_int_type(Integer_type* type, bool issue_error, if (!ret && issue_error) { - error_at(location, "integer constant overflow"); + go_error_at(location, "integer constant overflow"); this->set_invalid(); } @@ -15384,7 +16157,7 @@ Numeric_constant::check_float_type(Float_type* type, bool issue_error, if (issue_error) { this->set_invalid(); - error_at(location, "complex constant truncated to float"); + go_error_at(location, "complex constant truncated to float"); } return false; } @@ -15449,7 +16222,7 @@ Numeric_constant::check_float_type(Float_type* type, bool issue_error, if (!ret && issue_error) { - error_at(location, "floating point constant overflow"); + go_error_at(location, "floating point constant overflow"); this->set_invalid(); } @@ -15507,7 +16280,7 @@ Numeric_constant::check_complex_type(Complex_type* type, bool issue_error, { if (issue_error) { - error_at(location, "complex real part overflow"); + go_error_at(location, "complex real part overflow"); this->set_invalid(); } ret = false; @@ -15520,7 +16293,7 @@ Numeric_constant::check_complex_type(Complex_type* type, bool issue_error, { if (issue_error) { - error_at(location, "complex imaginary part overflow"); + go_error_at(location, "complex imaginary part overflow"); this->set_invalid(); } ret = false; diff --git a/gcc/go/gofrontend/expressions.h b/gcc/go/gofrontend/expressions.h index c33e63653e..adf9eabe15 100644 --- a/gcc/go/gofrontend/expressions.h +++ b/gcc/go/gofrontend/expressions.h @@ -11,6 +11,7 @@ #include <mpc.h> #include "operator.h" +#include "runtime.h" class Gogo; class Translate_context; @@ -28,6 +29,7 @@ class Struct_type; class Struct_field; class Expression_list; class Var_expression; +class Enclosed_var_expression; class Temporary_reference_expression; class Set_and_use_temporary_expression; class String_expression; @@ -35,6 +37,7 @@ class Type_conversion_expression; class Unsafe_type_conversion_expression; class Unary_expression; class Binary_expression; +class String_concat_expression; class Call_expression; class Call_result_expression; class Func_expression; @@ -42,6 +45,7 @@ class Func_descriptor_expression; class Unknown_expression; class Index_expression; class Array_index_expression; +class String_index_expression; class Map_index_expression; class Bound_method_expression; class Field_reference_expression; @@ -82,8 +86,10 @@ class Expression EXPRESSION_TYPE, EXPRESSION_UNARY, EXPRESSION_BINARY, + EXPRESSION_STRING_CONCAT, EXPRESSION_CONST_REFERENCE, EXPRESSION_VAR_REFERENCE, + EXPRESSION_ENCLOSED_VAR_REFERENCE, EXPRESSION_TEMPORARY_REFERENCE, EXPRESSION_SET_AND_USE_TEMPORARY, EXPRESSION_SINK, @@ -129,10 +135,10 @@ class Expression EXPRESSION_INTERFACE_VALUE, EXPRESSION_INTERFACE_MTABLE, EXPRESSION_STRUCT_FIELD_OFFSET, - EXPRESSION_MAP_DESCRIPTOR, EXPRESSION_LABEL_ADDR, EXPRESSION_CONDITIONAL, - EXPRESSION_COMPOUND + EXPRESSION_COMPOUND, + EXPRESSION_BACKEND }; Expression(Expression_classification, Location); @@ -157,6 +163,10 @@ class Expression static Expression* make_binary(Operator, Expression*, Expression*, Location); + // Make a string concatenation expression. + static Expression* + make_string_concat(Expression_list*); + // Make a reference to a constant in an expression. static Expression* make_const_reference(Named_object*, Location); @@ -165,6 +175,10 @@ class Expression static Expression* make_var_reference(Named_object*, Location); + // Make a reference to a variable within an enclosing function. + static Expression* + make_enclosing_var_reference(Expression*, Named_object*, Location); + // Make a reference to a temporary variable. Temporary variables // are always created by a single statement, which is what we use to // refer to them. @@ -367,7 +381,7 @@ class Expression make_array_composite_literal(Type*, Expression_list*, Location); // Make a slice composite literal. - static Expression* + static Slice_construction_expression* make_slice_composite_literal(Type*, Expression_list*, Location); // Take an expression and allocate it on the heap. @@ -459,11 +473,6 @@ class Expression static Expression* make_struct_field_offset(Struct_type*, const Struct_field*); - // Make an expression which evaluates to the address of the map - // descriptor for TYPE. - static Expression* - make_map_descriptor(Map_type* type, Location); - // Make an expression which evaluates to the address of an unnamed // label. static Expression* @@ -477,6 +486,10 @@ class Expression static Expression* make_compound(Expression*, Expression*, Location); + // Make a backend expression. + static Expression* + make_backend(Bexpression*, Type*, Location); + // Return the expression classification. Expression_classification classification() const @@ -492,10 +505,20 @@ class Expression is_constant() const { return this->do_is_constant(); } - // Return whether this is an immutable expression. - bool - is_immutable() const - { return this->do_is_immutable(); } + // Return whether this expression can be used as a static + // initializer. This is true for an expression that has only + // numbers and pointers to global variables or composite literals + // that do not require runtime initialization. It is false if we + // must generate code to compute this expression when it is used to + // initialize a global variable. This is not a language-level + // concept, but an implementation-level one. If this expression is + // used to initialize a global variable, this is true if we can pass + // an initializer to the backend, false if we must generate code to + // initialize the variable. It is always safe for this method to + // return false, but the resulting code may be less efficient. + bool + is_static_initializer() const + { return this->do_is_static_initializer(); } // If this is not a numeric constant, return false. If it is one, // return true, and set VAL to hold the value. @@ -538,6 +561,20 @@ class Expression var_expression() const { return this->convert<const Var_expression, EXPRESSION_VAR_REFERENCE>(); } + // If this is a enclosed_variable reference, return the + // Enclosed_var_expression structure. Otherwise, return NULL. + // This is a controlled dynamic cast. + Enclosed_var_expression* + enclosed_var_expression() + { return this->convert<Enclosed_var_expression, + EXPRESSION_ENCLOSED_VAR_REFERENCE>(); } + + const Enclosed_var_expression* + enclosed_var_expression() const + { return this->convert<const Enclosed_var_expression, + EXPRESSION_ENCLOSED_VAR_REFERENCE>(); } + + // If this is a reference to a temporary variable, return the // Temporary_reference_expression. Otherwise, return NULL. Temporary_reference_expression* @@ -604,6 +641,14 @@ class Expression binary_expression() { return this->convert<Binary_expression, EXPRESSION_BINARY>(); } + // If this is a string concatenation expression, return the + // String_concat_expression structure. Otherwise, return NULL. + String_concat_expression* + string_concat_expression() + { + return this->convert<String_concat_expression, EXPRESSION_STRING_CONCAT>(); + } + // If this is a call expression, return the Call_expression // structure. Otherwise, return NULL. This is a controlled dynamic // cast. @@ -654,6 +699,13 @@ class Expression array_index_expression() { return this->convert<Array_index_expression, EXPRESSION_ARRAY_INDEX>(); } + // If this is an expression which refers to indexing in a string, + // return the String_index_expression structure. Otherwise, return + // NULL. + String_index_expression* + string_index_expression() + { return this->convert<String_index_expression, EXPRESSION_STRING_INDEX>(); } + // If this is an expression which refers to indexing in a map, // return the Map_index_expression structure. Otherwise, return // NULL. @@ -954,9 +1006,10 @@ class Expression do_is_constant() const { return false; } - // Return whether this is an immutable expression. + // Return whether this expression can be used as a constant + // initializer. virtual bool - do_is_immutable() const + do_is_static_initializer() const { return false; } // Return whether this is a constant expression of numeric type, and @@ -1044,6 +1097,18 @@ class Expression virtual void do_dump_expression(Ast_dump_context*) const = 0; + // Varargs lowering creates a slice object (unnamed compiler temp) + // to contain the variable length collection of values. The enum + // below tells the lowering routine whether it can mark that temp + // as non-escaping or not. For general varargs calls it is not always + // safe to stack-allocated the storage, but for specific cases (ex: + // call to append()) it is legal. + enum Slice_storage_escape_disp + { + SLICE_STORAGE_MAY_ESCAPE, + SLICE_STORAGE_DOES_NOT_ESCAPE + }; + private: // Convert to the desired statement classification, or return NULL. // This is a controlled dynamic cast. @@ -1217,7 +1282,7 @@ class Var_expression : public Expression public: Var_expression(Named_object* variable, Location location) : Expression(EXPRESSION_VAR_REFERENCE, location), - variable_(variable) + variable_(variable), in_lvalue_pos_(VE_rvalue) { } // Return the variable. @@ -1225,6 +1290,16 @@ class Var_expression : public Expression named_object() const { return this->variable_; } + // Does this var expression appear in an lvalue (assigned-to) context? + bool + in_lvalue_pos() const + { return this->in_lvalue_pos_ == VE_lvalue; } + + // Mark a var_expression as appearing in an lvalue context. + void + set_in_lvalue_pos() + { this->in_lvalue_pos_ = VE_lvalue; } + protected: Expression* do_lower(Gogo*, Named_object*, Statement_inserter*, int); @@ -1255,6 +1330,73 @@ class Var_expression : public Expression private: // The variable we are referencing. Named_object* variable_; + // Set to TRUE if var expression appears in lvalue context + Varexpr_context in_lvalue_pos_; +}; + +// A reference to a variable within an enclosing function. + +class Enclosed_var_expression : public Expression +{ + public: + Enclosed_var_expression(Expression* reference, Named_object* variable, + Location location) + : Expression(EXPRESSION_ENCLOSED_VAR_REFERENCE, location), + reference_(reference), variable_(variable) + { } + + // The reference to the enclosed variable. This will be an indirection of the + // the field stored within closure variable. + Expression* + reference() const + { return this->reference_; } + + // The variable being enclosed and referenced. + Named_object* + variable() const + { return this->variable_; } + + protected: + int + do_traverse(Traverse*); + + Expression* + do_lower(Gogo*, Named_object*, Statement_inserter*, int); + + Expression* + do_flatten(Gogo*, Named_object*, Statement_inserter*); + + Type* + do_type() + { return this->reference_->type(); } + + void + do_determine_type(const Type_context* context) + { return this->reference_->determine_type(context); } + + Expression* + do_copy() + { return this; } + + bool + do_is_addressable() const + { return this->reference_->is_addressable(); } + + void + do_address_taken(bool escapes); + + Bexpression* + do_get_backend(Translate_context* context) + { return this->reference_->get_backend(context); } + + void + do_dump_expression(Ast_dump_context*) const; + + private: + // The reference to the enclosed variable. + Expression* reference_; + // The variable being enclosed. + Named_object* variable_; }; // A reference to a temporary variable. @@ -1394,7 +1536,7 @@ class String_expression : public Expression { return true; } bool - do_is_immutable() const + do_is_static_initializer() const { return true; } bool @@ -1481,7 +1623,7 @@ class Type_conversion_expression : public Expression do_is_constant() const; bool - do_is_immutable() const; + do_is_static_initializer() const; bool do_numeric_constant_value(Numeric_constant*) const; @@ -1545,7 +1687,7 @@ class Unsafe_type_conversion_expression : public Expression do_traverse(Traverse* traverse); bool - do_is_immutable() const; + do_is_static_initializer() const; Type* do_type() @@ -1633,10 +1775,11 @@ class Unary_expression : public Expression } // Apply unary opcode OP to UNC, setting NC. Return true if this - // could be done, false if not. Issue errors for overflow. + // could be done, false if not. On overflow, issues an error and + // sets *ISSUED_ERROR. static bool eval_constant(Operator op, const Numeric_constant* unc, - Location, Numeric_constant* nc); + Location, Numeric_constant* nc, bool *issued_error); static Expression* do_import(Import*); @@ -1656,11 +1799,7 @@ class Unary_expression : public Expression do_is_constant() const; bool - do_is_immutable() const - { - return (this->expr_->is_immutable() - || (this->op_ == OPERATOR_AND && this->expr_->is_variable())); - } + do_is_static_initializer() const; bool do_numeric_constant_value(Numeric_constant*) const; @@ -1703,6 +1842,9 @@ class Unary_expression : public Expression { this->issue_nil_check_ = (this->op_ == OPERATOR_MULT); } private: + static bool + base_is_static_initializer(Expression*); + // The unary operator to apply. Operator op_; // Normally true. False if this is an address expression which does @@ -1755,11 +1897,11 @@ class Binary_expression : public Expression // Apply binary opcode OP to LEFT_NC and RIGHT_NC, setting NC. // Return true if this could be done, false if not. Issue errors at - // LOCATION as appropriate. + // LOCATION as appropriate, and sets *ISSUED_ERROR if it did. static bool eval_constant(Operator op, Numeric_constant* left_nc, Numeric_constant* right_nc, Location location, - Numeric_constant* nc); + Numeric_constant* nc, bool* issued_error); // Compare constants LEFT_NC and RIGHT_NC according to OP, setting // *RESULT. Return true if this could be done, false if not. Issue @@ -1777,6 +1919,13 @@ class Binary_expression : public Expression static bool check_operator_type(Operator op, Type* type, Type* otype, Location); + // Set *RESULT_TYPE to the resulting type when OP is applied to + // operands of type LEFT_TYPE and RIGHT_TYPE. Return true on + // success, false on failure. + static bool + operation_type(Operator op, Type* left_type, Type* right_type, + Type** result_type); + protected: int do_traverse(Traverse* traverse); @@ -1792,8 +1941,7 @@ class Binary_expression : public Expression { return this->left_->is_constant() && this->right_->is_constant(); } bool - do_is_immutable() const - { return this->left_->is_immutable() && this->right_->is_immutable(); } + do_is_static_initializer() const; bool do_numeric_constant_value(Numeric_constant*) const; @@ -1828,10 +1976,6 @@ class Binary_expression : public Expression private: static bool - operation_type(Operator op, Type* left_type, Type* right_type, - Type** result_type); - - static bool cmp_to_bool(Operator op, int cmp); static bool @@ -1880,6 +2024,69 @@ class Binary_expression : public Expression Type* type_; }; +// A string concatenation expression. This is a sequence of strings +// added together. It is created when lowering Binary_expression. + +class String_concat_expression : public Expression +{ + public: + String_concat_expression(Expression_list* exprs) + : Expression(EXPRESSION_STRING_CONCAT, exprs->front()->location()), + exprs_(exprs) + { } + + // Return the list of string expressions to be concatenated. + Expression_list* + exprs() + { return this->exprs_; } + + protected: + int + do_traverse(Traverse* traverse) + { return this->exprs_->traverse(traverse); } + + Expression* + do_lower(Gogo*, Named_object*, Statement_inserter*, int) + { return this; } + + Expression* + do_flatten(Gogo*, Named_object*, Statement_inserter*); + + bool + do_is_constant() const; + + bool + do_is_static_initializer() const; + + Type* + do_type(); + + void + do_determine_type(const Type_context*); + + void + do_check_types(Gogo*); + + Expression* + do_copy() + { return Expression::make_string_concat(this->exprs_->copy()); } + + Bexpression* + do_get_backend(Translate_context*) + { go_unreachable(); } + + void + do_export(Export*) const + { go_unreachable(); } + + void + do_dump_expression(Ast_dump_context*) const; + + private: + // The string expressions to concatenate. + Expression_list* exprs_; +}; + // A call expression. The go statement needs to dig inside this. class Call_expression : public Expression @@ -1891,8 +2098,8 @@ class Call_expression : public Expression fn_(fn), args_(args), type_(NULL), results_(NULL), call_(NULL), call_temp_(NULL), expected_result_count_(0), is_varargs_(is_varargs), varargs_are_lowered_(false), types_are_determined_(false), - is_deferred_(false), issued_error_(false), is_multi_value_arg_(false), - is_flattened_(false) + is_deferred_(false), is_concurrent_(false), issued_error_(false), + is_multi_value_arg_(false), is_flattened_(false) { } // The function to call. @@ -1963,6 +2170,16 @@ class Call_expression : public Expression set_is_deferred() { this->is_deferred_ = true; } + // Whether this call is concurrently executed. + bool + is_concurrent() const + { return this->is_concurrent_; } + + // Note that the call is concurrently executed. + void + set_is_concurrent() + { this->is_concurrent_ = true; } + // We have found an error with this call expression; return true if // we should report it. bool @@ -2030,7 +2247,8 @@ class Call_expression : public Expression // Let a builtin expression lower varargs. void lower_varargs(Gogo*, Named_object* function, Statement_inserter* inserter, - Type* varargs_type, size_t param_count); + Type* varargs_type, size_t param_count, + Slice_storage_escape_disp escape_disp); // Let a builtin expression check whether types have been // determined. @@ -2045,6 +2263,9 @@ class Call_expression : public Expression check_argument_type(int, const Type*, const Type*, Location, bool); Expression* + lower_to_builtin(Named_object**, const char*, int); + + Expression* interface_method_function(Interface_field_reference_expression*, Expression**); @@ -2076,6 +2297,8 @@ class Call_expression : public Expression bool types_are_determined_; // True if the call is an argument to a defer statement. bool is_deferred_; + // True if the call is an argument to a go statement. + bool is_concurrent_; // True if we reported an error about a mismatch between call // results and uses. This is to avoid producing multiple errors // when there are multiple Call_result_expressions. @@ -2149,7 +2372,8 @@ class Func_expression : public Expression Func_expression(Named_object* function, Expression* closure, Location location) : Expression(EXPRESSION_FUNC_REFERENCE, location), - function_(function), closure_(closure) + function_(function), closure_(closure), + runtime_code_(Runtime::NUMBER_OF_FUNCTIONS) { } // Return the object associated with the function. @@ -2163,6 +2387,23 @@ class Func_expression : public Expression closure() { return this->closure_; } + // Return whether this is a reference to a runtime function. + bool + is_runtime_function() const + { return this->runtime_code_ != Runtime::NUMBER_OF_FUNCTIONS; } + + // Return the runtime code for this function expression. + // Returns Runtime::NUMBER_OF_FUNCTIONS if this is not a reference to a + // runtime function. + Runtime::Function + runtime_code() const + { return this->runtime_code_; } + + // Set the runtime code for this function expression. + void + set_runtime_code(Runtime::Function code) + { this->runtime_code_ = code; } + // Return a backend expression for the code of a function. static Bexpression* get_code_pointer(Gogo*, Named_object* function, Location loc); @@ -2204,6 +2445,8 @@ class Func_expression : public Expression // be a struct holding pointers to all the variables referenced by // this function and defined in enclosing functions. Expression* closure_; + // The runtime code for the referenced function. + Runtime::Function runtime_code_; }; // A function descriptor. A function descriptor is a struct with a @@ -2323,14 +2566,9 @@ class Index_expression : public Parser_expression Index_expression(Expression* left, Expression* start, Expression* end, Expression* cap, Location location) : Parser_expression(EXPRESSION_INDEX, location), - left_(left), start_(start), end_(end), cap_(cap), is_lvalue_(false) + left_(left), start_(start), end_(end), cap_(cap) { } - // Record that this expression is an lvalue. - void - set_is_lvalue() - { this->is_lvalue_ = true; } - // Dump an index expression, i.e. an expression of the form // expr[expr], expr[expr:expr], or expr[expr:expr:expr] to a dump context. static void @@ -2383,9 +2621,6 @@ class Index_expression : public Parser_expression // default capacity, non-NULL for indices and slices that specify the // capacity. Expression* cap_; - // Whether this is being used as an l-value. We set this during the - // parse because map index expressions need to know. - bool is_lvalue_; }; // An array index. This is used for both indexing and slicing. @@ -2408,6 +2643,26 @@ class Array_index_expression : public Expression array() const { return this->array_; } + // Return the index of a simple index expression, or the start index + // of a slice expression. + Expression* + start() + { return this->start_; } + + const Expression* + start() const + { return this->start_; } + + // Return the end index of a slice expression. This is NULL for a + // simple index expression. + Expression* + end() + { return this->end_; } + + const Expression* + end() const + { return this->end_; } + protected: int do_traverse(Traverse*); @@ -2477,6 +2732,72 @@ class Array_index_expression : public Expression Type* type_; }; +// A string index. This is used for both indexing and slicing. + +class String_index_expression : public Expression +{ + public: + String_index_expression(Expression* string, Expression* start, + Expression* end, Location location) + : Expression(EXPRESSION_STRING_INDEX, location), + string_(string), start_(start), end_(end) + { } + + // Return the string being indexed. + Expression* + string() const + { return this->string_; } + + protected: + int + do_traverse(Traverse*); + + Expression* + do_flatten(Gogo*, Named_object*, Statement_inserter*); + + Type* + do_type(); + + void + do_determine_type(const Type_context*); + + void + do_check_types(Gogo*); + + Expression* + do_copy() + { + return Expression::make_string_index(this->string_->copy(), + this->start_->copy(), + (this->end_ == NULL + ? NULL + : this->end_->copy()), + this->location()); + } + + bool + do_must_eval_subexpressions_in_order(int* skip) const + { + *skip = 1; + return true; + } + + Bexpression* + do_get_backend(Translate_context*); + + void + do_dump_expression(Ast_dump_context*) const; + + private: + // The string we are getting a value from. + Expression* string_; + // The start or only index. + Expression* start_; + // The end index of a slice. This may be NULL for a single index, + // or it may be a nil expression for the length of the string. + Expression* end_; +}; + // An index into a map. class Map_index_expression : public Expression @@ -2485,8 +2806,7 @@ class Map_index_expression : public Expression Map_index_expression(Expression* map, Expression* index, Location location) : Expression(EXPRESSION_MAP_INDEX, location), - map_(map), index_(index), is_lvalue_(false), - is_in_tuple_assignment_(false), value_pointer_(NULL) + map_(map), index_(index), value_pointer_(NULL) { } // Return the map. @@ -2511,31 +2831,12 @@ class Map_index_expression : public Expression Map_type* get_map_type() const; - // Record that this map expression is an lvalue. The difference is - // that an lvalue always inserts the key. - void - set_is_lvalue() - { this->is_lvalue_ = true; } - - // Return whether this map expression occurs in an assignment to a - // pair of values. - bool - is_in_tuple_assignment() const - { return this->is_in_tuple_assignment_; } - - // Record that this map expression occurs in an assignment to a pair - // of values. - void - set_is_in_tuple_assignment() - { this->is_in_tuple_assignment_ = true; } - - // Return an expression for the map index. This returns an expression which - // evaluates to a pointer to a value in the map. If INSERT is true, - // the key will be inserted if not present, and the value pointer - // will be zero initialized. If INSERT is false, and the key is not - // present in the map, the pointer will be NULL. + // Return an expression for the map index. This returns an + // expression that evaluates to a pointer to a value in the map. If + // the key is not present in the map, this will return a pointer to + // the zero value. Expression* - get_value_pointer(bool insert); + get_value_pointer(Gogo*); protected: int @@ -2581,10 +2882,6 @@ class Map_index_expression : public Expression Expression* map_; // The index. Expression* index_; - // Whether this is an lvalue. - bool is_lvalue_; - // Whether this is in a tuple assignment to a pair of values. - bool is_in_tuple_assignment_; // A pointer to the value at this index. Expression* value_pointer_; }; @@ -2638,7 +2935,7 @@ class Bound_method_expression : public Expression do_traverse(Traverse*); Expression* - do_lower(Gogo*, Named_object*, Statement_inserter*, int); + do_flatten(Gogo*, Named_object*, Statement_inserter*); Type* do_type(); @@ -2657,7 +2954,8 @@ class Bound_method_expression : public Expression } Bexpression* - do_get_backend(Translate_context*); + do_get_backend(Translate_context*) + { go_unreachable(); } void do_dump_expression(Ast_dump_context*) const; @@ -2981,37 +3279,70 @@ class Composite_literal_expression : public Parser_expression std::vector<bool> key_path_; }; -// Construct a struct. +// Helper/mixin class for struct and array construction expressions; +// encapsulates a list of values plus an optional traversal order +// recording the order in which the values should be visited. -class Struct_construction_expression : public Expression +class Ordered_value_list { public: - Struct_construction_expression(Type* type, Expression_list* vals, - Location location) - : Expression(EXPRESSION_STRUCT_CONSTRUCTION, location), - type_(type), vals_(vals), traverse_order_(NULL) + Ordered_value_list(Expression_list* vals) + : vals_(vals), traverse_order_(NULL) { } + Expression_list* + vals() const + { return this->vals_; } + + int + traverse_vals(Traverse* traverse); + + // Get the traversal order (may be NULL) + std::vector<unsigned long>* + traverse_order() + { return traverse_order_; } + // Set the traversal order, used to ensure that we implement the // order of evaluation rules. Takes ownership of the argument. void - set_traverse_order(std::vector<int>* traverse_order) + set_traverse_order(std::vector<unsigned long>* traverse_order) { this->traverse_order_ = traverse_order; } - // Return whether this is a constant initializer. + private: + // The list of values, in order of the fields in the struct or in + // order of indices in an array. A NULL value of vals_ means that + // all fields/slots should be zero-initialized; a single NULL entry + // in the list means that the corresponding field or array slot + // should be zero-initialized. + Expression_list* vals_; + // If not NULL, the order in which to traverse vals_. This is used + // so that we implement the order of evaluation rules correctly. + std::vector<unsigned long>* traverse_order_; +}; + +// Construct a struct. + +class Struct_construction_expression : public Expression, + public Ordered_value_list +{ + public: + Struct_construction_expression(Type* type, Expression_list* vals, + Location location) + : Expression(EXPRESSION_STRUCT_CONSTRUCTION, location), + Ordered_value_list(vals), + type_(type) + { } + + // Return whether this is a constant initializer. bool is_constant_struct() const; - Expression_list* - vals() const - { return this->vals_; } - protected: int do_traverse(Traverse* traverse); bool - do_is_immutable() const; + do_is_static_initializer() const; Type* do_type() @@ -3028,12 +3359,12 @@ class Struct_construction_expression : public Expression { Struct_construction_expression* ret = new Struct_construction_expression(this->type_, - (this->vals_ == NULL + (this->vals() == NULL ? NULL - : this->vals_->copy()), + : this->vals()->copy()), this->location()); - if (this->traverse_order_ != NULL) - ret->set_traverse_order(this->traverse_order_); + if (this->traverse_order() != NULL) + ret->set_traverse_order(this->traverse_order()); return ret; } @@ -3052,19 +3383,14 @@ class Struct_construction_expression : public Expression private: // The type of the struct to construct. Type* type_; - // The list of values, in order of the fields in the struct. A NULL - // entry means that the field should be zero-initialized. - Expression_list* vals_; - // If not NULL, the order in which to traverse vals_. This is used - // so that we implement the order of evaluation rules correctly. - std::vector<int>* traverse_order_; }; // Construct an array. This class is not used directly; instead we // use the child classes, Fixed_array_construction_expression and // Slice_construction_expression. -class Array_construction_expression : public Expression +class Array_construction_expression : public Expression, + public Ordered_value_list { protected: Array_construction_expression(Expression_classification classification, @@ -3072,7 +3398,8 @@ class Array_construction_expression : public Expression const std::vector<unsigned long>* indexes, Expression_list* vals, Location location) : Expression(classification, location), - type_(type), indexes_(indexes), vals_(vals) + Ordered_value_list(vals), + type_(type), indexes_(indexes) { go_assert(indexes == NULL || indexes->size() == vals->size()); } public: @@ -3083,19 +3410,14 @@ class Array_construction_expression : public Expression // Return the number of elements. size_t element_count() const - { return this->vals_ == NULL ? 0 : this->vals_->size(); } - - // The list of values. - Expression_list* - vals() const - { return this->vals_; } + { return this->vals() == NULL ? 0 : this->vals()->size(); } protected: virtual int do_traverse(Traverse* traverse); bool - do_is_immutable() const; + do_is_static_initializer() const; Type* do_type() @@ -3125,14 +3447,15 @@ protected: void do_dump_expression(Ast_dump_context*) const; + virtual void + dump_slice_storage_expression(Ast_dump_context*) const { } + private: // The type of the array to construct. Type* type_; // The list of indexes into the array, one for each value. This may // be NULL, in which case the indexes start at zero and increment. const std::vector<unsigned long>* indexes_; - // The list of values. This may be NULL if there are no values. - Expression_list* vals_; }; // Construct a fixed array. @@ -3169,6 +3492,18 @@ class Slice_construction_expression : public Array_construction_expression Slice_construction_expression(Type* type, const std::vector<unsigned long>* indexes, Expression_list* vals, Location location); + + Expression* + do_flatten(Gogo*, Named_object*, Statement_inserter*); + + // Record that the storage for this slice (e.g. vals) cannot escape, + // hence it can be stack-allocated. + void + set_storage_does_not_escape() + { + this->storage_escapes_ = false; + } + protected: // Note that taking the address of a slice literal is invalid. @@ -3188,9 +3523,25 @@ class Slice_construction_expression : public Array_construction_expression Bexpression* do_get_backend(Translate_context*); + void + dump_slice_storage_expression(Ast_dump_context* ast_dump_context) const; + + // Create an array value for the constructed slice. Invoked during + // flattening if slice storage does not escape, otherwise invoked + // later on during do_get_backend(). + Expression* + create_array_val(); + private: // The type of the values in this slice. Type* valtype_; + // Array value expression, optionally filled in during flattening. + Expression* array_val_; + // Slice storage expression, optionally filled in during flattening. + Expression* slice_storage_; + // Normally true. Can be set to false if we know that the resulting + // storage for the slice cannot escape. + bool storage_escapes_; }; // Construct a map. @@ -3515,6 +3866,54 @@ class Compound_expression : public Expression Expression* expr_; }; +// A backend expression. This is a backend expression wrapped in an +// Expression, for convenience during backend generation. + +class Backend_expression : public Expression +{ + public: + Backend_expression(Bexpression* bexpr, Type* type, Location location) + : Expression(EXPRESSION_BACKEND, location), bexpr_(bexpr), type_(type) + {} + + protected: + int + do_traverse(Traverse*); + + // For now these are always valid static initializers. If that + // changes we can change this. + bool + do_is_static_initializer() const + { return true; } + + Type* + do_type() + { return this->type_; } + + void + do_determine_type(const Type_context*) + { } + + Expression* + do_copy() + { + return new Backend_expression(this->bexpr_, this->type_, this->location()); + } + + Bexpression* + do_get_backend(Translate_context*) + { return this->bexpr_; } + + void + do_dump_expression(Ast_dump_context*) const; + + private: + // The backend expression we are wrapping. + Bexpression* bexpr_; + // The type of the expression; + Type* type_; +}; + // A numeric constant. This is used both for untyped constants and // for constants that have a type. diff --git a/gcc/go/gofrontend/go-diagnostics.cc b/gcc/go/gofrontend/go-diagnostics.cc new file mode 100644 index 0000000000..21e45b3af3 --- /dev/null +++ b/gcc/go/gofrontend/go-diagnostics.cc @@ -0,0 +1,177 @@ +// go-diagnostics.cc -- Go error/warning diagnostics utilities. + +// Copyright 2016 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-diagnostics.h" + +static std::string +mformat_value() +{ + return std::string(xstrerror(errno)); +} + +// Rewrite a format string to expand any extensions not +// supported by sprintf(). See comments in go-diagnostics.h +// for list of supported format specifiers. + +static std::string +expand_format(const char* fmt) +{ + std::stringstream ss; + for (const char* c = fmt; *c; ++c) + { + if (*c != '%') + { + ss << *c; + continue; + } + c++; + switch (*c) + { + case '\0': + { + // malformed format string + go_unreachable(); + } + case '%': + { + ss << "%"; + break; + } + case 'm': + { + ss << mformat_value(); + break; + } + case '<': + { + ss << go_open_quote(); + break; + } + case '>': + { + ss << go_close_quote(); + break; + } + case 'q': + { + ss << go_open_quote(); + c++; + if (*c == 'm') + { + ss << mformat_value(); + } + else + { + ss << "%" << *c; + } + ss << go_close_quote(); + break; + } + default: + { + ss << "%" << *c; + } + } + } + return ss.str(); +} + +// Expand message format specifiers, using a combination of +// expand_format above to handle extensions (ex: %m, %q) and vasprintf() +// to handle regular printf-style formatting. A pragma is being used here to +// suppress this warning: +// +// warning: function ‘std::__cxx11::string expand_message(const char*, __va_list_tag*)’ might be a candidate for ‘gnu_printf’ format attribute [-Wsuggest-attribute=format] +// +// What appears to be happening here is that the checker is deciding that +// because of the call to vasprintf() (which has attribute gnu_printf), the +// calling function must need to have attribute gnu_printf as well, even +// though there is already an attribute declaration for it. + +static std::string +expand_message(const char* fmt, va_list ap) GO_ATTRIBUTE_GCC_DIAG(1,0); + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wsuggest-attribute=format" + +static std::string +expand_message(const char* fmt, va_list ap) +{ + char* mbuf = 0; + std::string expanded_fmt = expand_format(fmt); + int nwr = vasprintf(&mbuf, expanded_fmt.c_str(), ap); + if (nwr == -1) + { + // memory allocation failed + go_be_error_at(Linemap::unknown_location(), + "memory allocation failed in vasprintf"); + go_assert(0); + } + std::string rval = std::string(mbuf); + free(mbuf); + return rval; +} + +#pragma GCC diagnostic pop + +static const char* cached_open_quote = NULL; +static const char* cached_close_quote = NULL; + +const char* +go_open_quote() +{ + if (cached_open_quote == NULL) + go_be_get_quotechars(&cached_open_quote, &cached_close_quote); + return cached_open_quote; +} + +const char* +go_close_quote() +{ + if (cached_close_quote == NULL) + go_be_get_quotechars(&cached_open_quote, &cached_close_quote); + return cached_close_quote; +} + +void +go_error_at(const Location location, const char* fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + go_be_error_at(location, expand_message(fmt, ap)); + va_end(ap); +} + +void +go_warning_at(const Location location, int opt, const char* fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + go_be_warning_at(location, opt, expand_message(fmt, ap)); + va_end(ap); +} + +void +go_fatal_error(const Location location, const char* fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + go_be_fatal_error(location, expand_message(fmt, ap)); + va_end(ap); +} + +void +go_inform(const Location location, const char* fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + go_be_inform(location, expand_message(fmt, ap)); + va_end(ap); +} diff --git a/gcc/go/gofrontend/go-diagnostics.h b/gcc/go/gofrontend/go-diagnostics.h new file mode 100644 index 0000000000..70c97cbf8c --- /dev/null +++ b/gcc/go/gofrontend/go-diagnostics.h @@ -0,0 +1,64 @@ +// go-diagnostics.h -- interface to diagnostic reporting -*- C++ -*- + +// Copyright 2016 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. + +#ifndef GO_DIAGNOSTICS_H +#define GO_DIAGNOSTICS_H + +#include "go-linemap.h" + +#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 1) +#define GO_ATTRIBUTE_GCC_DIAG(m, n) __attribute__ ((__format__ (__gcc_tdiag__, m, n))) __attribute__ ((__nonnull__ (m))) +#else +#define GO_ATTRIBUTE_GCC_DIAG(m, n) +#endif + +// These declarations define the interface through which the frontend +// reports errors and warnings. These functions accept printf-like +// format specifiers (e.g. %d, %f, %s, etc), with the following additional +// extensions: +// +// 1. 'q' qualifier may be applied to a specifier to add quoting, e.g. +// %qd produces a quoted decimal output, %qs a quoted string output. +// [This extension is supported only with single-character format +// specifiers]. +// +// 2. %m specifier outputs value of "strerror(errno)" at time of call. +// +// 3. %< outputs an opening quote, %> a closing quote. +// +// All other format specifiers are as defined by 'sprintf'. The final resulting +// message is then sent to the back end via go_be_error_at/go_be_warning_at. + +extern void go_error_at(const Location, const char* fmt, ...) + GO_ATTRIBUTE_GCC_DIAG(2,3); +extern void go_warning_at(const Location, int opt, const char* fmt, ...) + GO_ATTRIBUTE_GCC_DIAG(3,4); +extern void go_fatal_error(const Location, const char* fmt, ...) + GO_ATTRIBUTE_GCC_DIAG(2,3); +extern void go_inform(const Location, const char* fmt, ...) + GO_ATTRIBUTE_GCC_DIAG(2,3); + +// These interfaces provide a way for the front end to ask for +// the open/close quote characters it should use when formatting +// diagnostics (warnings, errors). +extern const char* go_open_quote(); +extern const char* go_close_quote(); + +// These interfaces are used by utilities above to pass warnings and +// errors (once format specifiers have been expanded) to the back end, +// and to determine quoting style. Avoid calling these routines directly; +// instead use the equivalent routines above. The back end is required to +// implement these routines. + +extern void go_be_error_at(const Location, const std::string& errmsg); +extern void go_be_warning_at(const Location, int opt, + const std::string& warningmsg); +extern void go_be_fatal_error(const Location, const std::string& errmsg); +extern void go_be_inform(const Location, const std::string& infomsg); +extern void go_be_get_quotechars(const char** open_quote, + const char** close_quote); + +#endif // !defined(GO_DIAGNOSTICS_H) diff --git a/gcc/go/gofrontend/go-encode-id.cc b/gcc/go/gofrontend/go-encode-id.cc new file mode 100644 index 0000000000..978f20823d --- /dev/null +++ b/gcc/go/gofrontend/go-encode-id.cc @@ -0,0 +1,113 @@ +// go-encode-id.cc -- Go identifier encoding hooks + +// Copyright 2016 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-location.h" +#include "go-linemap.h" +#include "go-encode-id.h" + +// Return whether the character c is OK to use in the assembler. + +static bool +char_needs_encoding(char c) +{ + switch (c) + { + case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': + case 'G': case 'H': case 'I': case 'J': case 'K': case 'L': + case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R': + case 'S': case 'T': case 'U': case 'V': case 'W': case 'X': + case 'Y': case 'Z': + case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': + case 'g': case 'h': case 'i': case 'j': case 'k': case 'l': + case 'm': case 'n': case 'o': case 'p': case 'q': case 'r': + case 's': case 't': case 'u': case 'v': case 'w': case 'x': + case 'y': case 'z': + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + case '_': case '.': case '$': case '/': + return false; + default: + return true; + } +} + +// Return whether the identifier needs to be translated because it +// contains non-ASCII characters. + +bool +go_id_needs_encoding(const std::string& str) +{ + for (std::string::const_iterator p = str.begin(); + p != str.end(); + ++p) + if (char_needs_encoding(*p)) + return true; + return false; +} + +// Pull the next UTF-8 character out of P and store it in *PC. Return +// the number of bytes read. + +static size_t +fetch_utf8_char(const char* p, unsigned int* pc) +{ + unsigned char c = *p; + if ((c & 0x80) == 0) + { + *pc = c; + return 1; + } + size_t len = 0; + while ((c & 0x80) != 0) + { + ++len; + c <<= 1; + } + unsigned int rc = *p & ((1 << (7 - len)) - 1); + for (size_t i = 1; i < len; i++) + { + unsigned int u = p[i]; + rc <<= 6; + rc |= u & 0x3f; + } + *pc = rc; + return len; +} + +// Encode an identifier using ASCII characters. + +std::string +go_encode_id(const std::string &id) +{ + std::string ret; + const char* p = id.c_str(); + const char* pend = p + id.length(); + while (p < pend) + { + unsigned int c; + size_t len = fetch_utf8_char(p, &c); + if (len == 1 && !char_needs_encoding(c)) + ret += c; + else + { + ret += "$U"; + char buf[30]; + snprintf(buf, sizeof buf, "%x", c); + ret += buf; + ret += "$"; + } + p += len; + } + return ret; +} + +std::string +go_selectively_encode_id(const std::string &id) +{ + if (go_id_needs_encoding(id)) + return go_encode_id(id); + return std::string(); +} diff --git a/gcc/go/gofrontend/go-encode-id.h b/gcc/go/gofrontend/go-encode-id.h new file mode 100644 index 0000000000..b95d97dd1b --- /dev/null +++ b/gcc/go/gofrontend/go-encode-id.h @@ -0,0 +1,30 @@ +// go-encode-id.h -- Go identifier encoding utilities -*- C++ -*- + +// Copyright 2016 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. + +#ifndef GO_ENCODE_ID_H +#define GO_ENCODE_ID_H + +#include "backend.h" + +// Given an identifier corresponding to a function or variable, +// this helper returns TRUE if the identifier needs special +// encoding to be used as an ASM name (symbol), FALSE if the name +// is OK as is. +extern bool +go_id_needs_encoding(const std::string& str); + +// Encodes the specified identifier for ASM name safety, returning a +// string with the encoded value. +extern std::string +go_encode_id(const std::string &id); + +// Returns the empty string if the specified name needs encoding, +// otherwise invokes go_encode_id() on the name and returns the +// result. +extern std::string +go_selectively_encode_id(const std::string &id); + +#endif // !defined(GO_ENCODE_ID_H) diff --git a/gcc/go/gofrontend/go-linemap.h b/gcc/go/gofrontend/go-linemap.h index ffbcbe7781..704efdbfa5 100644 --- a/gcc/go/gofrontend/go-linemap.h +++ b/gcc/go/gofrontend/go-linemap.h @@ -17,7 +17,6 @@ // The type is normally passed by value rather than by reference, and // it should support that efficiently. The type should be defined in // "go-location.h". - #include "go-location.h" // The Linemap class is a pure abstract interface, plus some static @@ -58,6 +57,16 @@ class Linemap virtual void stop() = 0; + // Produce a human-readable description of a Location, e.g. + // "foo.go:10". Returns an empty string for predeclared, builtin or + // unknown locations. + virtual std::string + to_string(Location) = 0; + + // Return the line number for a given location (for debugging dumps) + virtual int + location_line(Location) = 0; + protected: // Return a special Location used for predeclared identifiers. This // Location should be different from that for any actual source @@ -122,10 +131,22 @@ class Linemap go_assert(Linemap::instance_ != NULL); return Linemap::instance_->is_unknown(loc); } -}; -// The backend interface must define this function. It should return -// a fully implemented instance of Linemap. -extern Linemap* go_get_linemap(); + // Produce a human-readable description of a Location. + static std::string + location_to_string(Location loc) + { + go_assert(Linemap::instance_ != NULL); + return Linemap::instance_->to_string(loc); + } + + // Return line number for location + static int + location_to_line(Location loc) + { + go_assert(Linemap::instance_ != NULL); + return Linemap::instance_->location_line(loc); + } +}; #endif // !defined(GO_LINEMAP_H) diff --git a/gcc/go/gofrontend/go-sha1.h b/gcc/go/gofrontend/go-sha1.h new file mode 100644 index 0000000000..22db5a2b4b --- /dev/null +++ b/gcc/go/gofrontend/go-sha1.h @@ -0,0 +1,33 @@ +// go-sha1.h -- GCC specific sha1 checksum utilities. -*- C++ -*- + +// Copyright 2016 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. + +#ifndef GO_SHA1_H +#define GO_SHA1_H + +#include "go-system.h" + +// +// Interface class for computation of SHA1 checksums. Front end requests +// one of these objects from the back end to use for computing +// checksums (each back end tends to have a different SHA1 implementation). +// Back ends are expected to create a new class that derives from this +// one containing an implementation. +// + +class Go_sha1_helper +{ + public: + virtual ~Go_sha1_helper() { } + virtual void process_bytes(const void* buffer, size_t len) = 0; + virtual std::string finish() = 0; + static const int checksum_len = 20; +}; + +// Call to create and return a new sha1 helper (this routine defined +// by the backend). Caller is responsible for deletion. +extern Go_sha1_helper* go_create_sha1_helper(); + +#endif // !defined(GO_SHA1_H) diff --git a/gcc/go/gofrontend/go.cc b/gcc/go/gofrontend/go.cc index 98cf6501af..7050accbc5 100644 --- a/gcc/go/gofrontend/go.cc +++ b/gcc/go/gofrontend/go.cc @@ -7,6 +7,7 @@ #include "go-system.h" #include "go-c.h" +#include "go-diagnostics.h" #include "lex.h" #include "parse.h" @@ -20,25 +21,26 @@ static Gogo* gogo; GO_EXTERN_C void -go_create_gogo(int int_type_size, int pointer_size, const char *pkgpath, - const char *prefix, const char *relative_import_path, - bool check_divide_by_zero, bool check_divide_overflow) +go_create_gogo(const struct go_create_gogo_args* args) { go_assert(::gogo == NULL); - Linemap* linemap = go_get_linemap(); - ::gogo = new Gogo(go_get_backend(), linemap, int_type_size, pointer_size); - - if (pkgpath != NULL) - ::gogo->set_pkgpath(pkgpath); - else if (prefix != NULL) - ::gogo->set_prefix(prefix); - - if (relative_import_path != NULL) - ::gogo->set_relative_import_path(relative_import_path); - if (check_divide_by_zero) - ::gogo->set_check_divide_by_zero(check_divide_by_zero); - if (check_divide_overflow) - ::gogo->set_check_divide_overflow(check_divide_overflow); + ::gogo = new Gogo(args->backend, args->linemap, args->int_type_size, + args->pointer_size); + + if (args->pkgpath != NULL) + ::gogo->set_pkgpath(args->pkgpath); + else if (args->prefix != NULL) + ::gogo->set_prefix(args->prefix); + + if (args->relative_import_path != NULL) + ::gogo->set_relative_import_path(args->relative_import_path); + ::gogo->set_check_divide_by_zero(args->check_divide_by_zero); + ::gogo->set_check_divide_overflow(args->check_divide_overflow); + if (args->compiling_runtime) + ::gogo->set_compiling_runtime(args->compiling_runtime); + if (args->c_header != NULL) + ::gogo->set_c_header(args->c_header); + ::gogo->set_debug_escape_level(args->debug_escape_level); } // Parse the input files. @@ -50,6 +52,7 @@ go_parse_input_files(const char** filenames, unsigned int filename_count, { go_assert(filename_count > 0); + Lex::Linknames all_linknames; for (unsigned int i = 0; i < filename_count; ++i) { if (i > 0) @@ -63,8 +66,8 @@ go_parse_input_files(const char** filenames, unsigned int filename_count, { file = fopen(filename, "r"); if (file == NULL) - fatal_error(Linemap::unknown_location(), - "cannot open %s: %m", filename); + go_fatal_error(Linemap::unknown_location(), + "cannot open %s: %m", filename); } Lex lexer(filename, file, ::gogo->linemap()); @@ -74,6 +77,21 @@ go_parse_input_files(const char** filenames, unsigned int filename_count, if (strcmp(filename, "-") != 0) fclose(file); + + Lex::Linknames* linknames = lexer.get_and_clear_linknames(); + if (linknames != NULL) + { + if (!::gogo->current_file_imported_unsafe()) + { + for (Lex::Linknames::const_iterator p = linknames->begin(); + p != linknames->end(); + ++p) + go_error_at(p->second.loc, + ("//go:linkname only allowed in Go files that " + "import \"unsafe\"")); + } + all_linknames.insert(linknames->begin(), linknames->end()); + } } ::gogo->linemap()->stop(); @@ -84,6 +102,13 @@ go_parse_input_files(const char** filenames, unsigned int filename_count, // define them now. ::gogo->define_global_names(); + // Apply any go:linkname directives. + for (Lex::Linknames::const_iterator p = all_linknames.begin(); + p != all_linknames.end(); + ++p) + ::gogo->add_linkname(p->first, p->second.is_exported, p->second.ext_name, + p->second.loc); + // Finalize method lists and build stub methods for named types. ::gogo->finalize_methods(); @@ -110,9 +135,7 @@ go_parse_input_files(const char** filenames, unsigned int filename_count, if (only_check_syntax) return; - // Consider escape analysis information when deciding if a variable should - // live on the heap or on the stack. - ::gogo->optimize_allocations(filenames); + ::gogo->analyze_escape(); // Export global identifiers as appropriate. ::gogo->do_exports(); diff --git a/gcc/go/gofrontend/gogo.cc b/gcc/go/gofrontend/gogo.cc index 5413cc937f..a190917512 100644 --- a/gcc/go/gofrontend/gogo.cc +++ b/gcc/go/gofrontend/gogo.cc @@ -6,9 +6,13 @@ #include "go-system.h" +#include <fstream> + #include "filenames.h" #include "go-c.h" +#include "go-diagnostics.h" +#include "go-encode-id.h" #include "go-dump.h" #include "go-optimize.h" #include "lex.h" @@ -18,7 +22,6 @@ #include "runtime.h" #include "import.h" #include "export.h" -#include "escape.h" #include "backend.h" #include "gogo.h" @@ -33,6 +36,7 @@ Gogo::Gogo(Backend* backend, Linemap* linemap, int, int pointer_size) file_block_names_(), imports_(), imported_unsafe_(false), + current_file_imported_unsafe_(false), packages_(), init_functions_(), var_deps_(), @@ -46,11 +50,18 @@ Gogo::Gogo(Backend* backend, Linemap* linemap, int, int pointer_size) pkgpath_from_option_(false), prefix_from_option_(false), relative_import_path_(), + c_header_(), + check_divide_by_zero_(true), + check_divide_overflow_(true), + compiling_runtime_(false), + debug_escape_level_(0), verify_types_(), interface_types_(), specific_type_functions_(), specific_type_functions_are_written_(false), - named_types_are_converted_(false) + named_types_are_converted_(false), + analysis_sets_(), + gc_roots_() { const Location loc = Linemap::predeclared_location(); @@ -102,12 +113,14 @@ Gogo::Gogo(Backend* backend, Linemap* linemap, int, int pointer_size) uint8_type->integer_type()->set_is_byte(); Named_object* byte_type = Named_object::make_type("byte", NULL, uint8_type, loc); + byte_type->type_value()->set_is_alias(); this->add_named_type(byte_type->type_value()); // "rune" is an alias for "int32". int32_type->integer_type()->set_is_rune(); Named_object* rune_type = Named_object::make_type("rune", NULL, int32_type, loc); + rune_type->type_value()->set_is_alias(); this->add_named_type(rune_type->type_value()); this->add_named_type(Type::make_named_bool_type()); @@ -156,19 +169,11 @@ Gogo::Gogo(Backend* backend, Linemap* linemap, int, int pointer_size) Function_type* new_type = Type::make_function_type(NULL, NULL, NULL, loc); new_type->set_is_varargs(); new_type->set_is_builtin(); - Node::Escape_states* new_escapes = - new Node::Escape_states(1, Node::ESCAPE_NONE); - new_type->set_parameter_escape_states(new_escapes); - new_type->set_has_escape_info(); this->globals_->add_function_declaration("new", NULL, new_type, loc); Function_type* make_type = Type::make_function_type(NULL, NULL, NULL, loc); make_type->set_is_varargs(); make_type->set_is_builtin(); - Node::Escape_states* make_escapes = - new Node::Escape_states(2, Node::ESCAPE_NONE); - make_type->set_parameter_escape_states(make_escapes); - make_type->set_has_escape_info(); this->globals_->add_function_declaration("make", NULL, make_type, loc); Typed_identifier_list* len_result = new Typed_identifier_list(); @@ -176,10 +181,6 @@ Gogo::Gogo(Backend* backend, Linemap* linemap, int, int pointer_size) Function_type* len_type = Type::make_function_type(NULL, NULL, len_result, loc); len_type->set_is_builtin(); - Node::Escape_states* len_escapes = - new Node::Escape_states(1, Node::ESCAPE_NONE); - len_type->set_parameter_escape_states(len_escapes); - len_type->set_has_escape_info(); this->globals_->add_function_declaration("len", NULL, len_type, loc); Typed_identifier_list* cap_result = new Typed_identifier_list(); @@ -187,26 +188,16 @@ Gogo::Gogo(Backend* backend, Linemap* linemap, int, int pointer_size) Function_type* cap_type = Type::make_function_type(NULL, NULL, len_result, loc); cap_type->set_is_builtin(); - Node::Escape_states* cap_escapes = - new Node::Escape_states(1, Node::ESCAPE_NONE); - cap_type->set_parameter_escape_states(cap_escapes); - cap_type->set_has_escape_info(); this->globals_->add_function_declaration("cap", NULL, cap_type, loc); Function_type* print_type = Type::make_function_type(NULL, NULL, NULL, loc); print_type->set_is_varargs(); print_type->set_is_builtin(); - Node::Escape_states* print_escapes = - new Node::Escape_states(1, Node::ESCAPE_NONE); - print_type->set_parameter_escape_states(print_escapes); - print_type->set_has_escape_info(); this->globals_->add_function_declaration("print", NULL, print_type, loc); print_type = Type::make_function_type(NULL, NULL, NULL, loc); print_type->set_is_varargs(); print_type->set_is_builtin(); - print_type->set_parameter_escape_states(print_escapes); - print_type->set_has_escape_info(); this->globals_->add_function_declaration("println", NULL, print_type, loc); Type *empty = Type::make_empty_interface_type(loc); @@ -215,10 +206,6 @@ Gogo::Gogo(Backend* backend, Linemap* linemap, int, int pointer_size) Function_type *panic_type = Type::make_function_type(NULL, panic_parms, NULL, loc); panic_type->set_is_builtin(); - Node::Escape_states* panic_escapes = - new Node::Escape_states(1, Node::ESCAPE_ARG); - panic_type->set_parameter_escape_states(panic_escapes); - panic_type->set_has_escape_info(); this->globals_->add_function_declaration("panic", NULL, panic_type, loc); Typed_identifier_list* recover_result = new Typed_identifier_list(); @@ -232,10 +219,6 @@ Gogo::Gogo(Backend* backend, Linemap* linemap, int, int pointer_size) Function_type* close_type = Type::make_function_type(NULL, NULL, NULL, loc); close_type->set_is_varargs(); close_type->set_is_builtin(); - Node::Escape_states* close_escapes = - new Node::Escape_states(1, Node::ESCAPE_NONE); - close_type->set_parameter_escape_states(close_escapes); - close_type->set_has_escape_info(); this->globals_->add_function_declaration("close", NULL, close_type, loc); Typed_identifier_list* copy_result = new Typed_identifier_list(); @@ -244,56 +227,31 @@ Gogo::Gogo(Backend* backend, Linemap* linemap, int, int pointer_size) copy_result, loc); copy_type->set_is_varargs(); copy_type->set_is_builtin(); - Node::Escape_states* copy_escapes = - new Node::Escape_states(2, Node::ESCAPE_NONE); - copy_type->set_parameter_escape_states(copy_escapes); - copy_type->set_has_escape_info(); 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(); - Node::Escape_states* append_escapes = new Node::Escape_states; - append_escapes->push_back(Node::ESCAPE_ARG); - append_escapes->push_back(Node::ESCAPE_NONE); - append_type->set_parameter_escape_states(append_escapes); - append_type->set_has_escape_info(); this->globals_->add_function_declaration("append", NULL, append_type, loc); Function_type* complex_type = Type::make_function_type(NULL, NULL, NULL, loc); complex_type->set_is_varargs(); complex_type->set_is_builtin(); - Node::Escape_states* complex_escapes = - new Node::Escape_states(2, Node::ESCAPE_NONE); - complex_type->set_parameter_escape_states(complex_escapes); - complex_type->set_has_escape_info(); this->globals_->add_function_declaration("complex", NULL, complex_type, loc); Function_type* real_type = Type::make_function_type(NULL, NULL, NULL, loc); real_type->set_is_varargs(); real_type->set_is_builtin(); - Node::Escape_states* real_escapes = - new Node::Escape_states(1, Node::ESCAPE_NONE); - real_type->set_parameter_escape_states(real_escapes); - real_type->set_has_escape_info(); this->globals_->add_function_declaration("real", NULL, real_type, loc); Function_type* imag_type = Type::make_function_type(NULL, NULL, NULL, loc); imag_type->set_is_varargs(); imag_type->set_is_builtin(); - Node::Escape_states* imag_escapes = - new Node::Escape_states(1, Node::ESCAPE_NONE); - imag_type->set_parameter_escape_states(imag_escapes); - imag_type->set_has_escape_info(); this->globals_->add_function_declaration("imag", NULL, imag_type, loc); Function_type* delete_type = Type::make_function_type(NULL, NULL, NULL, loc); delete_type->set_is_varargs(); delete_type->set_is_builtin(); - Node::Escape_states* delete_escapes = - new Node::Escape_states(2, Node::ESCAPE_NONE); - delete_type->set_parameter_escape_states(delete_escapes); - delete_type->set_has_escape_info(); this->globals_->add_function_declaration("delete", NULL, delete_type, loc); } @@ -386,8 +344,8 @@ Gogo::set_package_name(const std::string& package_name, if (this->package_ != NULL) { if (this->package_->package_name() != package_name) - error_at(location, "expected package %<%s%>", - Gogo::message_name(this->package_->package_name()).c_str()); + go_error_at(location, "expected package %<%s%>", + Gogo::message_name(this->package_->package_name()).c_str()); return; } @@ -446,11 +404,12 @@ void Gogo::import_package(const std::string& filename, const std::string& local_name, bool is_local_name_exported, + bool must_exist, Location location) { if (filename.empty()) { - error_at(location, "import path is empty"); + go_error_at(location, "import path is empty"); return; } @@ -462,32 +421,33 @@ Gogo::import_package(const std::string& filename, int adv = Lex::fetch_char(pf, &c); if (adv == 0) { - error_at(location, "import path contains invalid UTF-8 sequence"); + go_error_at(location, "import path contains invalid UTF-8 sequence"); return; } if (c == '\0') { - error_at(location, "import path contains NUL"); + go_error_at(location, "import path contains NUL"); return; } if (c < 0x20 || c == 0x7f) { - error_at(location, "import path contains control character"); + go_error_at(location, "import path contains control character"); return; } if (c == '\\') { - error_at(location, "import path contains backslash; use slash"); + go_error_at(location, "import path contains backslash; use slash"); return; } if (Lex::is_unicode_space(c)) { - error_at(location, "import path contains space character"); + go_error_at(location, "import path contains space character"); return; } if (c < 0x7f && strchr("!\"#$%&'()*,:;<=>?[]^`{|}", c) != NULL) { - error_at(location, "import path contains invalid character '%c'", c); + go_error_at(location, + "import path contains invalid character '%c'", c); return; } pf += adv; @@ -495,16 +455,17 @@ Gogo::import_package(const std::string& filename, if (IS_ABSOLUTE_PATH(filename.c_str())) { - error_at(location, "import path cannot be absolute path"); + go_error_at(location, "import path cannot be absolute path"); return; } if (local_name == "init") - error_at(location, "cannot import package as init"); + go_error_at(location, "cannot import package as init"); if (filename == "unsafe") { this->import_unsafe(local_name, is_local_name_exported, location); + this->current_file_imported_unsafe_ = true; return; } @@ -547,7 +508,8 @@ Gogo::import_package(const std::string& filename, this->relative_import_path_); if (stream == NULL) { - error_at(location, "import file %qs not found", filename.c_str()); + if (must_exist) + go_error_at(location, "import file %qs not found", filename.c_str()); return; } @@ -557,9 +519,9 @@ Gogo::import_package(const std::string& filename, if (package != NULL) { if (package->pkgpath() == this->pkgpath()) - error_at(location, - ("imported package uses same package path as package " - "being compiled (see -fgo-pkgpath option)")); + go_error_at(location, + ("imported package uses same package path as package " + "being compiled (see -fgo-pkgpath option)")); this->imports_.insert(std::make_pair(filename, package)); } @@ -567,40 +529,50 @@ Gogo::import_package(const std::string& filename, delete stream; } +Import_init * +Gogo::lookup_init(const std::string& init_name) +{ + Import_init tmp("", init_name, -1); + Import_init_set::iterator it = this->imported_init_fns_.find(&tmp); + return (it != this->imported_init_fns_.end()) ? *it : NULL; +} + // Add an import control function for an imported package to the list. void Gogo::add_import_init_fn(const std::string& package_name, const std::string& init_name, int prio) { - for (std::set<Import_init>::const_iterator p = + for (Import_init_set::iterator p = this->imported_init_fns_.begin(); p != this->imported_init_fns_.end(); ++p) { - if (p->init_name() == init_name) + Import_init *ii = (*p); + if (ii->init_name() == init_name) { // If a test of package P1, built as part of package P1, // imports package P2, and P2 imports P1 (perhaps // indirectly), then we will see the same import name with // different import priorities. That is OK, so don't give // an error about it. - if (p->package_name() != package_name) + if (ii->package_name() != package_name) { - error("duplicate package initialization name %qs", - Gogo::message_name(init_name).c_str()); - inform(UNKNOWN_LOCATION, "used by package %qs at priority %d", - Gogo::message_name(p->package_name()).c_str(), - p->priority()); - inform(UNKNOWN_LOCATION, " and by package %qs at priority %d", - Gogo::message_name(package_name).c_str(), prio); + go_error_at(Linemap::unknown_location(), + "duplicate package initialization name %qs", + Gogo::message_name(init_name).c_str()); + go_inform(Linemap::unknown_location(), "used by package %qs", + Gogo::message_name(ii->package_name()).c_str()); + go_inform(Linemap::unknown_location(), " and by package %qs", + Gogo::message_name(package_name).c_str()); } - return; + ii->set_priority(prio); + return; } } - this->imported_init_fns_.insert(Import_init(package_name, init_name, - prio)); + Import_init* nii = new Import_init(package_name, init_name, prio); + this->imported_init_fns_.insert(nii); } // Return whether we are at the global binding level. @@ -635,12 +607,68 @@ Gogo::current_bindings() const return this->globals_; } +void +Gogo::update_init_priority(Import_init* ii, + std::set<const Import_init *>* visited) +{ + visited->insert(ii); + int succ_prior = -1; + + for (std::set<std::string>::const_iterator pci = + ii->precursors().begin(); + pci != ii->precursors().end(); + ++pci) + { + Import_init* succ = this->lookup_init(*pci); + if (visited->find(succ) == visited->end()) + update_init_priority(succ, visited); + succ_prior = std::max(succ_prior, succ->priority()); + } + if (ii->priority() <= succ_prior) + ii->set_priority(succ_prior + 1); +} + +void +Gogo::recompute_init_priorities() +{ + std::set<Import_init *> nonroots; + + for (Import_init_set::const_iterator p = + this->imported_init_fns_.begin(); + p != this->imported_init_fns_.end(); + ++p) + { + const Import_init *ii = *p; + for (std::set<std::string>::const_iterator pci = + ii->precursors().begin(); + pci != ii->precursors().end(); + ++pci) + { + Import_init* ii = this->lookup_init(*pci); + nonroots.insert(ii); + } + } + + // Recursively update priorities starting at roots. + std::set<const Import_init*> visited; + for (Import_init_set::iterator p = + this->imported_init_fns_.begin(); + p != this->imported_init_fns_.end(); + ++p) + { + Import_init* ii = *p; + if (nonroots.find(ii) != nonroots.end()) + continue; + update_init_priority(ii, &visited); + } +} + // Add statements to INIT_STMTS which run the initialization // functions for imported packages. This is only used for the "main" // package. void -Gogo::init_imports(std::vector<Bstatement*>& init_stmts) +Gogo::init_imports(std::vector<Bstatement*>& init_stmts, Bfunction *bfunction) { go_assert(this->is_main_package()); @@ -652,23 +680,27 @@ Gogo::init_imports(std::vector<Bstatement*>& init_stmts) Type::make_function_type(NULL, NULL, NULL, unknown_loc); Btype* fntype = func_type->get_backend_fntype(this); + // Recompute init priorities based on a walk of the init graph. + recompute_init_priorities(); + // We must call them in increasing priority order. - std::vector<Import_init> v; - for (std::set<Import_init>::const_iterator p = + std::vector<const Import_init*> v; + for (Import_init_set::const_iterator p = this->imported_init_fns_.begin(); p != this->imported_init_fns_.end(); ++p) v.push_back(*p); - std::sort(v.begin(), v.end()); + std::sort(v.begin(), v.end(), priority_compare); // We build calls to the init functions, which take no arguments. std::vector<Bexpression*> empty_args; - for (std::vector<Import_init>::const_iterator p = v.begin(); + for (std::vector<const Import_init*>::const_iterator p = v.begin(); p != v.end(); ++p) { - std::string user_name = p->package_name() + ".init"; - const std::string& init_name(p->init_name()); + const Import_init* ii = *p; + std::string user_name = ii->package_name() + ".init"; + const std::string& init_name(ii->init_name()); Bfunction* pfunc = this->backend()->function(fntype, user_name, init_name, true, true, true, false, @@ -678,7 +710,8 @@ Gogo::init_imports(std::vector<Bstatement*>& init_stmts) Bexpression* pfunc_call = this->backend()->call_expression(pfunc_code, empty_args, NULL, unknown_loc); - init_stmts.push_back(this->backend()->expression_statement(pfunc_call)); + init_stmts.push_back(this->backend()->expression_statement(bfunction, + pfunc_call)); } } @@ -701,7 +734,8 @@ Gogo::init_imports(std::vector<Bstatement*>& init_stmts) void Gogo::register_gc_vars(const std::vector<Named_object*>& var_gc, - std::vector<Bstatement*>& init_stmts) + std::vector<Bstatement*>& init_stmts, + Bfunction* init_bfn) { if (var_gc.empty()) return; @@ -713,10 +747,11 @@ Gogo::register_gc_vars(const std::vector<Named_object*>& var_gc, "__size", uint_type); Location builtin_loc = Linemap::predeclared_location(); - Expression* length = Expression::make_integer_ul(var_gc.size(), NULL, - builtin_loc); - + unsigned roots_len = var_gc.size() + this->gc_roots_.size() + 1; + Expression* length = Expression::make_integer_ul(roots_len, NULL, + builtin_loc); Array_type* root_array_type = Type::make_array_type(root_type, length); + root_array_type->set_is_array_incomparable(); Type* ptdt = Type::make_type_descriptor_ptr_type(); Struct_type* root_list_type = Type::make_builtin_struct_type(2, @@ -727,10 +762,9 @@ Gogo::register_gc_vars(const std::vector<Named_object*>& var_gc, Expression_list* roots_init = new Expression_list(); - size_t i = 0; for (std::vector<Named_object*>::const_iterator p = var_gc.begin(); p != var_gc.end(); - ++p, ++i) + ++p) { Expression_list* init = new Expression_list(); @@ -749,6 +783,27 @@ Gogo::register_gc_vars(const std::vector<Named_object*>& var_gc, roots_init->push_back(root_ctor); } + for (std::vector<Expression*>::const_iterator p = this->gc_roots_.begin(); + p != this->gc_roots_.end(); + ++p) + { + Expression_list *init = new Expression_list(); + + Expression* expr = *p; + Location eloc = expr->location(); + init->push_back(expr); + + Type* type = expr->type()->points_to(); + go_assert(type != NULL); + Expression* size = + Expression::make_type_info(type, Expression::TYPE_INFO_SIZE); + init->push_back(size); + + Expression* root_ctor = + Expression::make_struct_composite_literal(root_type, init, eloc); + roots_init->push_back(root_ctor); + } + // The list ends with a NULL entry. Expression_list* null_init = new Expression_list(); @@ -785,7 +840,7 @@ Gogo::register_gc_vars(const std::vector<Named_object*>& var_gc, Translate_context context(this, NULL, NULL, NULL); Bexpression* bcall = register_roots->get_backend(&context); - init_stmts.push_back(this->backend()->expression_statement(bcall)); + init_stmts.push_back(this->backend()->expression_statement(init_bfn, bcall)); } // Get the name to use for the import control function. If there is a @@ -1109,13 +1164,13 @@ sort_var_inits(Gogo* gogo, Var_inits* var_inits) var); if (ins.first->second) { - error_at(var->location(), - ("initialization expressions for %qs and " - "%qs depend upon each other"), - var->message_name().c_str(), - p2var->message_name().c_str()); - inform(p2->var()->location(), "%qs defined here", - p2var->message_name().c_str()); + go_error_at(var->location(), + ("initialization expressions for %qs and " + "%qs depend upon each other"), + var->message_name().c_str(), + p2var->message_name().c_str()); + go_inform(p2->var()->location(), "%qs defined here", + p2var->message_name().c_str()); init_loop = true; break; } @@ -1175,9 +1230,9 @@ sort_var_inits(Gogo* gogo, Var_inits* var_inits) Named_object* dep = gogo->var_depends_on(var->var_value()); if (init != NULL && dep == NULL && expression_requires(init, preinit, NULL, var)) - error_at(var->location(), - "initialization expression for %qs depends upon itself", - var->message_name().c_str()); + go_error_at(var->location(), + "initialization expression for %qs depends upon itself", + var->message_name().c_str()); } } @@ -1208,14 +1263,19 @@ Gogo::write_globals() std::vector<Bexpression*> const_decls; std::vector<Bfunction*> func_decls; - // The init function declaration, if necessary. + // The init function declaration and associated Bfunction, if necessary. Named_object* init_fndecl = NULL; + Bfunction* init_bfn = NULL; std::vector<Bstatement*> init_stmts; std::vector<Bstatement*> var_init_stmts; if (this->is_main_package()) - this->init_imports(init_stmts); + { + init_fndecl = this->initialization_function_decl(); + init_bfn = init_fndecl->func_value()->get_or_make_decl(this, init_fndecl); + this->init_imports(init_stmts, init_bfn); + } // A list of variable initializations. Var_inits var_inits; @@ -1279,49 +1339,59 @@ Gogo::write_globals() // The initializer is constant if it is the zero-value of the // variable's type or if the initial value is an immutable value // that is not copied to the heap. - bool is_constant_initializer = false; + bool is_static_initializer = false; if (var->init() == NULL) - is_constant_initializer = true; + is_static_initializer = true; else { Type* var_type = var->type(); Expression* init = var->init(); Expression* init_cast = Expression::make_cast(var_type, init, var->location()); - is_constant_initializer = - init_cast->is_immutable() && !var_type->has_pointer(); + is_static_initializer = init_cast->is_static_initializer(); } // Non-constant variable initializations might need to create // temporary variables, which will need the initialization // function as context. - if (!is_constant_initializer && init_fndecl == NULL) - init_fndecl = this->initialization_function_decl(); - Bexpression* var_binit = var->get_init(this, init_fndecl); + Named_object* var_init_fn; + if (is_static_initializer) + var_init_fn = NULL; + else + { + if (init_fndecl == NULL) + { + init_fndecl = this->initialization_function_decl(); + Function* func = init_fndecl->func_value(); + init_bfn = func->get_or_make_decl(this, init_fndecl); + } + var_init_fn = init_fndecl; + } + Bexpression* var_binit = var->get_init(this, var_init_fn); if (var_binit == NULL) ; - else if (is_constant_initializer) + else if (is_static_initializer) { if (expression_requires(var->init(), NULL, this->var_depends_on(var), no)) - error_at(no->location(), - "initialization expression for %qs depends " - "upon itself", - no->message_name().c_str()); + go_error_at(no->location(), + "initialization expression for %qs depends " + "upon itself", + no->message_name().c_str()); this->backend()->global_variable_set_init(bvar, var_binit); } else if (is_sink) var_init_stmt = - this->backend()->expression_statement(var_binit); + this->backend()->expression_statement(init_bfn, var_binit); else { Location loc = var->location(); Bexpression* var_expr = - this->backend()->var_expression(bvar, loc); + this->backend()->var_expression(bvar, VE_lvalue, loc); var_init_stmt = - this->backend()->assignment_statement(var_expr, var_binit, - loc); + this->backend()->assignment_statement(init_bfn, var_expr, + var_binit, loc); } } else @@ -1351,7 +1421,7 @@ Gogo::write_globals() Btype* btype = no->var_value()->type()->get_backend(this); Bexpression* zero = this->backend()->zero_expression(btype); Bstatement* zero_stmt = - this->backend()->expression_statement(zero); + this->backend()->expression_statement(init_bfn, zero); var_inits.push_back(Var_init(no, zero_stmt)); } @@ -1361,7 +1431,7 @@ Gogo::write_globals() } // Register global variables with the garbage collector. - this->register_gc_vars(var_gc, init_stmts); + this->register_gc_vars(var_gc, init_stmts, init_bfn); // Simple variable initializations, after all variables are // registered. @@ -1395,7 +1465,8 @@ Gogo::write_globals() Bexpression* call = this->backend()->call_expression(func_code, empty_args, NULL, func_loc); - init_stmts.push_back(this->backend()->expression_statement(call)); + Bstatement* ist = this->backend()->expression_statement(initfn, call); + init_stmts.push_back(ist); } // Set up a magic function to do all the initialization actions. @@ -1647,8 +1718,8 @@ Gogo::start_function(const std::string& name, Function_type* type, { if ((type->parameters() != NULL && !type->parameters()->empty()) || (type->results() != NULL && !type->results()->empty())) - error_at(location, - "func init must have no arguments and no return values"); + go_error_at(location, + "func init must have no arguments and no return values"); // There can be multiple "init" functions, so give them each a // different name. static int init_count; @@ -1711,16 +1782,29 @@ Gogo::start_function(const std::string& name, Function_type* type, if (rtype->classification() == Type::TYPE_POINTER) rtype = rtype->points_to(); + while (rtype->named_type() != NULL + && rtype->named_type()->is_alias()) + rtype = rtype->named_type()->real_type()->forwarded(); + if (rtype->is_error_type()) ret = Named_object::make_function(name, NULL, function); else if (rtype->named_type() != NULL) { - ret = rtype->named_type()->add_method(name, function); - if (!ret->is_function()) + if (rtype->named_type()->named_object()->package() != NULL) { - // Redefinition error. + go_error_at(type->receiver()->location(), + "may not define methods on non-local type"); ret = Named_object::make_function(name, NULL, function); } + else + { + ret = rtype->named_type()->add_method(name, function); + if (!ret->is_function()) + { + // Redefinition error. + ret = Named_object::make_function(name, NULL, function); + } + } } else if (rtype->forward_declaration_type() != NULL) { @@ -1744,8 +1828,9 @@ Gogo::start_function(const std::string& name, Function_type* type, } else { - error_at(type->receiver()->location(), - "invalid receiver type (receiver must be a named type)"); + go_error_at(type->receiver()->location(), + ("invalid receiver type (receiver must " + "be a named type)")); ret = Named_object::make_function(name, NULL, function); } } @@ -1857,8 +1942,8 @@ Gogo::declare_function(const std::string& name, Function_type* type, } else { - error_at(type->receiver()->location(), - "invalid receiver type (receiver must be a named type)"); + go_error_at(type->receiver()->location(), + "invalid receiver type (receiver must be a named type)"); return Named_object::make_erroneous_name(name); } } @@ -1889,74 +1974,6 @@ Gogo::add_label_reference(const std::string& label_name, issue_goto_errors); } -// Add a function to the call graph. - -Node* -Gogo::add_call_node(Named_object* function) -{ - Node* call = this->lookup_call_node(function); - if (call == NULL) - { - call = Node::make_call(function); - this->call_graph_.insert(call); - this->named_call_nodes_[function] = call; - } - return call; -} - -// Find the call node that represents FUNCTION. Return NULL if it does not -// exist. - -Node* -Gogo::lookup_call_node(Named_object* function) const -{ - Named_escape_nodes::const_iterator p = this->named_call_nodes_.find(function); - if (p == this->named_call_nodes_.end()) - return NULL; - return p->second; -} - -// Add a connection node for OBJECT. - -Node* -Gogo::add_connection_node(Named_object* object) -{ - Node* connection = this->lookup_connection_node(object); - if (connection == NULL) - { - connection = Node::make_connection(object, Node::ESCAPE_NONE); - - // Each global variable is a part of the global connection graph. - if (object->is_variable() - && object->var_value()->is_global()) - { - connection->connection_node()->set_escape_state(Node::ESCAPE_GLOBAL); - this->global_connections_.insert(connection); - } - - // Each function declaration or definition is the root of its own - // connection graph. This means closures will have their own - // connection graph that objects in the enclosing function might - // refer to. - if (object->is_function() || object->is_function_declaration()) - this->connection_roots_.insert(connection); - this->named_connection_nodes_[object] = connection; - } - return connection; -} - -// Find the connection node for OBJECT. Return NULL if it does not exist. - -Node* -Gogo::lookup_connection_node(Named_object* object) const -{ - Named_escape_nodes::const_iterator p = - this->named_connection_nodes_.find(object); - if (p == this->named_connection_nodes_.end()) - return NULL; - return p->second; -} - // Return the current binding state. Bindings_snapshot* @@ -2124,6 +2141,29 @@ Gogo::add_dot_import_object(Named_object* no) this->current_bindings()->add_named_object(no); } +// Add a linkname. This implements the go:linkname compiler directive. +// We only support this for functions and function declarations. + +void +Gogo::add_linkname(const std::string& go_name, bool is_exported, + const std::string& ext_name, Location loc) +{ + Named_object* no = + this->package_->bindings()->lookup(this->pack_hidden_name(go_name, + is_exported)); + if (no == NULL) + go_error_at(loc, "%s is not defined", go_name.c_str()); + else if (no->is_function()) + no->func_value()->set_asm_name(ext_name); + else if (no->is_function_declaration()) + no->func_declaration_value()->set_asm_name(ext_name); + else + go_error_at(loc, + ("%s is not a function; " + "//go:linkname is only supported for functions"), + go_name.c_str()); +} + // Mark all local variables used. This is used when some types of // parse error occur. @@ -2197,6 +2237,14 @@ Gogo::is_thunk(const Named_object* no) void Gogo::define_global_names() { + if (this->is_main_package()) + { + // Every Go program has to import the runtime package, so that + // it is properly initialized. + this->import_package("runtime", "_", false, false, + Linemap::predeclared_location()); + } + for (Bindings::const_declarations_iterator p = this->globals_->begin_declarations(); p != this->globals_->end_declarations(); @@ -2213,13 +2261,19 @@ Gogo::define_global_names() if (global_no->is_type()) { if (no->type_declaration_value()->has_methods()) - error_at(no->location(), - "may not define methods for global type"); + { + for (std::vector<Named_object*>::const_iterator p = + no->type_declaration_value()->methods()->begin(); + p != no->type_declaration_value()->methods()->end(); + p++) + go_error_at((*p)->location(), + "may not define methods on non-local type"); + } no->set_type_value(global_no->type_value()); } else { - error_at(no->location(), "expected type"); + go_error_at(no->location(), "expected type"); Type* errtype = Type::make_error_type(); Named_object* err = Named_object::make_type("erroneous_type", NULL, errtype, @@ -2251,18 +2305,18 @@ Gogo::define_global_names() if (pf != this->file_block_names_.end()) { std::string n = p->second->message_name(); - error_at(p->second->location(), - "%qs defined as both imported name and global name", - n.c_str()); - inform(pf->second, "%qs imported here", n.c_str()); + go_error_at(p->second->location(), + "%qs defined as both imported name and global name", + n.c_str()); + go_inform(pf->second, "%qs imported here", n.c_str()); } // No package scope identifier may be named "init". if (!p->second->is_function() && Gogo::unpack_hidden_name(p->second->name()) == "init") { - error_at(p->second->location(), - "cannot declare init - must be func"); + go_error_at(p->second->location(), + "cannot declare init - must be func"); } } } @@ -2293,20 +2347,22 @@ Gogo::clear_file_scope() std::string pkg_name = package->package_name(); if (p1->first != pkg_name && p1->first[0] != '.') { - error_at(p1->second->location(), - "imported and not used: %s as %s", - Gogo::message_name(pkg_name).c_str(), - Gogo::message_name(p1->first).c_str()); + go_error_at(p1->second->location(), + "imported and not used: %s as %s", + Gogo::message_name(pkg_name).c_str(), + Gogo::message_name(p1->first).c_str()); } else - error_at(p1->second->location(), - "imported and not used: %s", - Gogo::message_name(pkg_name).c_str()); + go_error_at(p1->second->location(), + "imported and not used: %s", + Gogo::message_name(pkg_name).c_str()); } } } package->clear_used(); } + + this->current_file_imported_unsafe_ = false; } // Queue up a type specific function for later writing. These are @@ -2314,7 +2370,7 @@ Gogo::clear_file_scope() // parse tree is lowered. void -Gogo::queue_specific_type_function(Type* type, Named_type* name, +Gogo::queue_specific_type_function(Type* type, Named_type* name, int64_t size, const std::string& hash_name, Function_type* hash_fntype, const std::string& equal_name, @@ -2322,7 +2378,7 @@ Gogo::queue_specific_type_function(Type* type, Named_type* name, { go_assert(!this->specific_type_functions_are_written_); go_assert(!this->in_global_scope()); - Specific_type_function* tsf = new Specific_type_function(type, name, + Specific_type_function* tsf = new Specific_type_function(type, name, size, hash_name, hash_fntype, equal_name, @@ -2357,7 +2413,7 @@ Specific_type_functions::type(Type* t) case Type::TYPE_NAMED: { Named_type* nt = t->named_type(); - if (!t->compare_is_identity(this->gogo_) && t->is_comparable()) + if (t->needs_specific_type_functions(this->gogo_)) t->type_functions(this->gogo_, nt, NULL, NULL, &hash_fn, &equal_fn); // If this is a struct type, we don't want to make functions @@ -2391,7 +2447,7 @@ Specific_type_functions::type(Type* t) case Type::TYPE_STRUCT: case Type::TYPE_ARRAY: - if (!t->compare_is_identity(this->gogo_) && t->is_comparable()) + if (t->needs_specific_type_functions(this->gogo_)) t->type_functions(this->gogo_, NULL, NULL, NULL, &hash_fn, &equal_fn); break; @@ -2414,7 +2470,7 @@ Gogo::write_specific_type_functions() { Specific_type_function* tsf = this->specific_type_functions_.back(); this->specific_type_functions_.pop_back(); - tsf->type->write_specific_type_functions(this, tsf->name, + tsf->type->write_specific_type_functions(this, tsf->name, tsf->size, tsf->hash_name, tsf->hash_fntype, tsf->equal_name, @@ -3104,11 +3160,11 @@ Check_types_traverse::variable(Named_object* named_object) && !Type::are_assignable(var->type(), init->type(), &reason)) { if (reason.empty()) - error_at(var->location(), "incompatible type in initialization"); + go_error_at(var->location(), "incompatible type in initialization"); else - error_at(var->location(), - "incompatible type in initialization (%s)", - reason.c_str()); + go_error_at(var->location(), + "incompatible type in initialization (%s)", + reason.c_str()); init = Expression::make_error(named_object->location()); var->clear_init(); } @@ -3128,10 +3184,10 @@ Check_types_traverse::variable(Named_object* named_object) // initialization. if (fntype->is_builtin()) { - error_at(init->location(), - "invalid use of special builtin function %qs; " - "must be called", - no->message_name().c_str()); + go_error_at(init->location(), + "invalid use of special builtin function %qs; " + "must be called", + no->message_name().c_str()); } } if (!var->is_used() @@ -3141,8 +3197,8 @@ Check_types_traverse::variable(Named_object* named_object) && !var->type()->is_error() && (init == NULL || !init->is_error_expression()) && !Lex::is_invalid_identifier(named_object->name())) - error_at(var->location(), "%qs declared and not used", - named_object->message_name().c_str()); + go_error_at(var->location(), "%qs declared and not used", + named_object->message_name().c_str()); } return TRAVERSE_CONTINUE; } @@ -3161,21 +3217,21 @@ Check_types_traverse::constant(Named_object* named_object, bool) && !ctype->is_string_type()) { if (ctype->is_nil_type()) - error_at(constant->location(), "const initializer cannot be nil"); + go_error_at(constant->location(), "const initializer cannot be nil"); else if (!ctype->is_error()) - error_at(constant->location(), "invalid constant type"); + go_error_at(constant->location(), "invalid constant type"); constant->set_error(); } else if (!constant->expr()->is_constant()) { - error_at(constant->expr()->location(), "expression is not constant"); + go_error_at(constant->expr()->location(), "expression is not constant"); constant->set_error(); } else if (!Type::are_assignable(constant->type(), constant->expr()->type(), NULL)) { - error_at(constant->location(), - "initialization expression has wrong type"); + go_error_at(constant->location(), + "initialization expression has wrong type"); constant->set_error(); } return TRAVERSE_CONTINUE; @@ -3638,11 +3694,21 @@ Order_eval::statement(Block* block, size_t* pindex, Statement* s) // be handled specially. We can't create a temporary // because there is no type to give it. Any actual uses of // the values will be done via Call_result_expressions. - s = Statement::make_statement(*pexpr, true); - } + // + // Since a given call expression can be shared by multiple + // Call_result_expressions, avoid hoisting the call the + // second time we see it here. + if (this->remember_expression(*pexpr)) + s = NULL; + else + s = Statement::make_statement(*pexpr, true); + } - block->insert_statement_before(*pindex, s); - ++*pindex; + if (s != NULL) + { + block->insert_statement_before(*pindex, s); + ++*pindex; + } } if (init != orig_init) @@ -4164,33 +4230,15 @@ Build_recover_thunks::function(Named_object* orig_no) // Return the expression to pass for the .can_recover parameter to the // new function. This indicates whether a call to recover may return -// non-nil. The expression is -// __go_can_recover(__builtin_return_address()). +// non-nil. The expression is runtime.canrecover(__builtin_return_address()). Expression* Build_recover_thunks::can_recover_arg(Location location) { static Named_object* builtin_return_address; if (builtin_return_address == NULL) - { - const Location bloc = Linemap::predeclared_location(); - - Typed_identifier_list* param_types = new Typed_identifier_list(); - Type* uint_type = Type::lookup_integer_type("uint"); - param_types->push_back(Typed_identifier("l", uint_type, bloc)); - - Typed_identifier_list* return_types = new Typed_identifier_list(); - Type* voidptr_type = Type::make_pointer_type(Type::make_void_type()); - return_types->push_back(Typed_identifier("", voidptr_type, bloc)); - - Function_type* fntype = Type::make_function_type(NULL, param_types, - return_types, bloc); - builtin_return_address = - Named_object::make_function_declaration("__builtin_return_address", - NULL, fntype, bloc); - const char* n = "__builtin_return_address"; - builtin_return_address->func_declaration_value()->set_asm_name(n); - } + builtin_return_address = + Gogo::declare_builtin_rf_address("__builtin_return_address"); static Named_object* can_recover; if (can_recover == NULL) @@ -4204,10 +4252,10 @@ Build_recover_thunks::can_recover_arg(Location location) results->push_back(Typed_identifier("", boolean_type, bloc)); Function_type* fntype = Type::make_function_type(NULL, param_types, results, bloc); - can_recover = Named_object::make_function_declaration("__go_can_recover", - NULL, fntype, - bloc); - can_recover->func_declaration_value()->set_asm_name("__go_can_recover"); + can_recover = + Named_object::make_function_declaration("runtime_canrecover", + NULL, fntype, bloc); + can_recover->func_declaration_value()->set_asm_name("runtime.canrecover"); } Expression* fn = Expression::make_func_reference(builtin_return_address, @@ -4230,10 +4278,10 @@ Build_recover_thunks::can_recover_arg(Location location) // function with an extra parameter, which is whether a call to // recover can succeed. We then move the body of this function to // that one. We then turn this function into a thunk which calls the -// new one, passing the value of -// __go_can_recover(__builtin_return_address()). The function will be -// marked as not splitting the stack. This will cooperate with the -// implementation of defer to make recover do the right thing. +// new one, passing the value of runtime.canrecover(__builtin_return_address()). +// The function will be marked as not splitting the stack. This will +// cooperate with the implementation of defer to make recover do the +// right thing. void Gogo::build_recover_thunks() @@ -4242,6 +4290,30 @@ Gogo::build_recover_thunks() this->traverse(&build_recover_thunks); } +// Return a declaration for __builtin_return_address or +// __builtin_frame_address. + +Named_object* +Gogo::declare_builtin_rf_address(const char* name) +{ + const Location bloc = Linemap::predeclared_location(); + + Typed_identifier_list* param_types = new Typed_identifier_list(); + Type* uint32_type = Type::lookup_integer_type("uint32"); + param_types->push_back(Typed_identifier("l", uint32_type, bloc)); + + Typed_identifier_list* return_types = new Typed_identifier_list(); + Type* voidptr_type = Type::make_pointer_type(Type::make_void_type()); + return_types->push_back(Typed_identifier("", voidptr_type, bloc)); + + Function_type* fntype = Type::make_function_type(NULL, param_types, + return_types, bloc); + Named_object* ret = Named_object::make_function_declaration(name, NULL, + fntype, bloc); + ret->func_declaration_value()->set_asm_name(name); + return ret; +} + // Build a call to the runtime error function. Expression* @@ -4408,8 +4480,8 @@ Check_return_statements_traverse::function(Named_object* no) return TRAVERSE_CONTINUE; if (func->block()->may_fall_through()) - error_at(func->block()->end_location(), - "missing return at end of function"); + go_error_at(func->block()->end_location(), + "missing return at end of function"); return TRAVERSE_CONTINUE; } @@ -4423,21 +4495,6 @@ Gogo::check_return_statements() this->traverse(&traverse); } -// Work out the package priority. It is one more than the maximum -// priority of an imported package. - -int -Gogo::package_priority() const -{ - int priority = 0; - for (Packages::const_iterator p = this->packages_.begin(); - p != this->packages_.end(); - ++p) - if (p->second->priority() > priority) - priority = p->second->priority(); - return priority + 1; -} - // Export identifiers as requested. void @@ -4465,7 +4522,6 @@ Gogo::do_exports() exp.export_globals(this->package_name(), prefix, pkgpath, - this->package_priority(), this->packages_, this->imports_, (this->need_init_fn_ && !this->is_main_package() @@ -4473,6 +4529,146 @@ Gogo::do_exports() : ""), this->imported_init_fns_, this->package_->bindings()); + + if (!this->c_header_.empty() && !saw_errors()) + this->write_c_header(); +} + +// Write the top level named struct types in C format to a C header +// file. This is used when building the runtime package, to share +// struct definitions between C and Go. + +void +Gogo::write_c_header() +{ + std::ofstream out; + out.open(this->c_header_.c_str()); + if (out.fail()) + { + go_error_at(Linemap::unknown_location(), + "cannot open %s: %m", this->c_header_.c_str()); + return; + } + + std::list<Named_object*> types; + Bindings* top = this->package_->bindings(); + for (Bindings::const_definitions_iterator p = top->begin_definitions(); + p != top->end_definitions(); + ++p) + { + Named_object* no = *p; + + // Skip names that start with underscore followed by something + // other than an uppercase letter, as when compiling the runtime + // package they are mostly types defined by mkrsysinfo.sh based + // on the C system header files. We don't need to translate + // types to C and back to Go. But do accept the special cases + // _defer and _panic. + std::string name = Gogo::unpack_hidden_name(no->name()); + if (name[0] == '_' + && (name[1] < 'A' || name[1] > 'Z') + && (name != "_defer" && name != "_panic")) + continue; + + if (no->is_type() && no->type_value()->struct_type() != NULL) + types.push_back(no); + if (no->is_const() && no->const_value()->type()->integer_type() != NULL) + { + Numeric_constant nc; + unsigned long val; + if (no->const_value()->expr()->numeric_constant_value(&nc) + && nc.to_unsigned_long(&val) == Numeric_constant::NC_UL_VALID) + { + out << "#define " << no->message_name() << ' ' << val + << std::endl; + } + } + } + + std::vector<const Named_object*> written; + int loop = 0; + while (!types.empty()) + { + Named_object* no = types.front(); + types.pop_front(); + + std::vector<const Named_object*> requires; + std::vector<const Named_object*> declare; + if (!no->type_value()->struct_type()->can_write_to_c_header(&requires, + &declare)) + continue; + + bool ok = true; + for (std::vector<const Named_object*>::const_iterator pr + = requires.begin(); + pr != requires.end() && ok; + ++pr) + { + for (std::list<Named_object*>::const_iterator pt = types.begin(); + pt != types.end() && ok; + ++pt) + if (*pr == *pt) + ok = false; + } + if (!ok) + { + ++loop; + if (loop > 10000) + { + // This should be impossible since the code parsed and + // type checked. + go_unreachable(); + } + + types.push_back(no); + continue; + } + + for (std::vector<const Named_object*>::const_iterator pd + = declare.begin(); + pd != declare.end(); + ++pd) + { + if (*pd == no) + continue; + + std::vector<const Named_object*> drequires; + std::vector<const Named_object*> ddeclare; + if (!(*pd)->type_value()->struct_type()-> + can_write_to_c_header(&drequires, &ddeclare)) + continue; + + bool done = false; + for (std::vector<const Named_object*>::const_iterator pw + = written.begin(); + pw != written.end(); + ++pw) + { + if (*pw == *pd) + { + done = true; + break; + } + } + if (!done) + { + out << std::endl; + out << "struct " << (*pd)->message_name() << ";" << std::endl; + written.push_back(*pd); + } + } + + out << std::endl; + out << "struct " << no->message_name() << " {" << std::endl; + no->type_value()->struct_type()->write_to_c_header(out); + out << "};" << std::endl; + written.push_back(no); + } + + out.close(); + if (out.fail()) + go_error_at(Linemap::unknown_location(), + "error writing to %s: %m", this->c_header_.c_str()); } // Find the blocks in order to convert named types defined in blocks. @@ -4531,7 +4727,6 @@ Gogo::convert_named_types() Array_type::make_array_type_descriptor_type(); Array_type::make_slice_type_descriptor_type(); Map_type::make_map_type_descriptor_type(); - Map_type::make_map_descriptor_type(); Channel_type::make_chan_type_descriptor_type(); Interface_type::make_interface_type_descriptor_type(); Expression::make_func_descriptor_type(); @@ -4563,7 +4758,7 @@ Function::Function(Function_type* type, Named_object* enclosing, Block* block, : type_(type), enclosing_(enclosing), results_(NULL), closure_var_(NULL), block_(block), location_(location), labels_(), local_type_count_(0), descriptor_(NULL), fndecl_(NULL), defer_stack_(NULL), - is_sink_(false), results_are_named_(false), nointerface_(false), + pragmas_(0), is_sink_(false), results_are_named_(false), is_unnamed_type_stub_method_(false), calls_recover_(false), is_recover_thunk_(false), has_recover_thunk_(false), calls_defer_retaddr_(false), is_type_specific_function_(false), @@ -4635,6 +4830,24 @@ Function::update_result_variables() (*p)->result_var_value()->set_function(this); } +// Whether this method should not be included in the type descriptor. + +bool +Function::nointerface() const +{ + go_assert(this->is_method()); + return (this->pragmas_ & GOPRAGMA_NOINTERFACE) != 0; +} + +// Record that this method should not be included in the type +// descriptor. + +void +Function::set_nointerface() +{ + this->pragmas_ |= GOPRAGMA_NOINTERFACE; +} + // Return the closure variable, creating it if necessary. Named_object* @@ -4647,7 +4860,8 @@ Function::closure_var() // we find them. Location loc = this->type_->location(); Struct_field_list* sfl = new Struct_field_list; - Type* struct_type = Type::make_struct_type(sfl, loc); + Struct_type* struct_type = Type::make_struct_type(sfl, loc); + struct_type->set_is_struct_incomparable(); Variable* var = new Variable(Type::make_pointer_type(struct_type), NULL, false, false, false, loc); var->set_is_used(); @@ -4729,10 +4943,10 @@ Function::add_label_definition(Gogo* gogo, const std::string& label_name, label = ins.first->second; if (label->is_defined()) { - error_at(location, "label %qs already defined", - Gogo::message_name(label_name).c_str()); - inform(label->location(), "previous definition of %qs was here", - Gogo::message_name(label_name).c_str()); + go_error_at(location, "label %qs already defined", + Gogo::message_name(label_name).c_str()); + go_inform(label->location(), "previous definition of %qs was here", + Gogo::message_name(label_name).c_str()); return new Label(label_name); } } @@ -4798,8 +5012,8 @@ Function::check_labels() const { Label* label = p->second; if (!label->is_used()) - error_at(label->location(), "label %qs defined and not used", - Gogo::message_name(label->name()).c_str()); + go_error_at(label->location(), "label %qs defined and not used", + Gogo::message_name(label->name()).c_str()); } } @@ -4918,13 +5132,7 @@ Function::export_func_with_type(Export* exp, const std::string& name, exp->write_c_string("("); const Typed_identifier* receiver = fntype->receiver(); exp->write_name(receiver->name()); - - if (fntype->has_escape_info()) - { - exp->write_c_string(" "); - exp->write_escape(fntype->receiver_escape_state()); - } - + exp->write_escape(receiver->note()); exp->write_c_string(" "); exp->write_type(receiver->type()); exp->write_c_string(") "); @@ -4948,13 +5156,7 @@ Function::export_func_with_type(Export* exp, const std::string& name, else exp->write_c_string(", "); exp->write_name(p->name()); - - if (fntype->has_escape_info()) - { - exp->write_c_string(" "); - exp->write_escape(fntype->parameter_escape_states()->at(i)); - } - + exp->write_escape(p->note()); exp->write_c_string(" "); if (!is_varargs || p + 1 != parameters->end()) exp->write_type(p->type()); @@ -4988,6 +5190,7 @@ Function::export_func_with_type(Export* exp, const std::string& name, else exp->write_c_string(", "); exp->write_name(p->name()); + exp->write_escape(p->note()); exp->write_c_string(" "); exp->write_type(p->type()); } @@ -5002,59 +5205,39 @@ Function::export_func_with_type(Export* exp, const std::string& name, void Function::import_func(Import* imp, std::string* pname, Typed_identifier** preceiver, - Node::Escapement_lattice* rcvr_escape, Typed_identifier_list** pparameters, - Node::Escape_states** pparam_escapes, Typed_identifier_list** presults, - bool* is_varargs, bool* has_escape_info) + bool* is_varargs) { - *has_escape_info = false; - imp->require_c_string("func "); *preceiver = NULL; - *rcvr_escape = Node::ESCAPE_NONE; if (imp->peek_char() == '(') { imp->require_c_string("("); std::string name = imp->read_name(); - - if (imp->match_c_string(" <escape")){ - *has_escape_info = true; - imp->require_c_string(" "); - *rcvr_escape = imp->read_escape_info(); - } - + std::string escape_note = imp->read_escape(); imp->require_c_string(" "); Type* rtype = imp->read_type(); *preceiver = new Typed_identifier(name, rtype, imp->location()); + (*preceiver)->set_note(escape_note); imp->require_c_string(") "); } *pname = imp->read_identifier(); Typed_identifier_list* parameters; - Node::Escape_states* param_escapes; *is_varargs = false; imp->require_c_string(" ("); if (imp->peek_char() == ')') - { - parameters = NULL; - param_escapes = NULL; - } + parameters = NULL; else { parameters = new Typed_identifier_list(); - param_escapes = new Node::Escape_states(); while (true) { std::string name = imp->read_name(); - if (imp->match_c_string(" <escape")){ - *has_escape_info = true; - imp->require_c_string(" "); - param_escapes->push_back(imp->read_escape_info()); - } - + std::string escape_note = imp->read_escape(); imp->require_c_string(" "); if (imp->match_c_string("...")) @@ -5066,8 +5249,9 @@ Function::import_func(Import* imp, std::string* pname, Type* ptype = imp->read_type(); if (*is_varargs) ptype = Type::make_array_type(ptype, NULL); - parameters->push_back(Typed_identifier(name, ptype, - imp->location())); + Typed_identifier t = Typed_identifier(name, ptype, imp->location()); + t.set_note(escape_note); + parameters->push_back(t); if (imp->peek_char() != ',') break; go_assert(!*is_varargs); @@ -5076,7 +5260,6 @@ Function::import_func(Import* imp, std::string* pname, } imp->require_c_string(")"); *pparameters = parameters; - *pparam_escapes = param_escapes; Typed_identifier_list* results; if (imp->peek_char() != ' ') @@ -5096,10 +5279,13 @@ Function::import_func(Import* imp, std::string* pname, while (true) { std::string name = imp->read_name(); + std::string note = imp->read_escape(); imp->require_c_string(" "); Type* rtype = imp->read_type(); - results->push_back(Typed_identifier(name, rtype, - imp->location())); + Typed_identifier t = Typed_identifier(name, rtype, + imp->location()); + t.set_note(note); + results->push_back(t); if (imp->peek_char() != ',') break; imp->require_c_string(", "); @@ -5168,6 +5354,12 @@ Function::get_or_make_decl(Gogo* gogo, Named_object* no) } } + if (!this->asm_name_.empty()) + { + asm_name = this->asm_name_; + is_visible = true; + } + // If a function calls the predeclared recover function, we // can't inline it, because recover behaves differently in a // function passed directly to defer. If this is a recover @@ -5184,17 +5376,30 @@ Function::get_or_make_decl(Gogo* gogo, Named_object* no) if (this->calls_defer_retaddr_) is_inlinable = false; + // Check the //go:noinline compiler directive. + if ((this->pragmas_ & GOPRAGMA_NOINLINE) != 0) + is_inlinable = false; + // If this is a thunk created to call a function which calls // the predeclared recover function, we need to disable // stack splitting for the thunk. bool disable_split_stack = this->is_recover_thunk_; + // Check the //go:nosplit compiler directive. + if ((this->pragmas_ & GOPRAGMA_NOSPLIT) != 0) + disable_split_stack = true; + + // Encode name if asm_name not already set at this point + if (asm_name.empty() && go_id_needs_encoding(no->get_id(gogo))) + asm_name = go_encode_id(no->get_id(gogo)); + // This should go into a unique section if that has been // requested elsewhere, or if this is a nointerface function. // We want to put a nointerface function into a unique section // because there is a good chance that the linker garbage // collection can discard it. - bool in_unique_section = this->in_unique_section_ || this->nointerface_; + bool in_unique_section = (this->in_unique_section_ + || (this->is_method() && this->nointerface())); Btype* functype = this->type_->get_backend_fntype(gogo); this->fndecl_ = @@ -5241,6 +5446,8 @@ Function_declaration::get_or_make_decl(Gogo* gogo, Named_object* no) asm_name.append(rtype->mangled_name(gogo)); } } + else if (go_id_needs_encoding(no->get_id(gogo))) + asm_name = go_encode_id(no->get_id(gogo)); Btype* functype = this->fntype_->get_backend_fntype(gogo); this->fndecl_ = @@ -5427,7 +5634,8 @@ Function::build(Gogo* gogo, Named_object* named_function) for (size_t i = 0; i < vars.size(); ++i) { Bstatement* init_stmt = - gogo->backend()->init_statement(vars[i], var_inits[i]); + gogo->backend()->init_statement(this->fndecl_, vars[i], + var_inits[i]); init.push_back(init_stmt); } if (defer_init != NULL) @@ -5495,11 +5703,11 @@ Function::build_defer_wrapper(Gogo* gogo, Named_object* named_function, // libgo/runtime/go-unwind.c. std::vector<Bstatement*> stmts; - Expression* call = Runtime::make_call(Runtime::CHECK_DEFER, end_loc, 1, + Expression* call = Runtime::make_call(Runtime::CHECKDEFER, end_loc, 1, this->defer_stack(end_loc)); Translate_context context(gogo, named_function, NULL, NULL); Bexpression* defer = call->get_backend(&context); - stmts.push_back(gogo->backend()->expression_statement(defer)); + stmts.push_back(gogo->backend()->expression_statement(this->fndecl_, defer)); Bstatement* ret_bstmt = this->return_value(gogo, named_function, end_loc); if (ret_bstmt != NULL) @@ -5508,11 +5716,11 @@ Function::build_defer_wrapper(Gogo* gogo, Named_object* named_function, go_assert(*except == NULL); *except = gogo->backend()->statement_list(stmts); - call = Runtime::make_call(Runtime::CHECK_DEFER, end_loc, 1, + call = Runtime::make_call(Runtime::CHECKDEFER, end_loc, 1, this->defer_stack(end_loc)); defer = call->get_backend(&context); - call = Runtime::make_call(Runtime::UNDEFER, end_loc, 1, + call = Runtime::make_call(Runtime::DEFERRETURN, end_loc, 1, this->defer_stack(end_loc)); Bexpression* undefer = call->get_backend(&context); Bstatement* function_defer = @@ -5536,9 +5744,10 @@ Function::build_defer_wrapper(Gogo* gogo, Named_object* named_function, Expression* ref = Expression::make_temporary_reference(this->defer_stack_, end_loc); Bexpression* bref = ref->get_backend(&context); - ret = gogo->backend()->conditional_expression(NULL, bref, ret, NULL, + ret = gogo->backend()->conditional_expression(this->fndecl_, + NULL, bref, ret, NULL, end_loc); - stmts.push_back(gogo->backend()->expression_statement(ret)); + stmts.push_back(gogo->backend()->expression_statement(this->fndecl_, ret)); } go_assert(*fini == NULL); @@ -5567,7 +5776,8 @@ Function::return_value(Gogo* gogo, Named_object* named_function, { Named_object* no = (*this->results_)[i]; Bvariable* bvar = no->get_backend_variable(gogo, named_function); - Bexpression* val = gogo->backend()->var_expression(bvar, location); + Bexpression* val = gogo->backend()->var_expression(bvar, VE_rvalue, + location); if (no->result_var_value()->is_in_heap()) { Btype* bt = no->result_var_value()->type()->get_backend(gogo); @@ -5586,7 +5796,7 @@ Block::Block(Block* enclosing, Location location) ? NULL : enclosing->bindings())), start_location_(location), - end_location_(UNKNOWN_LOCATION) + end_location_(Linemap::unknown_location()) { } @@ -5872,8 +6082,8 @@ Bindings_snapshot::check_goto_block(Location loc, const Block* bfrom, { if (pb == NULL) { - error_at(loc, "goto jumps into block"); - inform(bto->start_location(), "goto target block starts here"); + go_error_at(loc, "goto jumps into block"); + go_inform(bto->start_location(), "goto target block starts here"); return false; } } @@ -5901,8 +6111,8 @@ Bindings_snapshot::check_goto_defs(Location loc, const Block* block, go_assert(p != block->bindings()->end_definitions()); std::string n = (*p)->message_name(); - error_at(loc, "goto jumps over declaration of %qs", n.c_str()); - inform((*p)->location(), "%qs defined here", n.c_str()); + go_error_at(loc, "goto jumps over declaration of %qs", n.c_str()); + go_inform((*p)->location(), "%qs defined here", n.c_str()); } } @@ -6112,7 +6322,7 @@ Variable::type_from_tuple(Expression* expr, bool report_error) const else { if (report_error) - error_at(this->location(), "invalid tuple definition"); + go_error_at(this->location(), "invalid tuple definition"); return Type::make_error_type(); } } @@ -6156,15 +6366,16 @@ Variable::type_from_range(Expression* expr, bool get_index_type, else { if (report_error) - error_at(this->location(), - "invalid definition of value variable for channel range"); + go_error_at(this->location(), + ("invalid definition of value variable " + "for channel range")); return Type::make_error_type(); } } else { if (report_error) - error_at(this->location(), "invalid type for range clause"); + go_error_at(this->location(), "invalid type for range clause"); return Type::make_error_type(); } } @@ -6180,7 +6391,7 @@ Variable::type_from_chan_element(Expression* expr, bool report_error) const else { if (report_error) - error_at(this->location(), "expected channel"); + go_error_at(this->location(), "expected channel"); return Type::make_error_type(); } } @@ -6211,7 +6422,7 @@ Variable::type() { if (this->type_ == NULL || !this->type_->is_error_type()) { - error_at(this->location_, "variable initializer refers to itself"); + go_error_at(this->location_, "variable initializer refers to itself"); this->type_ = Type::make_error_type(); } return this->type_; @@ -6319,17 +6530,17 @@ Variable::determine_type() if (type->is_void_type()) { - error_at(this->location_, "variable has no type"); + go_error_at(this->location_, "variable has no type"); type = Type::make_error_type(); } else if (type->is_nil_type()) { - error_at(this->location_, "variable defined to nil type"); + go_error_at(this->location_, "variable defined to nil type"); type = Type::make_error_type(); } else if (type->is_call_multiple_result_type()) { - error_at(this->location_, + go_error_at(this->location_, "single variable set to multiple-value function call"); type = Type::make_error_type(); } @@ -6378,6 +6589,8 @@ Variable::get_init_block(Gogo* gogo, Named_object* function, Translate_context context(gogo, function, NULL, NULL); Bblock* bblock = this->preinit_->get_backend(&context); + Bfunction* bfunction = + function->func_value()->get_or_make_decl(gogo, function); // It's possible to have pre-init statements without an initializer // if the pre-init statements set the variable. @@ -6387,7 +6600,8 @@ Variable::get_init_block(Gogo* gogo, Named_object* function, if (var_decl == NULL) { Bexpression* init_bexpr = this->init_->get_backend(&context); - decl_init = gogo->backend()->expression_statement(init_bexpr); + decl_init = gogo->backend()->expression_statement(bfunction, + init_bexpr); } else { @@ -6395,8 +6609,10 @@ Variable::get_init_block(Gogo* gogo, Named_object* function, Expression* val_expr = Expression::make_cast(this->type(), this->init_, loc); Bexpression* val = val_expr->get_backend(&context); - Bexpression* var_ref = gogo->backend()->var_expression(var_decl, loc); - decl_init = gogo->backend()->assignment_statement(var_ref, val, loc); + Bexpression* var_ref = + gogo->backend()->var_expression(var_decl, VE_lvalue, loc); + decl_init = gogo->backend()->assignment_statement(bfunction, var_ref, + val, loc); } } Bstatement* block_stmt = gogo->backend()->block_statement(bblock); @@ -6455,23 +6671,39 @@ Variable::get_backend_variable(Gogo* gogo, Named_object* function, type = Type::make_pointer_type(type); } - std::string n = Gogo::unpack_hidden_name(name); + const std::string n = Gogo::unpack_hidden_name(name); Btype* btype = type->get_backend(gogo); Bvariable* bvar; - if (this->is_global_) - bvar = backend->global_variable((package == NULL - ? gogo->package_name() - : package->package_name()), - (package == NULL - ? gogo->pkgpath_symbol() - : package->pkgpath_symbol()), - n, - btype, - package != NULL, - Gogo::is_hidden_name(name), - this->in_unique_section_, - this->location_); + if (Map_type::is_zero_value(this)) + bvar = Map_type::backend_zero_value(gogo); + else if (this->is_global_) + { + std::string var_name(package != NULL + ? package->package_name() + : gogo->package_name()); + var_name.push_back('.'); + var_name.append(n); + std::string asm_name; + if (Gogo::is_hidden_name(name)) + asm_name = var_name; + else + { + asm_name = package != NULL + ? package->pkgpath_symbol() + : gogo->pkgpath_symbol(); + asm_name.push_back('.'); + asm_name.append(n); + } + asm_name = go_encode_id(asm_name); + bvar = backend->global_variable(var_name, + asm_name, + btype, + package != NULL, + Gogo::is_hidden_name(name), + this->in_unique_section_, + this->location_); + } else if (function == NULL) { go_assert(saw_errors()); @@ -6659,7 +6891,7 @@ Type_declaration::add_method_declaration(const std::string& name, return ret; } -// Return whether any methods ere defined. +// Return whether any methods are defined. bool Type_declaration::has_methods() const @@ -6672,6 +6904,36 @@ Type_declaration::has_methods() const void Type_declaration::define_methods(Named_type* nt) { + if (this->methods_.empty()) + return; + + while (nt->is_alias()) + { + Type *t = nt->real_type()->forwarded(); + if (t->named_type() != NULL) + nt = t->named_type(); + else if (t->forward_declaration_type() != NULL) + { + Named_object* no = t->forward_declaration_type()->named_object(); + Type_declaration* td = no->type_declaration_value(); + td->methods_.insert(td->methods_.end(), this->methods_.begin(), + this->methods_.end()); + this->methods_.clear(); + return; + } + else + { + for (std::vector<Named_object*>::const_iterator p = + this->methods_.begin(); + p != this->methods_.end(); + ++p) + go_error_at((*p)->location(), + ("invalid receiver type " + "(receiver must be a named type")); + return; + } + } + for (std::vector<Named_object*>::const_iterator p = this->methods_.begin(); p != this->methods_.end(); ++p) @@ -6974,9 +7236,9 @@ Named_object::export_named_object(Export* exp) const break; case NAMED_OBJECT_TYPE_DECLARATION: - error_at(this->type_declaration_value()->location(), - "attempt to export %<%s%> which was declared but not defined", - this->message_name().c_str()); + go_error_at(this->type_declaration_value()->location(), + "attempt to export %<%s%> which was declared but not defined", + this->message_name().c_str()); break; case NAMED_OBJECT_FUNC_DECLARATION: @@ -7088,6 +7350,14 @@ Named_object::get_backend(Gogo* gogo, std::vector<Bexpression*>& const_decls, std::vector<Btype*>& type_decls, std::vector<Bfunction*>& func_decls) { + // If this is a definition, avoid trying to get the backend + // representation, as that can crash. + if (this->is_redefinition_) + { + go_assert(saw_errors()); + return; + } + switch (this->classification_) { case NAMED_OBJECT_CONST: @@ -7121,8 +7391,9 @@ Named_object::get_backend(Gogo* gogo, std::vector<Bexpression*>& const_decls, break; case NAMED_OBJECT_TYPE_DECLARATION: - error("reference to undefined type %qs", - this->message_name().c_str()); + go_error_at(Linemap::unknown_location(), + "reference to undefined type %qs", + this->message_name().c_str()); return; case NAMED_OBJECT_VAR: @@ -7354,7 +7625,9 @@ Bindings::new_definition(Named_object* old_object, Named_object* new_object) if (new_object->is_function_declaration()) { if (!new_object->func_declaration_value()->asm_name().empty()) - sorry("__asm__ for function definitions"); + go_error_at(Linemap::unknown_location(), + ("sorry, not implemented: " + "__asm__ for function definitions")); Function_type* old_type = old_object->func_value()->type(); Function_type* new_type = new_object->func_declaration_value()->type(); @@ -7373,7 +7646,9 @@ Bindings::new_definition(Named_object* old_object, Named_object* new_object) if (old_type->is_valid_redeclaration(new_type, &reason)) { if (!old_object->func_declaration_value()->asm_name().empty()) - sorry("__asm__ for function definitions"); + go_error_at(Linemap::unknown_location(), + ("sorry, not implemented: " + "__asm__ for function definitions")); old_object->set_function_value(new_object->func_value()); this->named_objects_.push_back(old_object); return old_object; @@ -7388,15 +7663,15 @@ Bindings::new_definition(Named_object* old_object, Named_object* new_object) std::string n = old_object->message_name(); if (reason.empty()) - error_at(new_object->location(), "redefinition of %qs", n.c_str()); + go_error_at(new_object->location(), "redefinition of %qs", n.c_str()); else - error_at(new_object->location(), "redefinition of %qs: %s", n.c_str(), - reason.c_str()); + go_error_at(new_object->location(), "redefinition of %qs: %s", n.c_str(), + reason.c_str()); old_object->set_is_redefinition(); new_object->set_is_redefinition(); - inform(old_object->location(), "previous definition of %qs was here", - n.c_str()); + go_inform(old_object->location(), "previous definition of %qs was here", + n.c_str()); return old_object; } @@ -7687,11 +7962,10 @@ Unnamed_label::get_goto(Translate_context* context, Location location) Package::Package(const std::string& pkgpath, const std::string& pkgpath_symbol, Location location) : pkgpath_(pkgpath), pkgpath_symbol_(pkgpath_symbol), - package_name_(), bindings_(new Bindings(NULL)), priority_(0), + package_name_(), bindings_(new Bindings(NULL)), location_(location) { go_assert(!pkgpath.empty()); - } // Set the package name. @@ -7703,10 +7977,11 @@ Package::set_package_name(const std::string& package_name, Location location) if (this->package_name_.empty()) this->package_name_ = package_name; else if (this->package_name_ != package_name) - error_at(location, - "saw two different packages with the same package path %s: %s, %s", - this->pkgpath_.c_str(), this->package_name_.c_str(), - package_name.c_str()); + go_error_at(location, + ("saw two different packages with " + "the same package path %s: %s, %s"), + this->pkgpath_.c_str(), this->package_name_.c_str(), + package_name.c_str()); } // Return the pkgpath symbol, which is a prefix for symbols defined in @@ -7732,16 +8007,6 @@ Package::set_pkgpath_symbol(const std::string& pkgpath_symbol) go_assert(this->pkgpath_symbol_ == pkgpath_symbol); } -// Set the priority. We may see multiple priorities for an imported -// package; we want to use the largest one. - -void -Package::set_priority(int priority) -{ - if (priority > this->priority_) - this->priority_ = priority; -} - // Note that symbol from this package was and qualified by ALIAS. void @@ -7766,8 +8031,8 @@ Package::forget_usage(Expression* usage) const this->fake_uses_.erase(p); if (this->fake_uses_.empty()) - error_at(this->location(), "imported and not used: %s", - Gogo::message_name(this->package_name()).c_str()); + go_error_at(this->location(), "imported and not used: %s", + Gogo::message_name(this->package_name()).c_str()); } // Clear the used field for the next file. If the only usages of this package @@ -7851,13 +8116,14 @@ Traverse::remember_type(const Type* type) } // Record that we are looking at an expression, and return true if we -// have already seen it. +// have already seen it. NB: this routine used to assert if the traverse +// mask did not include expressions/types -- this is no longer the case, +// since it can be useful to remember specific expressions during +// walks that only cover statements. bool Traverse::remember_expression(const Expression* expression) { - go_assert((this->traverse_mask() & traverse_types) != 0 - || (this->traverse_mask() & traverse_expressions) != 0); if (this->expressions_seen_ == NULL) this->expressions_seen_ = new Expressions_seen(); std::pair<Expressions_seen::iterator, bool> ins = diff --git a/gcc/go/gofrontend/gogo.h b/gcc/go/gofrontend/gogo.h index 0b1f8ef3ca..7c29828231 100644 --- a/gcc/go/gofrontend/gogo.h +++ b/gcc/go/gofrontend/gogo.h @@ -7,7 +7,6 @@ #ifndef GO_GOGO_H #define GO_GOGO_H -#include "escape.h" #include "go-linemap.h" class Traverse; @@ -20,6 +19,7 @@ class Typed_identifier; class Typed_identifier_list; class Function_type; class Expression; +class Expression_list; class Statement; class Temporary_statement; class Block; @@ -51,6 +51,8 @@ class Bblock; class Bvariable; class Blabel; class Bfunction; +class Escape_context; +class Node; // This file declares the basic classes used to hold the internal // representation of Go which is built by the parser. @@ -77,33 +79,64 @@ class Import_init init_name() const { return this->init_name_; } - // The priority of the initialization function. Functions with a - // lower priority number must be run first. + // Older V1 export data uses a priority scheme to order + // initialization functions; functions with a lower priority number + // must be run first. This value will be set to -1 for current + // generation objects, and will take on a non-negative value only + // when importing a V1-vintage object. int priority() const { return this->priority_; } + // Reset priority. + void + set_priority(int new_priority) + { this->priority_ = new_priority; } + + // Record the fact that some other init fcn must be run before this init fcn. + void + record_precursor_fcn(std::string init_fcn_name) + { this->precursor_functions_.insert(init_fcn_name); } + + // Return the list of precursor fcns for this fcn (must be run before it). + const std::set<std::string>& + precursors() const + { return this->precursor_functions_; } + private: // The name of the package being imported. std::string package_name_; // The name of the package's init function. std::string init_name_; - // The priority. + // Names of init functions that must be run before this fcn. + std::set<std::string> precursor_functions_; + // Priority for this function. See note above on obsolescence. int priority_; }; // For sorting purposes. +struct Import_init_lt { + bool operator()(const Import_init* i1, const Import_init* i2) + { + return i1->init_name() < i2->init_name(); + } +}; + +// Set of import init objects. +class Import_init_set : public std::set<Import_init*, Import_init_lt> { +}; + inline bool -operator<(const Import_init& i1, const Import_init& i2) +priority_compare(const Import_init* i1, const Import_init* i2) { - if (i1.priority() < i2.priority()) + if (i1->priority() < i2->priority()) return true; - if (i1.priority() > i2.priority()) + if (i1->priority() > i2->priority()) return false; - if (i1.package_name() != i2.package_name()) - return i1.package_name() < i2.package_name(); - return i1.init_name() < i2.init_name(); + if (i1->package_name() != i2->package_name()) + return i1->package_name() < i2->package_name(); + return i1->init_name() < i2->init_name(); } // The holder for the internal representation of the entire @@ -126,21 +159,6 @@ class Gogo linemap() { return this->linemap_; } - // Get the Call Graph. - const std::set<Node*>& - call_graph() const - { return this->call_graph_; } - - // Get the roots of each connection graph. - const std::set<Node*>& - connection_roots() const - { return this->connection_roots_; } - - // Get the nodes that escape globally. - const std::set<Node*>& - global_connections() const - { return this->global_connections_; } - // Get the package name. const std::string& package_name() const; @@ -184,6 +202,27 @@ class Gogo } // Given a name which may or may not have been hidden, return the + // name to use within a mangled symbol name. + static std::string + mangle_possibly_hidden_name(const std::string& name) + { + // FIXME: This adds in pkgpath twice for hidden symbols, which is + // less than ideal. + std::string n; + if (!Gogo::is_hidden_name(name)) + n = name; + else + { + n = "."; + std::string pkgpath = Gogo::hidden_name_pkgpath(name); + n.append(Gogo::pkgpath_for_symbol(pkgpath)); + n.append(1, '.'); + n.append(Gogo::unpack_hidden_name(name)); + } + return n; + } + + // Given a name which may or may not have been hidden, return the // name to use in an error message. static std::string message_name(const std::string& name); @@ -233,6 +272,12 @@ class Gogo set_relative_import_path(const std::string& s) { this->relative_import_path_ = s; } + // Set the C header file to write. This is used for the runtime + // package. + void + set_c_header(const std::string& s) + { this->c_header_ = s; } + // Return whether to check for division by zero in binary operations. bool check_divide_by_zero() const @@ -253,18 +298,32 @@ class Gogo set_check_divide_overflow(bool b) { this->check_divide_overflow_ = b; } - // Return the priority to use for the package we are compiling. - // This is two more than the largest priority of any package we - // import. + // Return whether we are compiling the runtime package. + bool + compiling_runtime() const + { return this->compiling_runtime_; } + + // Set whether we are compiling the runtime package. + void + set_compiling_runtime(bool b) + { this->compiling_runtime_ = b; } + + // Return the level of escape analysis debug information to emit. int - package_priority() const; + debug_escape_level() const + { return this->debug_escape_level_; } + + // Set the level of escape analysis debugging from a command line option. + void + set_debug_escape_level(int level) + { this->debug_escape_level_ = level; } // 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. void import_package(const std::string& filename, const std::string& local_name, - bool is_local_name_exported, Location); + bool is_local_name_exported, bool must_exist, Location); // Whether we are the global binding level. bool @@ -361,21 +420,15 @@ class Gogo add_label_reference(const std::string&, Location, bool issue_goto_errors); - // Add a FUNCTION to the call graph. - Node* - add_call_node(Named_object* function); + // An analysis set is a list of functions paired with a boolean that indicates + // whether the list of functions are recursive. + typedef std::pair<std::vector<Named_object*>, bool> Analysis_set; - // Lookup the call node for FUNCTION. - Node* - lookup_call_node(Named_object* function) const; - - // Add a connection node for OBJECT. - Node* - add_connection_node(Named_object* object); - - // Lookup the connection node for OBJECT. - Node* - lookup_connection_node(Named_object* object) const; + // Add a GROUP of possibly RECURSIVE functions to the Analysis_set for this + // package. + void + add_analysis_set(const std::vector<Named_object*>& group, bool recursive) + { this->analysis_sets_.push_back(std::make_pair(group, recursive)); } // Return a snapshot of the current binding state. Bindings_snapshot* @@ -438,6 +491,12 @@ class Gogo add_file_block_name(const std::string& name, Location location) { this->file_block_names_[name] = location; } + // Add a linkname, from the go:linkname compiler directive. This + // changes the externally visible name of go_name to be ext_name. + void + add_linkname(const std::string& go_name, bool is_exported, + const std::string& ext_name, Location location); + // Mark all local variables in current bindings as used. This is // used when there is a parse error to avoid useless errors. void @@ -473,6 +532,11 @@ class Gogo set_need_init_fn() { this->need_init_fn_ = true; } + // Return whether the current file imported the unsafe package. + bool + current_file_imported_unsafe() const + { return this->current_file_imported_unsafe_; } + // Clear out all names in file scope. This is called when we start // parsing a new file. void @@ -499,7 +563,7 @@ class Gogo // used when a type-specific function is needed when not at the top // level. void - queue_specific_type_function(Type* type, Named_type* name, + queue_specific_type_function(Type* type, Named_type* name, int64_t size, const std::string& hash_name, Function_type* hash_fntype, const std::string& equal_name, @@ -514,6 +578,15 @@ class Gogo specific_type_functions_are_written() const { return this->specific_type_functions_are_written_; } + // Add a pointer that needs to be added to the list of objects + // traversed by the garbage collector. This should be an expression + // of pointer type that points to static storage. It's not + // necessary to add global variables to this list, just global + // variable initializers that would otherwise not be seen. + void + add_gc_root(Expression* expr) + { this->gc_roots_.push_back(expr); } + // Traverse the tree. See the Traverse class. void traverse(Traverse*); @@ -576,25 +649,27 @@ class Gogo void check_return_statements(); - // Build call graph. + // Analyze the program flow for escape information. void - build_call_graph(); + analyze_escape(); - // Build connection graphs. + // Discover the groups of possibly recursive functions in this package. void - build_connection_graphs(); + discover_analysis_sets(); - // Analyze reachability in the connection graphs. + // Build a connectivity graph between the objects in each analyzed function. void - analyze_reachability(); + assign_connectivity(Escape_context*, Named_object*); - // Record escape information in function signatures for export data. + // Traverse the objects in the connecitivty graph from the sink, adjusting the + // escape levels of each object. void - mark_escaping_signatures(); + propagate_escape(Escape_context*, Node*); - // Optimize variable allocation. + // Add notes about the escape level of a function's input and output + // parameters for exporting and importing top level functions. void - optimize_allocations(const char** filenames); + tag_function(Escape_context*, Named_object*); // Do all exports. void @@ -606,6 +681,10 @@ class Gogo add_import_init_fn(const std::string& package_name, const std::string& init_name, int prio); + // Return the Import_init for a given init name. + Import_init* + lookup_init(const std::string& init_name); + // Turn short-cut operators (&&, ||) into explicit if statements. void remove_shortcuts(); @@ -622,6 +701,11 @@ class Gogo void build_recover_thunks(); + // Return a declaration for __builtin_return_address or + // __builtin_frame_address. + static Named_object* + declare_builtin_rf_address(const char* name); + // Simplify statements which might use thunks: go and defer // statements. void @@ -699,6 +783,9 @@ class Gogo const Bindings* current_bindings() const; + void + write_c_header(); + // Get the decl for the magic initialization function. Named_object* initialization_function_decl(); @@ -707,14 +794,16 @@ class Gogo Named_object* create_initialization_function(Named_object* fndecl, Bstatement* code_stmt); - // Initialize imported packages. + // Initialize imported packages. BFUNCTION is the function + // into which the package init calls will be placed. void - init_imports(std::vector<Bstatement*>&); + init_imports(std::vector<Bstatement*>&, Bfunction* bfunction); // Register variables with the garbage collector. void register_gc_vars(const std::vector<Named_object*>&, - std::vector<Bstatement*>&); + std::vector<Bstatement*>&, + Bfunction* init_bfunction); // Type used to map import names to packages. typedef std::map<std::string, Package*> Imports; @@ -730,31 +819,37 @@ class Gogo // where they were defined. typedef Unordered_map(std::string, Location) File_block_names; - // Type used to map named objects that refer to objects to the - // node that represent them in the escape analysis graphs. - typedef Unordered_map(Named_object*, Node*) Named_escape_nodes; - // Type used to queue writing a type specific function. struct Specific_type_function { Type* type; Named_type* name; + int64_t size; std::string hash_name; Function_type* hash_fntype; std::string equal_name; Function_type* equal_fntype; - Specific_type_function(Type* atype, Named_type* aname, + Specific_type_function(Type* atype, Named_type* aname, int64_t asize, const std::string& ahash_name, Function_type* ahash_fntype, const std::string& aequal_name, Function_type* aequal_fntype) - : type(atype), name(aname), hash_name(ahash_name), + : type(atype), name(aname), size(asize), hash_name(ahash_name), hash_fntype(ahash_fntype), equal_name(aequal_name), equal_fntype(aequal_fntype) { } }; + // Recompute init priorities. + void + recompute_init_priorities(); + + // Recursive helper used by the routine above. + void + update_init_priority(Import_init* ii, + std::set<const Import_init *>* visited); + // The backend generator. Backend* backend_; // The object used to keep track of file names and line numbers. @@ -766,26 +861,14 @@ class Gogo // The global binding contour. This includes the builtin functions // and the package we are compiling. Bindings* globals_; - // The call graph for a program execution which represents the functions - // encountered and the caller-callee relationship between the functions. - std::set<Node*> call_graph_; - // The nodes that form the roots of the connection graphs for each called - // function and represent the connectivity relationship between all objects - // in the function. - std::set<Node*> connection_roots_; - // All connection nodes that have an escape state of ESCAPE_GLOBAL are a part - // of a special connection graph of only global variables. - std::set<Node*> global_connections_; - // Mapping from named objects to nodes in the call graph. - Named_escape_nodes named_call_nodes_; - // Mapping from named objects to nodes in a connection graph. - Named_escape_nodes named_connection_nodes_; // The list of names we have seen in the file block. File_block_names file_block_names_; // Mapping from import file names to packages. Imports imports_; // Whether the magic unsafe package was imported. bool imported_unsafe_; + // Whether the magic unsafe package was imported by the current file. + bool current_file_imported_unsafe_; // Mapping from package names we have seen to packages. This does // not include the package we are compiling. Packages packages_; @@ -800,7 +883,7 @@ class Gogo // The name of the magic initialization function. std::string init_fn_name_; // A list of import control variables for packages that we import. - std::set<Import_init> imported_init_fns_; + Import_init_set imported_init_fns_; // The package path used for reflection data. std::string pkgpath_; // The package path to use for a symbol name. @@ -816,12 +899,20 @@ class Gogo // The relative import path, from the -fgo-relative-import-path // option. std::string relative_import_path_; + // The C header file to write, from the -fgo-c-header option. + std::string c_header_; // Whether or not to check for division by zero, from the // -fgo-check-divide-zero option. bool check_divide_by_zero_; // Whether or not to check for division overflow, from the // -fgo-check-divide-overflow option. bool check_divide_overflow_; + // Whether we are compiling the runtime package, from the + // -fgo-compiling-runtime option. + bool compiling_runtime_; + // The level of escape analysis debug information to emit, from the + // -fgo-debug-escape option. + int debug_escape_level_; // A list of types to verify. std::vector<Type*> verify_types_; // A list of interface types defined while parsing. @@ -832,6 +923,11 @@ class Gogo bool specific_type_functions_are_written_; // Whether named types have been converted. bool named_types_are_converted_; + // A list containing groups of possibly mutually recursive functions to be + // considered during escape analysis. + std::vector<Analysis_set> analysis_sets_; + // A list of objects to add to the GC roots. + std::vector<Expression*> gc_roots_; }; // A block of statements. @@ -995,23 +1091,27 @@ class Function results_are_named() const { return this->results_are_named_; } + // Set the assembler name. + void + set_asm_name(const std::string& asm_name) + { this->asm_name_ = asm_name; } + + // Set the pragmas for this function. + void + set_pragmas(unsigned int pragmas) + { + this->pragmas_ = pragmas; + } + // Whether this method should not be included in the type // descriptor. bool - nointerface() const - { - go_assert(this->is_method()); - return this->nointerface_; - } + nointerface() const; // Record that this method should not be included in the type // descriptor. void - set_nointerface() - { - go_assert(this->is_method()); - this->nointerface_ = true; - } + set_nointerface(); // Record that this function is a stub method created for an unnamed // type. @@ -1215,11 +1315,8 @@ class Function // Import a function. static void import_func(Import*, std::string* pname, Typed_identifier** receiver, - Node::Escapement_lattice* rcvr_escape, Typed_identifier_list** pparameters, - Node::Escape_states** pparam_escapes, - Typed_identifier_list** presults, bool* is_varargs, - bool* has_escape_info); + Typed_identifier_list** presults, bool* is_varargs); private: // Type for mapping from label names to Label objects. @@ -1253,6 +1350,9 @@ class Function Labels labels_; // The number of local types defined in this function. unsigned int local_type_count_; + // The assembler name: this is the name that will be put in the object file. + // Set by the go:linkname compiler directive. This is normally empty. + std::string asm_name_; // The function descriptor, if any. Expression* descriptor_; // The function decl. @@ -1261,12 +1361,12 @@ class Function // distinguish the defer stack for one function from another. This // is NULL unless we actually need a defer stack. Temporary_statement* defer_stack_; + // Pragmas for this function. This is a set of GOPRAGMA bits. + unsigned int pragmas_; // True if this function is sink-named. No code is generated. bool is_sink_ : 1; // True if the result variables are named. bool results_are_named_ : 1; - // True if this method should not be included in the type descriptor. - bool nointerface_ : 1; // True if this function is a stub method created for an unnamed // type. bool is_unnamed_type_stub_method_ : 1; @@ -1328,7 +1428,7 @@ class Function_declaration public: Function_declaration(Function_type* fntype, Location location) : fntype_(fntype), location_(location), asm_name_(), descriptor_(NULL), - fndecl_(NULL) + fndecl_(NULL), pragmas_(0) { } Function_type* @@ -1348,6 +1448,13 @@ class Function_declaration set_asm_name(const std::string& asm_name) { this->asm_name_ = asm_name; } + // Set the pragmas for this function. + void + set_pragmas(unsigned int pragmas) + { + this->pragmas_ = pragmas; + } + // Return an expression for the function descriptor, given the named // object for this function. This may only be called for functions // without a closure. This will be an immutable struct with one @@ -1390,6 +1497,8 @@ class Function_declaration Expression* descriptor_; // The function decl if needed. Bfunction* fndecl_; + // Pragmas for this function. This is a set of GOPRAGMA bits. + unsigned int pragmas_; }; // A variable. @@ -2614,7 +2723,7 @@ class Label public: Label(const std::string& name) : name_(name), location_(Linemap::unknown_location()), snapshot_(NULL), - refs_(), is_used_(false), blabel_(NULL) + refs_(), is_used_(false), blabel_(NULL), depth_(DEPTH_UNKNOWN) { } // Return the label's name. @@ -2637,6 +2746,26 @@ class Label set_is_used() { this->is_used_ = true; } + // Return whether this label is looping. + bool + looping() const + { return this->depth_ == DEPTH_LOOPING; } + + // Set this label as looping. + void + set_looping() + { this->depth_ = DEPTH_LOOPING; } + + // Return whether this label is nonlooping. + bool + nonlooping() const + { return this->depth_ == DEPTH_NONLOOPING; } + + // Set this label as nonlooping. + void + set_nonlooping() + { this->depth_ = DEPTH_NONLOOPING; } + // Return the location of the definition. Location location() const @@ -2696,6 +2825,16 @@ class Label is_dummy_label() const { return this->name_ == "_"; } + // A classification of a label's looping depth. + enum Loop_depth + { + DEPTH_UNKNOWN, + // A label never jumped to. + DEPTH_NONLOOPING, + // A label jumped to. + DEPTH_LOOPING + }; + private: // The name of the label. std::string name_; @@ -2711,6 +2850,8 @@ class Label bool is_used_; // The backend representation. Blabel* blabel_; + // The looping depth of this label, for escape analysis. + Loop_depth depth_; }; // An unnamed label. These are used when lowering loops. @@ -2719,7 +2860,7 @@ class Unnamed_label { public: Unnamed_label(Location location) - : location_(location), blabel_(NULL) + : location_(location), derived_from_(NULL), blabel_(NULL) { } // Get the location where the label is defined. @@ -2732,6 +2873,16 @@ class Unnamed_label set_location(Location location) { this->location_ = location; } + // Get the top level statement this unnamed label is derived from. + Statement* + derived_from() const + { return this->derived_from_; } + + // Set the top level statement this unnamed label is derived from. + void + set_derived_from(Statement* s) + { this->derived_from_ = s; } + // Return a statement which defines this label. Bstatement* get_definition(Translate_context*); @@ -2747,6 +2898,9 @@ class Unnamed_label // The location where the label is defined. Location location_; + // The top-level statement this unnamed label was derived/lowered from. + // This is NULL is this label is not the top-level of a lowered statement. + Statement* derived_from_; // The backend representation of this label. Blabel* blabel_; }; @@ -2824,17 +2978,6 @@ class Package return this->package_name_; } - // The priority of this package. The init function of packages with - // lower priority must be run before the init function of packages - // with higher priority. - int - priority() const - { return this->priority_; } - - // Set the priority. - void - set_priority(int priority); - // Return the bindings. Bindings* bindings() @@ -2926,10 +3069,6 @@ class Package std::string package_name_; // The names in this package. Bindings* bindings_; - // The priority of this package. A package has a priority higher - // than the priority of all of the packages that it imports. This - // is used to run init functions in the right order. - int priority_; // The location of the most recent import statement. Location location_; // The set of aliases associated with this package. diff --git a/gcc/go/gofrontend/import-archive.cc b/gcc/go/gofrontend/import-archive.cc index 7d7f426059..18d1fdc24b 100644 --- a/gcc/go/gofrontend/import-archive.cc +++ b/gcc/go/gofrontend/import-archive.cc @@ -6,6 +6,7 @@ #include "go-system.h" +#include "go-diagnostics.h" #include "import.h" #ifndef O_BINARY @@ -144,7 +145,7 @@ Archive_file::initialize() struct stat st; if (fstat(this->fd_, &st) < 0) { - error_at(this->location_, "%s: %m", this->filename_.c_str()); + go_error_at(this->location_, "%s: %m", this->filename_.c_str()); return false; } this->filesize_ = st.st_size; @@ -153,7 +154,7 @@ Archive_file::initialize() if (::lseek(this->fd_, 0, SEEK_SET) < 0 || ::read(this->fd_, buf, sizeof(armagt)) != sizeof(armagt)) { - error_at(this->location_, "%s: %m", this->filename_.c_str()); + go_error_at(this->location_, "%s: %m", this->filename_.c_str()); return false; } this->is_thin_archive_ = memcmp(buf, armagt, sizeof(armagt)) == 0; @@ -183,7 +184,7 @@ Archive_file::initialize() char* rdbuf = new char[size]; if (::read(this->fd_, rdbuf, size) != size) { - error_at(this->location_, "%s: could not read extended names", + go_error_at(this->location_, "%s: could not read extended names", filename.c_str()); delete[] rdbuf; return false; @@ -203,7 +204,7 @@ Archive_file::read(off_t offset, off_t size, char* buf) if (::lseek(this->fd_, offset, SEEK_SET) < 0 || ::read(this->fd_, buf, size) != size) { - error_at(this->location_, "%s: %m", this->filename_.c_str()); + go_error_at(this->location_, "%s: %m", this->filename_.c_str()); return false; } return true; @@ -219,20 +220,20 @@ Archive_file::read_header(off_t off, std::string* pname, off_t* size, Archive_header hdr; if (::lseek(this->fd_, off, SEEK_SET) < 0) { - error_at(this->location_, "%s: %m", this->filename_.c_str()); + go_error_at(this->location_, "%s: %m", this->filename_.c_str()); return false; } ssize_t got = ::read(this->fd_, &hdr, sizeof hdr); if (got != sizeof hdr) { if (got < 0) - error_at(this->location_, "%s: %m", this->filename_.c_str()); + go_error_at(this->location_, "%s: %m", this->filename_.c_str()); else if (got > 0) - error_at(this->location_, "%s: short archive header at %ld", - this->filename_.c_str(), static_cast<long>(off)); + go_error_at(this->location_, "%s: short archive header at %ld", + this->filename_.c_str(), static_cast<long>(off)); else - error_at(this->location_, "%s: unexpected EOF at %ld", - this->filename_.c_str(), static_cast<long>(off)); + go_error_at(this->location_, "%s: unexpected EOF at %ld", + this->filename_.c_str(), static_cast<long>(off)); } off_t local_nested_off; if (!this->interpret_header(&hdr, off, pname, size, &local_nested_off)) @@ -252,8 +253,8 @@ Archive_file::interpret_header(const Archive_header* hdr, off_t off, { if (memcmp(hdr->ar_fmag, arfmag, sizeof arfmag) != 0) { - error_at(this->location_, "%s: malformed archive header at %lu", - this->filename_.c_str(), static_cast<unsigned long>(off)); + go_error_at(this->location_, "%s: malformed archive header at %lu", + this->filename_.c_str(), static_cast<unsigned long>(off)); return false; } @@ -272,8 +273,8 @@ Archive_file::interpret_header(const Archive_header* hdr, off_t off, || *size < 0 || (*size == LONG_MAX && errno == ERANGE)) { - error_at(this->location_, "%s: malformed archive header size at %lu", - this->filename_.c_str(), static_cast<unsigned long>(off)); + go_error_at(this->location_, "%s: malformed archive header size at %lu", + this->filename_.c_str(), static_cast<unsigned long>(off)); return false; } @@ -284,8 +285,9 @@ Archive_file::interpret_header(const Archive_header* hdr, off_t off, if (name_end == NULL || name_end - hdr->ar_name >= static_cast<int>(sizeof hdr->ar_name)) { - error_at(this->location_, "%s: malformed archive header name at %lu", - this->filename_.c_str(), static_cast<unsigned long>(off)); + go_error_at(this->location_, + "%s: malformed archive header name at %lu", + this->filename_.c_str(), static_cast<unsigned long>(off)); return false; } pname->assign(hdr->ar_name, name_end - hdr->ar_name); @@ -321,8 +323,8 @@ Archive_file::interpret_header(const Archive_header* hdr, off_t off, || (x == LONG_MAX && errno == ERANGE) || static_cast<size_t>(x) >= this->extended_names_.size()) { - error_at(this->location_, "%s: bad extended name index at %lu", - this->filename_.c_str(), static_cast<unsigned long>(off)); + go_error_at(this->location_, "%s: bad extended name index at %lu", + this->filename_.c_str(), static_cast<unsigned long>(off)); return false; } @@ -331,8 +333,9 @@ Archive_file::interpret_header(const Archive_header* hdr, off_t off, if (static_cast<size_t>(name_end - name) > this->extended_names_.size() || name_end[-1] != '/') { - error_at(this->location_, "%s: bad extended name entry at header %lu", - this->filename_.c_str(), static_cast<unsigned long>(off)); + go_error_at(this->location_, + "%s: bad extended name entry at header %lu", + this->filename_.c_str(), static_cast<unsigned long>(off)); return false; } pname->assign(name, name_end - 1 - name); @@ -380,8 +383,8 @@ Archive_file::get_file_and_offset(off_t off, const std::string& hdrname, int nfd = open(filename.c_str(), O_RDONLY | O_BINARY); if (nfd < 0) { - error_at(this->location_, "%s: can't open nested archive %s", - this->filename_.c_str(), filename.c_str()); + go_error_at(this->location_, "%s: can't open nested archive %s", + this->filename_.c_str(), filename.c_str()); return false; } nfile = new Archive_file(filename, nfd, this->location_); @@ -406,7 +409,7 @@ Archive_file::get_file_and_offset(off_t off, const std::string& hdrname, *memfd = open(filename.c_str(), O_RDONLY | O_BINARY); if (*memfd < 0) { - error_at(this->location_, "%s: %m", filename.c_str()); + go_error_at(this->location_, "%s: %m", filename.c_str()); return false; } *memoff = 0; @@ -499,10 +502,10 @@ Archive_iterator::read_next_header() { if (filesize != this->off_) { - error_at(this->afile_->location(), - "%s: short archive header at %lu", - this->afile_->filename().c_str(), - static_cast<unsigned long>(this->off_)); + go_error_at(this->afile_->location(), + "%s: short archive header at %lu", + this->afile_->filename().c_str(), + static_cast<unsigned long>(this->off_)); this->off_ = filesize; } this->header_.off = filesize; diff --git a/gcc/go/gofrontend/import.cc b/gcc/go/gofrontend/import.cc index 1bb6ad8acf..f6b4e0c056 100644 --- a/gcc/go/gofrontend/import.cc +++ b/gcc/go/gofrontend/import.cc @@ -7,9 +7,9 @@ #include "go-system.h" #include "filenames.h" -#include "simple-object.h" #include "go-c.h" +#include "go-diagnostics.h" #include "gogo.h" #include "lex.h" #include "types.h" @@ -133,7 +133,7 @@ Import::try_package_in_directory(const std::string& filename, if (fd < 0) { if (errno != ENOENT && errno != EISDIR) - warning_at(location, 0, "%s: %m", filename.c_str()); + go_warning_at(location, 0, "%s: %m", filename.c_str()); fd = Import::try_suffixes(&found_filename); if (fd < 0) @@ -147,8 +147,8 @@ Import::try_package_in_directory(const std::string& filename, close(fd); - error_at(location, "%s exists but does not contain any Go export data", - found_filename.c_str()); + go_error_at(location, "%s exists but does not contain any Go export data", + found_filename.c_str()); return NULL; } @@ -200,8 +200,7 @@ Import::try_suffixes(std::string* pfilename) // Look for export data in the file descriptor FD. Import::Stream* -Import::find_export_data(const std::string& filename, int fd, - Location location) +Import::find_export_data(const std::string& filename, int fd, Location location) { // See if we can read this as an object file. Import::Stream* stream = Import::find_object_export_data(filename, fd, 0, @@ -209,11 +208,11 @@ Import::find_export_data(const std::string& filename, int fd, if (stream != NULL) return stream; - const int len = MAX(Export::v1_magic_len, Import::archive_magic_len); + const int len = MAX(Export::magic_len, Import::archive_magic_len); if (lseek(fd, 0, SEEK_SET) < 0) { - error_at(location, "lseek %s failed: %m", filename.c_str()); + go_error_at(location, "lseek %s failed: %m", filename.c_str()); return NULL; } @@ -223,7 +222,8 @@ Import::find_export_data(const std::string& filename, int fd, return NULL; // Check for a file containing nothing but Go export data. - if (memcmp(buf, Export::v1_magic, Export::v1_magic_len) == 0) + if (memcmp(buf, Export::cur_magic, Export::magic_len) == 0 || + memcmp(buf, Export::v1_magic, Export::magic_len) == 0) return new Stream_from_file(fd); // See if we can read this as an archive. @@ -233,7 +233,7 @@ Import::find_export_data(const std::string& filename, int fd, return NULL; } -// Look for export data in a simple_object. +// Look for export data in an object file. Import::Stream* Import::find_object_export_data(const std::string& filename, @@ -248,10 +248,10 @@ Import::find_object_export_data(const std::string& filename, if (errmsg != NULL) { if (err == 0) - error_at(location, "%s: %s", filename.c_str(), errmsg); + go_error_at(location, "%s: %s", filename.c_str(), errmsg); else - error_at(location, "%s: %s: %s", filename.c_str(), errmsg, - xstrerror(err)); + go_error_at(location, "%s: %s: %s", filename.c_str(), errmsg, + xstrerror(err)); return NULL; } @@ -270,7 +270,7 @@ Import::Import(Stream* stream, Location location) : gogo_(NULL), stream_(stream), location_(location), package_(NULL), add_to_globals_(false), builtin_types_((- SMALLEST_BUILTIN_CODE) + 1), - types_() + types_(), version_(EXPORT_FORMAT_UNKNOWN) { } @@ -293,8 +293,26 @@ Import::import(Gogo* gogo, const std::string& local_name, // The vector of types is package specific. this->types_.clear(); - stream->require_bytes(this->location_, Export::v1_magic, - Export::v1_magic_len); + // Check magic string / version number. + if (stream->match_bytes(Export::cur_magic, Export::magic_len)) + { + stream->require_bytes(this->location_, Export::cur_magic, + Export::magic_len); + this->version_ = EXPORT_FORMAT_CURRENT; + } + else if (stream->match_bytes(Export::v1_magic, Export::magic_len)) + { + stream->require_bytes(this->location_, Export::v1_magic, + Export::magic_len); + this->version_ = EXPORT_FORMAT_V1; + } + else + { + go_error_at(this->location_, + ("error in import data at %d: invalid magic string"), + stream->pos()); + return NULL; + } this->require_c_string("package "); std::string package_name = this->read_identifier(); @@ -330,13 +348,16 @@ Import::import(Gogo* gogo, const std::string& local_name, return NULL; } - this->require_c_string("priority "); - std::string priority_string = this->read_identifier(); - int prio; - if (!this->string_to_int(priority_string, false, &prio)) - return NULL; - this->package_->set_priority(prio); - this->require_c_string(";\n"); + // Read and discard priority if older V1 export data format. + if (version() == EXPORT_FORMAT_V1) + { + this->require_c_string("priority "); + std::string priority_string = this->read_identifier(); + int prio; + if (!this->string_to_int(priority_string, false, &prio)) + return NULL; + this->require_c_string(";\n"); + } while (stream->match_c_string("package")) this->read_one_package(); @@ -362,11 +383,11 @@ Import::import(Gogo* gogo, const std::string& local_name, break; else { - error_at(this->location_, - ("error in import data at %d: " - "expected %<const%>, %<type%>, %<var%>, " - "%<func%>, or %<checksum%>"), - stream->pos()); + go_error_at(this->location_, + ("error in import data at %d: " + "expected %<const%>, %<type%>, %<var%>, " + "%<func%>, or %<checksum%>"), + stream->pos()); stream->set_saw_error(); return NULL; } @@ -377,7 +398,7 @@ Import::import(Gogo* gogo, const std::string& local_name, // verify that the checksum matches at link time or at dynamic // load time. this->require_c_string("checksum "); - stream->advance(Export::v1_checksum_len * 2); + stream->advance(Export::checksum_len * 2); this->require_c_string(";\n"); } @@ -423,26 +444,88 @@ Import::read_one_import() p->set_package_name(package_name, this->location()); } -// Read the list of import control functions. +// Read the list of import control functions and/or init graph. void Import::read_import_init_fns(Gogo* gogo) { this->require_c_string("init"); + + // Maps init function to index in the "init" clause; needed + // to read the init_graph section. + std::map<std::string, unsigned> init_idx; + while (!this->match_c_string(";")) { + int priority = -1; + this->require_c_string(" "); std::string package_name = this->read_identifier(); this->require_c_string(" "); std::string init_name = this->read_identifier(); - this->require_c_string(" "); - std::string prio_string = this->read_identifier(); - int prio; - if (!this->string_to_int(prio_string, false, &prio)) - return; - gogo->add_import_init_fn(package_name, init_name, prio); + if (this->version_ == EXPORT_FORMAT_V1) + { + // Older version 1 init fcn export data format is: + // + // <packname> <fcn> <priority> + this->require_c_string(" "); + std::string prio_string = this->read_identifier(); + if (!this->string_to_int(prio_string, false, &priority)) + return; + } + gogo->add_import_init_fn(package_name, init_name, priority); + + // Record the index of this init fcn so that we can look it + // up by index in the subsequent init_graph section. + unsigned idx = init_idx.size(); + init_idx[init_name] = idx; } this->require_c_string(";\n"); + + if (this->match_c_string("init_graph")) + { + this->require_c_string("init_graph"); + + // Build a vector mapping init fcn slot to Import_init pointer. + go_assert(init_idx.size() > 0); + std::vector<Import_init*> import_initvec; + import_initvec.resize(init_idx.size()); + for (std::map<std::string, unsigned>::const_iterator it = + init_idx.begin(); + it != init_idx.end(); ++it) + { + const std::string& init_name = it->first; + Import_init* ii = gogo->lookup_init(init_name); + import_initvec[it->second] = ii; + } + + // Init graph format is: + // + // init_graph <src1> <sink1> <src2> <sink2> ... ; + // + // where src + sink are init functions indices. + + while (!this->match_c_string(";")) + { + this->require_c_string(" "); + std::string src_string = this->read_identifier(); + unsigned src; + if (!this->string_to_unsigned(src_string, &src)) return; + + this->require_c_string(" "); + std::string sink_string = this->read_identifier(); + unsigned sink; + if (!this->string_to_unsigned(sink_string, &sink)) return; + + go_assert(src < import_initvec.size()); + Import_init* ii_src = import_initvec[src]; + go_assert(sink < import_initvec.size()); + Import_init* ii_sink = import_initvec[sink]; + + ii_src->record_precursor_fcn(ii_sink->init_name()); + } + this->require_c_string(";\n"); + } } // Import a constant. @@ -502,28 +585,16 @@ Import::import_func(Package* package) { std::string name; Typed_identifier* receiver; - Node::Escapement_lattice rcvr_escape; Typed_identifier_list* parameters; - Node::Escape_states* param_escapes; Typed_identifier_list* results; bool is_varargs; - bool has_escape_info; - Function::import_func(this, &name, &receiver, &rcvr_escape, ¶meters, - ¶m_escapes, &results, &is_varargs, - &has_escape_info); + Function::import_func(this, &name, &receiver, + ¶meters, &results, &is_varargs); Function_type *fntype = Type::make_function_type(receiver, parameters, results, this->location_); if (is_varargs) fntype->set_is_varargs(); - if (has_escape_info) - { - if (fntype->is_method()) - fntype->set_receiver_escape_state(rcvr_escape); - fntype->set_parameter_escape_states(param_escapes); - fntype->set_has_escape_info(); - } - Location loc = this->location_; Named_object* no; if (fntype->is_method()) @@ -594,9 +665,9 @@ Import::read_type() : (static_cast<size_t>(index) >= this->types_.size() || this->types_[index] == NULL)) { - error_at(this->location_, - "error in import data at %d: bad type index %d", - stream->pos(), index); + go_error_at(this->location_, + "error in import data at %d: bad type index %d", + stream->pos(), index); stream->set_saw_error(); return Type::make_error_type(); } @@ -607,9 +678,9 @@ Import::read_type() if (c != ' ') { if (!stream->saw_error()) - error_at(this->location_, - "error in import data at %d: expect %< %> or %<>%>'", - stream->pos()); + go_error_at(this->location_, + "error in import data at %d: expect %< %> or %<>%>'", + stream->pos()); stream->set_saw_error(); stream->advance(1); return Type::make_error_type(); @@ -619,9 +690,9 @@ Import::read_type() || (static_cast<size_t>(index) < this->types_.size() && this->types_[index] != NULL)) { - error_at(this->location_, - "error in import data at %d: type index already defined", - stream->pos()); + go_error_at(this->location_, + "error in import data at %d: type index already defined", + stream->pos()); stream->set_saw_error(); return Type::make_error_type(); } @@ -666,6 +737,13 @@ Import::read_type() this->require_c_string(" "); + bool is_alias = false; + if (this->match_c_string("= ")) + { + stream->advance(2); + is_alias = true; + } + // The package name may follow. This is the name of the package in // the package clause of that package. The type name will include // the pkgpath, which may be different. @@ -698,8 +776,8 @@ Import::read_type() no = package->add_type_declaration(type_name, this->location_); else if (!no->is_type_declaration() && !no->is_type()) { - error_at(this->location_, "imported %<%s.%s%> both type and non-type", - pkgpath.c_str(), Gogo::message_name(type_name).c_str()); + go_error_at(this->location_, "imported %<%s.%s%> both type and non-type", + pkgpath.c_str(), Gogo::message_name(type_name).c_str()); stream->set_saw_error(); return Type::make_error_type(); } @@ -739,6 +817,9 @@ Import::read_type() // This type has not yet been imported. ntype->clear_is_visible(); + if (is_alias) + ntype->set_is_alias(); + if (!type->is_undefined() && type->interface_type() != NULL) this->gogo_->record_interface_type(type->interface_type()); @@ -774,19 +855,43 @@ Import::read_type() return type; } -// Read escape info in the import stream. +// Read an escape note. -Node::Escapement_lattice -Import::read_escape_info() +std::string +Import::read_escape() { - Stream* stream = this->stream_; - this->require_c_string("<escape "); + if (this->match_c_string(" <esc:")) + { + Stream* stream = this->stream_; + this->require_c_string(" <esc:"); - int escape_value = stream->get_char() - '0'; - this->require_c_string(">"); - return Node::Escapement_lattice(escape_value); + std::string escape = "esc:"; + int c; + while (true) + { + c = stream->get_char(); + if (c != 'x' && !ISXDIGIT(c)) + break; + escape += c; + } + + if (c != '>') + { + go_error_at(this->location(), + ("error in import data at %d: " + "expect %< %> or %<>%>, got %c"), + stream->pos(), c); + stream->set_saw_error(); + stream->advance(1); + escape = Escape_note::make_tag(Node::ESCAPE_UNKNOWN); + } + return escape; + } + else + return Escape_note::make_tag(Node::ESCAPE_UNKNOWN); } + // Register the builtin types. void @@ -868,8 +973,8 @@ Import::string_to_int(const std::string &s, bool is_neg_ok, int* ret) long prio = strtol(s.c_str(), &end, 10); if (*end != '\0' || prio > 0x7fffffff || (prio < 0 && !is_neg_ok)) { - error_at(this->location_, "invalid integer in import data at %d", - this->stream_->pos()); + go_error_at(this->location_, "invalid integer in import data at %d", + this->stream_->pos()); this->stream_->set_saw_error(); return false; } @@ -925,8 +1030,8 @@ Import::Stream::require_bytes(Location location, const char* bytes, || memcmp(bytes, read, length) != 0) { if (!this->saw_error_) - error_at(location, "import error at %d: expected %<%.*s%>", - this->pos(), static_cast<int>(length), bytes); + go_error_at(location, "import error at %d: expected %<%.*s%>", + this->pos(), static_cast<int>(length), bytes); this->saw_error_ = true; return; } @@ -940,7 +1045,7 @@ Stream_from_file::Stream_from_file(int fd) { if (lseek(fd, 0, SEEK_SET) != 0) { - error("lseek failed: %m"); + go_fatal_error(Linemap::unknown_location(), "lseek failed: %m"); this->set_saw_error(); } } @@ -968,7 +1073,7 @@ Stream_from_file::do_peek(size_t length, const char** bytes) if (got < 0) { if (!this->saw_error()) - error("read failed: %m"); + go_fatal_error(Linemap::unknown_location(), "read failed: %m"); this->set_saw_error(); return false; } @@ -976,7 +1081,7 @@ Stream_from_file::do_peek(size_t length, const char** bytes) if (lseek(this->fd_, - got, SEEK_CUR) != 0) { if (!this->saw_error()) - error("lseek failed: %m"); + go_fatal_error(Linemap::unknown_location(), "lseek failed: %m"); this->set_saw_error(); return false; } @@ -998,7 +1103,7 @@ Stream_from_file::do_advance(size_t skip) if (lseek(this->fd_, skip, SEEK_CUR) != 0) { if (!this->saw_error()) - error("lseek failed: %m"); + go_fatal_error(Linemap::unknown_location(), "lseek failed: %m"); this->set_saw_error(); } if (!this->data_.empty()) diff --git a/gcc/go/gofrontend/import.h b/gcc/go/gofrontend/import.h index 7aa1fc9b62..aab4b899ca 100644 --- a/gcc/go/gofrontend/import.h +++ b/gcc/go/gofrontend/import.h @@ -7,7 +7,6 @@ #ifndef GO_IMPORT_H #define GO_IMPORT_H -#include "escape.h" #include "export.h" #include "go-linemap.h" @@ -198,9 +197,9 @@ class Import Type* read_type(); - // Read escape information. - Node::Escapement_lattice - read_escape_info(); + // Read an escape note. + std::string + read_escape(); private: static Stream* @@ -233,7 +232,7 @@ class Import void read_one_import(); - // Read the import control functions. + // Read the import control functions and init graph. void read_import_init_fns(Gogo*); @@ -261,6 +260,21 @@ class Import bool string_to_int(const std::string&, bool is_neg_ok, int* ret); + // Get an unsigned integer from a string. + bool + string_to_unsigned(const std::string& s, unsigned* ret) + { + int ivalue; + if (!this->string_to_int(s, false, &ivalue)) + return false; + *ret = static_cast<unsigned>(ivalue); + return true; + } + + // Return the version number of the export data we're reading. + Export_data_version + version() const { return this->version_; } + // The general IR. Gogo* gogo_; // The stream from which to read import data. @@ -276,6 +290,8 @@ class Import std::vector<Named_type*> builtin_types_; // Mapping from exported type codes to Type structures. std::vector<Type*> types_; + // Version of export data we're reading. + Export_data_version version_; }; // Read import data from a string. diff --git a/gcc/go/gofrontend/lex.cc b/gcc/go/gofrontend/lex.cc index 34a0811abe..e834c13aaf 100644 --- a/gcc/go/gofrontend/lex.cc +++ b/gcc/go/gofrontend/lex.cc @@ -5,6 +5,7 @@ // license that can be found in the LICENSE file. #include "go-system.h" +#include "go-diagnostics.h" #include "lex.h" @@ -442,8 +443,8 @@ Token::print(FILE* file) const Lex::Lex(const char* input_file_name, FILE* input_file, Linemap* linemap) : input_file_name_(input_file_name), input_file_(input_file), linemap_(linemap), linebuf_(NULL), linebufsize_(120), linesize_(0), - lineoff_(0), lineno_(0), add_semi_at_eol_(false), saw_nointerface_(false), - extern_() + lineoff_(0), lineno_(0), add_semi_at_eol_(false), pragmas_(0), + extern_(), linknames_(NULL) { this->linebuf_ = new char[this->linebufsize_]; this->linemap_->start_file(input_file_name, 0); @@ -477,7 +478,7 @@ Lex::get_line() { size_t ns = 2 * size + 1; if (ns < size || static_cast<ssize_t>(ns) < 0) - error_at(this->location(), "out of memory"); + go_error_at(this->location(), "out of memory"); char* nb = new char[ns]; memcpy(nb, buf, cur); delete[] buf; @@ -743,9 +744,9 @@ Lex::next_token() return this->gather_identifier(); if (!issued_error) - error_at(this->location(), - "invalid character 0x%x in input file", - ci); + go_error_at(this->location(), + "invalid character 0x%x in input file", + ci); p = pend; @@ -834,7 +835,7 @@ Lex::advance_one_utf8_char(const char* p, unsigned int* value, if (*p == '\0') { - error_at(this->location(), "invalid NUL byte"); + go_error_at(this->location(), "invalid NUL byte"); *issued_error = true; *value = 0; return p + 1; @@ -843,7 +844,7 @@ Lex::advance_one_utf8_char(const char* p, unsigned int* value, int adv = Lex::fetch_char(p, value); if (adv == 0) { - error_at(this->location(), "invalid UTF-8 encoding"); + go_error_at(this->location(), "invalid UTF-8 encoding"); *issued_error = true; return p + 1; } @@ -851,7 +852,7 @@ Lex::advance_one_utf8_char(const char* p, unsigned int* value, // Warn about byte order mark, except at start of file. if (*value == 0xfeff && (this->lineno_ != 1 || this->lineoff_ != 0)) { - error_at(this->location(), "Unicode (UTF-8) BOM in middle of file"); + go_error_at(this->location(), "Unicode (UTF-8) BOM in middle of file"); *issued_error = true; } @@ -881,7 +882,7 @@ Lex::gather_identifier() && (cc < '0' || cc > '9')) { // Check for an invalid character here, as we get better - // error behavior if we swallow them as part of the + // error behaviour if we swallow them as part of the // identifier we are building. if ((cc >= ' ' && cc < 0x7f) || cc == '\t' @@ -890,9 +891,9 @@ Lex::gather_identifier() break; this->lineoff_ = p - this->linebuf_; - error_at(this->location(), - "invalid character 0x%x in identifier", - cc); + go_error_at(this->location(), + "invalid character 0x%x in identifier", + cc); if (!has_non_ascii_char) { buf.assign(pstart, p - pstart); @@ -922,12 +923,12 @@ Lex::gather_identifier() { // There is no valid place for a non-ASCII character // other than an identifier, so we get better error - // handling behavior if we swallow this character after + // handling behaviour if we swallow this character after // giving an error. if (!issued_error) - error_at(this->location(), - "invalid character 0x%x in identifier", - ci); + go_error_at(this->location(), + "invalid character 0x%x in identifier", + ci); is_invalid = true; } if (is_first) @@ -985,6 +986,52 @@ Lex::is_hex_digit(char c) || (c >= 'a' && c <= 'f')); } +// not a hex value +#define NHV 100 + +// for use by Lex::hex_val +static const unsigned char hex_value_lookup_table[256] = +{ + NHV, NHV, NHV, NHV, NHV, NHV, NHV, NHV, // NUL SOH STX ETX EOT ENQ ACK BEL + NHV, NHV, NHV, NHV, NHV, NHV, NHV, NHV, // BS HT LF VT FF CR SO SI + NHV, NHV, NHV, NHV, NHV, NHV, NHV, NHV, // DLE DC1 DC2 DC3 DC4 NAK SYN ETB + NHV, NHV, NHV, NHV, NHV, NHV, NHV, NHV, // CAN EM SUB ESC FS GS RS US + NHV, NHV, NHV, NHV, NHV, NHV, NHV, NHV, // SP ! " # $ % & ' + NHV, NHV, NHV, NHV, NHV, NHV, NHV, NHV, // ( ) * + , - . / + 0, 1, 2, 3, 4, 5, 6, 7, // 0 1 2 3 4 5 6 7 + 8, 9, NHV, NHV, NHV, NHV, NHV, NHV, // 8 9 : ; < = > ? + NHV, 10, 11, 12, 13, 14, 15, NHV, // @ A B C D E F G + NHV, NHV, NHV, NHV, NHV, NHV, NHV, NHV, // H I J K L M N O + NHV, NHV, NHV, NHV, NHV, NHV, NHV, NHV, // P Q R S T U V W + NHV, NHV, NHV, NHV, NHV, NHV, NHV, NHV, // X Y Z [ \ ] ^ _ + NHV, 10, 11, 12, 13, 14, 15, NHV, // ` a b c d e f g + NHV, NHV, NHV, NHV, NHV, NHV, NHV, NHV, // h i j k l m n o + NHV, NHV, NHV, NHV, NHV, NHV, NHV, NHV, // p q r s t u v w + NHV, NHV, NHV, NHV, NHV, NHV, NHV, NHV, // x y z { | } ~ + NHV, NHV, NHV, NHV, NHV, NHV, NHV, NHV, // + NHV, NHV, NHV, NHV, NHV, NHV, NHV, NHV, // + NHV, NHV, NHV, NHV, NHV, NHV, NHV, NHV, // + NHV, NHV, NHV, NHV, NHV, NHV, NHV, NHV, // + NHV, NHV, NHV, NHV, NHV, NHV, NHV, NHV, // + NHV, NHV, NHV, NHV, NHV, NHV, NHV, NHV, // + NHV, NHV, NHV, NHV, NHV, NHV, NHV, NHV, // + NHV, NHV, NHV, NHV, NHV, NHV, NHV, NHV, // + NHV, NHV, NHV, NHV, NHV, NHV, NHV, NHV, // + NHV, NHV, NHV, NHV, NHV, NHV, NHV, NHV, // + NHV, NHV, NHV, NHV, NHV, NHV, NHV, NHV, // + NHV, NHV, NHV, NHV, NHV, NHV, NHV, NHV, // + NHV, NHV, NHV, NHV, NHV, NHV, NHV, NHV, // + NHV, NHV, NHV, NHV, NHV, NHV, NHV, NHV, // + NHV, NHV, NHV, NHV, NHV, NHV, NHV, NHV, // + NHV, NHV, NHV, NHV, NHV, NHV, NHV, NHV // +}; + +unsigned +Lex::hex_val(char c) +{ + return hex_value_lookup_table[static_cast<unsigned char>(c)]; +} + // Return whether an exponent could start at P. bool @@ -1063,9 +1110,9 @@ Lex::gather_number() if (r != 0) { if (base == 8) - error_at(this->location(), "invalid octal literal"); + go_error_at(this->location(), "invalid octal literal"); else - error_at(this->location(), "invalid hex literal"); + go_error_at(this->location(), "invalid hex literal"); } if (neg) @@ -1179,7 +1226,7 @@ Lex::advance_one_char(const char* p, bool is_single_quote, unsigned int* value, if (is_single_quote && (*value == '\'' || *value == '\n') && !issued_error) - error_at(this->location(), "invalid character literal"); + go_error_at(this->location(), "invalid character literal"); return ret; } else @@ -1198,12 +1245,12 @@ Lex::advance_one_char(const char* p, bool is_single_quote, unsigned int* value, + Lex::octal_value(p[2])); if (*value > 255) { - error_at(this->location(), "invalid octal constant"); + go_error_at(this->location(), "invalid octal constant"); *value = 255; } return p + 3; } - error_at(this->location(), "invalid octal character"); + go_error_at(this->location(), "invalid octal character"); return (p[1] >= '0' && p[1] <= '7' ? p + 2 : p + 1); @@ -1212,10 +1259,10 @@ Lex::advance_one_char(const char* p, bool is_single_quote, unsigned int* value, *is_character = false; if (Lex::is_hex_digit(p[1]) && Lex::is_hex_digit(p[2])) { - *value = (hex_value(p[1]) << 4) + hex_value(p[2]); + *value = (Lex::hex_val(p[1]) << 4) + Lex::hex_val(p[2]); return p + 3; } - error_at(this->location(), "invalid hex character"); + go_error_at(this->location(), "invalid hex character"); return (Lex::is_hex_digit(p[1]) ? p + 2 : p + 1); @@ -1246,12 +1293,12 @@ Lex::advance_one_char(const char* p, bool is_single_quote, unsigned int* value, return p + 1; case '\'': if (!is_single_quote) - error_at(this->location(), "invalid quoted character"); + go_error_at(this->location(), "invalid quoted character"); *value = '\''; return p + 1; case '"': if (is_single_quote) - error_at(this->location(), "invalid quoted character"); + go_error_at(this->location(), "invalid quoted character"); *value = '"'; return p + 1; @@ -1259,21 +1306,21 @@ Lex::advance_one_char(const char* p, bool is_single_quote, unsigned int* value, if (Lex::is_hex_digit(p[1]) && Lex::is_hex_digit(p[2]) && Lex::is_hex_digit(p[3]) && Lex::is_hex_digit(p[4])) { - *value = ((hex_value(p[1]) << 12) - + (hex_value(p[2]) << 8) - + (hex_value(p[3]) << 4) - + hex_value(p[4])); + *value = ((Lex::hex_val(p[1]) << 12) + + (Lex::hex_val(p[2]) << 8) + + (Lex::hex_val(p[3]) << 4) + + Lex::hex_val(p[4])); if (*value >= 0xd800 && *value < 0xe000) { - error_at(this->location(), - "invalid unicode code point 0x%x", - *value); + go_error_at(this->location(), + "invalid unicode code point 0x%x", + *value); // Use the replacement character. *value = 0xfffd; } return p + 5; } - error_at(this->location(), "invalid little unicode code point"); + go_error_at(this->location(), "invalid little unicode code point"); return p + 1; case 'U': @@ -1282,29 +1329,30 @@ Lex::advance_one_char(const char* p, bool is_single_quote, unsigned int* value, && Lex::is_hex_digit(p[5]) && Lex::is_hex_digit(p[6]) && Lex::is_hex_digit(p[7]) && Lex::is_hex_digit(p[8])) { - *value = ((hex_value(p[1]) << 28) - + (hex_value(p[2]) << 24) - + (hex_value(p[3]) << 20) - + (hex_value(p[4]) << 16) - + (hex_value(p[5]) << 12) - + (hex_value(p[6]) << 8) - + (hex_value(p[7]) << 4) - + hex_value(p[8])); + *value = ((Lex::hex_val(p[1]) << 28) + + (Lex::hex_val(p[2]) << 24) + + (Lex::hex_val(p[3]) << 20) + + (Lex::hex_val(p[4]) << 16) + + (Lex::hex_val(p[5]) << 12) + + (Lex::hex_val(p[6]) << 8) + + (Lex::hex_val(p[7]) << 4) + + Lex::hex_val(p[8])); if (*value > 0x10ffff || (*value >= 0xd800 && *value < 0xe000)) { - error_at(this->location(), "invalid unicode code point 0x%x", - *value); + go_error_at(this->location(), + "invalid unicode code point 0x%x", + *value); // Use the replacement character. *value = 0xfffd; } return p + 9; } - error_at(this->location(), "invalid big unicode code point"); + go_error_at(this->location(), "invalid big unicode code point"); return p + 1; default: - error_at(this->location(), "invalid character after %<\\%>"); + go_error_at(this->location(), "invalid character after %<\\%>"); *value = *p; return p + 1; } @@ -1336,15 +1384,15 @@ Lex::append_char(unsigned int v, bool is_character, std::string* str, { if (v > 0x10ffff) { - warning_at(location, 0, - "unicode code point 0x%x out of range in string", v); + go_warning_at(location, 0, + "unicode code point 0x%x out of range in string", v); // Turn it into the "replacement character". v = 0xfffd; } if (v >= 0xd800 && v < 0xe000) { - warning_at(location, 0, - "unicode code point 0x%x is invalid surrogate pair", v); + go_warning_at(location, 0, + "unicode code point 0x%x is invalid surrogate pair", v); v = 0xfffd; } if (v <= 0xffff) @@ -1381,7 +1429,7 @@ Lex::gather_character() if (*p != '\'') { - error_at(this->location(), "unterminated character constant"); + go_error_at(this->location(), "unterminated character constant"); this->lineoff_ = p - this->linebuf_; return this->make_invalid_token(); } @@ -1415,7 +1463,7 @@ Lex::gather_string() p = this->advance_one_char(p, false, &c, &is_character); if (p >= pend) { - error_at(this->location(), "unterminated string"); + go_error_at(this->location(), "unterminated string"); --p; break; } @@ -1459,7 +1507,7 @@ Lex::gather_raw_string() this->lineoff_ = p - this->linebuf_; if (!this->require_line()) { - error_at(location, "unterminated raw string"); + go_error_at(location, "unterminated raw string"); return Token::make_string_token(value, location); } p = this->linebuf_ + this->lineoff_; @@ -1639,7 +1687,7 @@ Lex::skip_c_comment(bool* found_newline) { if (!this->require_line()) { - error_at(this->location(), "unterminated comment"); + go_error_at(this->location(), "unterminated comment"); return false; } @@ -1676,29 +1724,48 @@ Lex::skip_cpp_comment() // //extern comment. this->extern_.clear(); - const char* p = this->linebuf_ + this->lineoff_; + Location loc = this->location(); + size_t lineoff = this->lineoff_; + + const char* p = this->linebuf_ + lineoff; const char* pend = this->linebuf_ + this->linesize_; - // By convention, a C++ comment at the start of the line of the form + const char* pcheck = p; + bool saw_error = false; + while (pcheck < pend) + { + this->lineoff_ = pcheck - this->linebuf_; + unsigned int c; + bool issued_error; + pcheck = this->advance_one_utf8_char(pcheck, &c, &issued_error); + if (issued_error) + saw_error = true; + } + + if (saw_error) + return; + + // Recognize various magic comments at the start of a line. + + if (lineoff != 2) + { + // Not at the start of the line. (lineoff == 2 because of the + // two characters in "//"). + return; + } + + while (pend > p + && (pend[-1] == ' ' || pend[-1] == '\t' + || pend[-1] == '\r' || pend[-1] == '\n')) + --pend; + + // A C++ comment at the start of the line of the form // //line FILE:LINENO // is interpreted as setting the file name and line number of the // next source line. - - if (this->lineoff_ == 2 - && pend - p > 5 - && memcmp(p, "line ", 5) == 0) + if (pend - p > 5 && memcmp(p, "line ", 5) == 0) { p += 5; - - // Before finding FILE:LINENO, make sure line has valid characters. - const char* pcheck = p; - while (pcheck < pend) - { - unsigned int c; - bool issued_error; - pcheck = this->advance_one_utf8_char(pcheck, &c, &issued_error); - } - while (p < pend && *p == ' ') ++p; const char* pcolon = static_cast<const char*>(memchr(p, ':', pend - p)); @@ -1726,6 +1793,7 @@ Lex::skip_cpp_comment() p = plend; } } + return; } // As a special gccgo extension, a C++ comment at the start of the @@ -1734,37 +1802,138 @@ Lex::skip_cpp_comment() // which immediately precedes a function declaration means that the // external name of the function declaration is NAME. This is // normally used to permit Go code to call a C function. - if (this->lineoff_ == 2 - && pend - p > 7 - && memcmp(p, "extern ", 7) == 0) + if (pend - p > 7 && memcmp(p, "extern ", 7) == 0) { p += 7; while (p < pend && (*p == ' ' || *p == '\t')) ++p; - const char* plend = pend; - while (plend > p - && (plend[-1] == ' ' || plend[-1] == '\t' || plend[-1] == '\n')) - --plend; - if (plend > p) - this->extern_ = std::string(p, plend - p); + if (pend > p) + this->extern_ = std::string(p, pend - p); + return; } - // For field tracking analysis: a //go:nointerface comment means - // that the next interface method should not be stored in the type - // descriptor. This permits it to be discarded if it is not needed. - if (this->lineoff_ == 2 - && pend - p > 14 - && memcmp(p, "go:nointerface", 14) == 0) - this->saw_nointerface_ = true; + // All other special comments start with "go:". - while (p < pend) + if (pend - p < 4 || memcmp(p, "go:", 3) != 0) + return; + + const char *ps = p + 3; + while (ps < pend && *ps != ' ' && *ps != '\t') + ++ps; + std::string verb = std::string(p, ps - p); + + if (verb == "go:linkname") { - this->lineoff_ = p - this->linebuf_; - unsigned int c; - bool issued_error; - p = this->advance_one_utf8_char(p, &c, &issued_error); - if (issued_error) - this->extern_.clear(); + // As in the gc compiler, set the external link name for a Go symbol. + std::string go_name; + std::string ext_name; + bool is_exported = false; + if (ps < pend) + { + while (ps < pend && (*ps == ' ' || *ps == '\t')) + ++ps; + if (ps < pend) + { + const char* pg = ps; + + unsigned int c; + bool issued_error; + ps = this->advance_one_utf8_char(ps, &c, &issued_error); + is_exported = Lex::is_unicode_uppercase(c); + + while (ps < pend && *ps != ' ' && *ps != '\t') + ++ps; + if (ps < pend) + go_name = std::string(pg, ps - pg); + while (ps < pend && (*ps == ' ' || *ps == '\t')) + ++ps; + } + if (ps < pend) + { + const char* pc = ps; + while (ps < pend && *ps != ' ' && *ps != '\t') + ++ps; + if (ps <= pend) + ext_name = std::string(pc, ps - pc); + } + if (ps != pend) + { + go_name.clear(); + ext_name.clear(); + } + } + if (go_name.empty() || ext_name.empty()) + go_error_at(loc, "usage: //go:linkname localname linkname"); + else + { + if (this->linknames_ == NULL) + this->linknames_ = new Linknames(); + (*this->linknames_)[go_name] = Linkname(ext_name, is_exported, loc); + } + } + else if (verb == "go:nointerface") + { + // For field tracking analysis: a //go:nointerface comment means + // that the next interface method should not be stored in the + // type descriptor. This permits it to be discarded if it is + // not needed. + this->pragmas_ |= GOPRAGMA_NOINTERFACE; + } + else if (verb == "go:noescape") + { + // Applies to the next function declaration. Any arguments do + // not escape. + // FIXME: Not implemented. + this->pragmas_ |= GOPRAGMA_NOESCAPE; + } + else if (verb == "go:nosplit") + { + // Applies to the next function. Do not split the stack when + // entering the function. + this->pragmas_ |= GOPRAGMA_NOSPLIT; + } + else if (verb == "go:noinline") + { + // Applies to the next function. Do not inline the function. + this->pragmas_ |= GOPRAGMA_NOINLINE; + } + else if (verb == "go:systemstack") + { + // Applies to the next function. It must run on the system stack. + // FIXME: Should only work when compiling the runtime package. + // FIXME: Not implemented. + this->pragmas_ |= GOPRAGMA_SYSTEMSTACK; + } + else if (verb == "go:nowritebarrier") + { + // Applies to the next function. If the function needs to use + // any write barriers, it should emit an error instead. + // FIXME: Should only work when compiling the runtime package. + // FIXME: Not implemented. + this->pragmas_ |= GOPRAGMA_NOWRITEBARRIER; + } + else if (verb == "go:nowritebarrierrec") + { + // Applies to the next function. If the function, or any + // function that it calls, needs to use any write barriers, it + // should emit an error instead. + // FIXME: Should only work when compiling the runtime package. + // FIXME: Not implemented. + this->pragmas_ |= GOPRAGMA_NOWRITEBARRIERREC; + } + else if (verb == "go:cgo_unsafe_args") + { + // Applies to the next function. Taking the address of any + // argument implies taking the address of all arguments. + // FIXME: Not implemented. + this->pragmas_ |= GOPRAGMA_CGOUNSAFEARGS; + } + else if (verb == "go:uintptrescapes") + { + // Applies to the next function. If an argument is a pointer + // converted to uintptr, then the pointer escapes. + // FIXME: Not implemented. + this->pragmas_ |= GOPRAGMA_UINTPTRESCAPES; } } @@ -2600,10 +2769,10 @@ Lex::is_exported_name(const std::string& name) for (size_t i = 2; i < len && p[i] != '$'; ++i) { c = p[i]; - if (!hex_p(c)) + if (!Lex::is_hex_digit(c)) return false; ci <<= 4; - ci |= hex_value(c); + ci |= Lex::hex_val(c); } return Lex::is_unicode_uppercase(ci); } diff --git a/gcc/go/gofrontend/lex.h b/gcc/go/gofrontend/lex.h index 3e0152aaf2..0a7a842ba8 100644 --- a/gcc/go/gofrontend/lex.h +++ b/gcc/go/gofrontend/lex.h @@ -49,6 +49,24 @@ enum Keyword KEYWORD_VAR }; +// Pragmas built from magic comments and recorded for functions. +// These are used as bits in a bitmask. +// The set of values is intended to be the same as the gc compiler. + +enum GoPragma +{ + GOPRAGMA_NOINTERFACE = 1 << 0, // Method not in type descriptor. + GOPRAGMA_NOESCAPE = 1 << 1, // Args do not escape. + GOPRAGMA_NORACE = 1 << 2, // No race detector. + GOPRAGMA_NOSPLIT = 1 << 3, // Do not split stack. + GOPRAGMA_NOINLINE = 1 << 4, // Do not inline. + GOPRAGMA_SYSTEMSTACK = 1 << 5, // Must run on system stack. + GOPRAGMA_NOWRITEBARRIER = 1 << 6, // No write barriers. + GOPRAGMA_NOWRITEBARRIERREC = 1 << 7, // No write barriers here or callees. + GOPRAGMA_CGOUNSAFEARGS = 1 << 8, // Pointer to arg is pointer to all. + GOPRAGMA_UINTPTRESCAPES = 1 << 9 // uintptr(p) escapes. +}; + // A token returned from the lexer. class Token @@ -348,13 +366,39 @@ class Lex extern_name() const { return this->extern_; } - // Return whether we have seen a //go:nointerface comment, clearing - // the flag. - bool - get_and_clear_nointerface() + // Return the current set of pragmas, and clear them. + unsigned int + get_and_clear_pragmas() + { + unsigned int ret = this->pragmas_; + this->pragmas_ = 0; + return ret; + } + + struct Linkname + { + std::string ext_name; // External name. + bool is_exported; // Whether the internal name is exported. + Location loc; // Location of go:linkname directive. + + Linkname() + : ext_name(), is_exported(false), loc() + { } + + Linkname(const std::string& ext_name_a, bool is_exported_a, Location loc_a) + : ext_name(ext_name_a), is_exported(is_exported_a), loc(loc_a) + { } + }; + + typedef std::map<std::string, Linkname> Linknames; + + // Return the linknames seen so far, or NULL if none, and clear the + // set. These are from go:linkname compiler directives. + Linknames* + get_and_clear_linknames() { - bool ret = this->saw_nointerface_; - this->saw_nointerface_ = false; + Linknames* ret = this->linknames_; + this->linknames_ = NULL; return ret; } @@ -410,6 +454,9 @@ class Lex octal_value(char c) { return c - '0'; } + static unsigned + hex_val(char c); + Token make_invalid_token() { return Token::make_invalid_token(this->location()); } @@ -492,11 +539,13 @@ class Lex size_t lineno_; // Whether to add a semicolon if we see a newline now. bool add_semi_at_eol_; - // Whether we just saw a magic go:nointerface comment. - bool saw_nointerface_; + // Pragmas for the next function, from magic comments. + unsigned int pragmas_; // The external name to use for a function declaration, from a magic // //extern comment. std::string extern_; + // The list of //go:linkname comments, if any. + Linknames* linknames_; }; #endif // !defined(GO_LEX_H) diff --git a/gcc/go/gofrontend/operator.h b/gcc/go/gofrontend/operator.h index f3e0fd0743..e0a97d05f3 100644 --- a/gcc/go/gofrontend/operator.h +++ b/gcc/go/gofrontend/operator.h @@ -63,4 +63,10 @@ enum Operator OPERATOR_RSQUARE // ] }; +// Whether a variable expression appears in lvalue (assignment) context. +enum Varexpr_context { + VE_rvalue, + VE_lvalue +}; + #endif // !defined(GO_OPERATOR_H) diff --git a/gcc/go/gofrontend/parse.cc b/gcc/go/gofrontend/parse.cc index 827eb0a120..84840fb79c 100644 --- a/gcc/go/gofrontend/parse.cc +++ b/gcc/go/gofrontend/parse.cc @@ -8,6 +8,7 @@ #include "lex.h" #include "gogo.h" +#include "go-diagnostics.h" #include "types.h" #include "statements.h" #include "expressions.h" @@ -109,7 +110,7 @@ Parse::identifier_list(Typed_identifier_list* til) { if (!token->is_identifier()) { - error_at(this->location(), "expected identifier"); + go_error_at(this->location(), "expected identifier"); return; } std::string name = @@ -172,7 +173,7 @@ Parse::qualified_ident(std::string* pname, Named_object** ppackage) const Token* token = this->peek_token(); if (!token->is_identifier()) { - error_at(this->location(), "expected identifier"); + go_error_at(this->location(), "expected identifier"); return false; } @@ -191,7 +192,7 @@ Parse::qualified_ident(std::string* pname, Named_object** ppackage) Named_object* package = this->gogo_->lookup(name, NULL); if (package == NULL || !package->is_package()) { - error_at(this->location(), "expected package"); + go_error_at(this->location(), "expected package"); // We expect . IDENTIFIER; skip both. if (this->advance_token()->is_identifier()) this->advance_token(); @@ -203,7 +204,7 @@ Parse::qualified_ident(std::string* pname, Named_object** ppackage) token = this->advance_token(); if (!token->is_identifier()) { - error_at(this->location(), "expected identifier"); + go_error_at(this->location(), "expected identifier"); return false; } @@ -211,7 +212,7 @@ Parse::qualified_ident(std::string* pname, Named_object** ppackage) if (name == "_") { - error_at(this->location(), "invalid use of %<_%>"); + go_error_at(this->location(), "invalid use of %<_%>"); name = Gogo::erroneous_name(); } @@ -269,13 +270,13 @@ Parse::type() else { if (!ret->is_error_type()) - error_at(this->location(), "expected %<)%>"); + go_error_at(this->location(), "expected %<)%>"); } return ret; } else { - error_at(token->location(), "expected type"); + go_error_at(token->location(), "expected type"); return Type::make_error_type(); } } @@ -329,9 +330,10 @@ Parse::type_name(bool issue_error) { Package* p = package->package_value(); const std::string& packname(p->package_name()); - error_at(location, "invalid reference to hidden type %<%s.%s%>", - Gogo::message_name(packname).c_str(), - Gogo::message_name(name).c_str()); + go_error_at(location, + "invalid reference to hidden type %<%s.%s%>", + Gogo::message_name(packname).c_str(), + Gogo::message_name(name).c_str()); issue_error = false; } } @@ -345,9 +347,9 @@ Parse::type_name(bool issue_error) else { const std::string& packname(package->package_value()->package_name()); - error_at(location, "reference to undefined identifier %<%s.%s%>", - Gogo::message_name(packname).c_str(), - Gogo::message_name(name).c_str()); + go_error_at(location, "reference to undefined identifier %<%s.%s%>", + Gogo::message_name(packname).c_str(), + Gogo::message_name(name).c_str()); issue_error = false; ok = false; } @@ -365,7 +367,7 @@ Parse::type_name(bool issue_error) if (!ok) { if (issue_error) - error_at(location, "expected type"); + go_error_at(location, "expected type"); return Type::make_error_type(); } @@ -405,14 +407,14 @@ Parse::array_type(bool may_use_ellipsis) } else { - error_at(this->location(), - "use of %<[...]%> outside of array literal"); + go_error_at(this->location(), + "use of %<[...]%> outside of array literal"); length = Expression::make_error(this->location()); this->advance_token(); } if (!this->peek_token()->is_op(OPERATOR_RSQUARE)) { - error_at(this->location(), "expected %<]%>"); + go_error_at(this->location(), "expected %<]%>"); return Type::make_error_type(); } this->advance_token(); @@ -436,7 +438,7 @@ Parse::map_type() go_assert(this->peek_token()->is_keyword(KEYWORD_MAP)); if (!this->advance_token()->is_op(OPERATOR_LSQUARE)) { - error_at(this->location(), "expected %<[%>"); + go_error_at(this->location(), "expected %<[%>"); return Type::make_error_type(); } this->advance_token(); @@ -445,7 +447,7 @@ Parse::map_type() if (!this->peek_token()->is_op(OPERATOR_RSQUARE)) { - error_at(this->location(), "expected %<]%>"); + go_error_at(this->location(), "expected %<]%>"); return Type::make_error_type(); } this->advance_token(); @@ -470,10 +472,10 @@ Parse::struct_type() Location token_loc = this->location(); if (this->peek_token()->is_op(OPERATOR_SEMICOLON) && this->advance_token()->is_op(OPERATOR_LCURLY)) - error_at(token_loc, "unexpected semicolon or newline before %<{%>"); + go_error_at(token_loc, "unexpected semicolon or newline before %<{%>"); else { - error_at(this->location(), "expected %<{%>"); + go_error_at(this->location(), "expected %<{%>"); return Type::make_error_type(); } } @@ -487,7 +489,7 @@ Parse::struct_type() this->advance_token(); else if (!this->peek_token()->is_op(OPERATOR_RCURLY)) { - error_at(this->location(), "expected %<;%> or %<}%> or newline"); + go_error_at(this->location(), "expected %<;%> or %<}%> or newline"); if (!this->skip_past_error(OPERATOR_RCURLY)) return Type::make_error_type(); } @@ -506,8 +508,8 @@ Parse::struct_type() { if (pi->field_name() == pj->field_name() && !Gogo::is_sink_name(pi->field_name())) - error_at(pi->location(), "duplicate field name %<%s%>", - Gogo::message_name(pi->field_name()).c_str()); + go_error_at(pi->location(), "duplicate field name %<%s%>", + Gogo::message_name(pi->field_name()).c_str()); } } @@ -545,7 +547,7 @@ Parse::field_decl(Struct_field_list* sfl) } else { - error_at(this->location(), "expected field name"); + go_error_at(this->location(), "expected field name"); this->gogo_->mark_locals_used(); while (!token->is_op(OPERATOR_SEMICOLON) && !token->is_op(OPERATOR_RCURLY) @@ -561,7 +563,7 @@ Parse::field_decl(Struct_field_list* sfl) this->advance_token(); if (!this->peek_token()->is_identifier()) { - error_at(this->location(), "expected field name"); + go_error_at(this->location(), "expected field name"); this->gogo_->mark_locals_used(); while (!token->is_op(OPERATOR_SEMICOLON) && !token->is_op(OPERATOR_RCURLY) @@ -596,7 +598,7 @@ Parse::field_decl(Struct_field_list* sfl) token = this->peek_token(); if (!token->is_identifier()) { - error_at(this->location(), "expected identifier"); + go_error_at(this->location(), "expected identifier"); return; } std::string name = @@ -657,7 +659,7 @@ Parse::channel_type() { if (!this->advance_token()->is_keyword(KEYWORD_CHAN)) { - error_at(this->location(), "expected %<chan%>"); + go_error_at(this->location(), "expected %<chan%>"); return Type::make_error_type(); } send = false; @@ -679,13 +681,13 @@ Parse::channel_type() { token = this->peek_token(); if (token->is_op(OPERATOR_RCURLY)) - error_at(this->location(), "unexpected %<}%> in channel type"); + go_error_at(this->location(), "unexpected %<}%> in channel type"); else if (token->is_op(OPERATOR_RPAREN)) - error_at(this->location(), "unexpected %<)%> in channel type"); + go_error_at(this->location(), "unexpected %<)%> in channel type"); else if (token->is_op(OPERATOR_COMMA)) - error_at(this->location(), "unexpected comma in channel type"); + go_error_at(this->location(), "unexpected comma in channel type"); else - error_at(this->location(), "expected channel element type"); + go_error_at(this->location(), "expected channel element type"); return Type::make_error_type(); } @@ -710,11 +712,11 @@ Parse::check_signature_names(const Typed_identifier_list* params, std::pair<Parse::Names::iterator, bool> ins = names->insert(val); if (!ins.second) { - error_at(p->location(), "redefinition of %qs", - Gogo::message_name(p->name()).c_str()); - inform(ins.first->second->location(), - "previous definition of %qs was here", - Gogo::message_name(p->name()).c_str()); + go_error_at(p->location(), "redefinition of %qs", + Gogo::message_name(p->name()).c_str()); + go_inform(ins.first->second->location(), + "previous definition of %qs was here", + Gogo::message_name(p->name()).c_str()); } } } @@ -770,7 +772,7 @@ Parse::parameters(Typed_identifier_list** pparams, bool* is_varargs) if (!this->peek_token()->is_op(OPERATOR_LPAREN)) { - error_at(this->location(), "expected %<(%>"); + go_error_at(this->location(), "expected %<(%>"); return false; } @@ -790,7 +792,7 @@ Parse::parameters(Typed_identifier_list** pparams, bool* is_varargs) if (!token->is_op(OPERATOR_RPAREN)) { - error_at(this->location(), "expected %<)%>"); + go_error_at(this->location(), "expected %<)%>"); return false; } this->advance_token(); @@ -929,7 +931,8 @@ Parse::parameter_list(bool* is_varargs) type = this->type(); else { - error_at(this->location(), "%<...%> only permits one name"); + go_error_at(this->location(), + "%<...%> only permits one name"); saw_error = true; this->advance_token(); type = this->type(); @@ -960,8 +963,8 @@ Parse::parameter_list(bool* is_varargs) type = Type::make_forward_declaration(no); else { - error_at(p->location(), "expected %<%s%> to be a type", - Gogo::message_name(p->name()).c_str()); + go_error_at(p->location(), "expected %<%s%> to be a type", + Gogo::message_name(p->name()).c_str()); saw_error = true; type = Type::make_error_type(); } @@ -985,7 +988,7 @@ Parse::parameter_list(bool* is_varargs) break; if (is_varargs != NULL && *is_varargs) { - error_at(this->location(), "%<...%> must be last parameter"); + go_error_at(this->location(), "%<...%> must be last parameter"); saw_error = true; } this->parameter_decl(parameters_have_names, ret, is_varargs, &mix_error, @@ -993,7 +996,7 @@ Parse::parameter_list(bool* is_varargs) } if (mix_error) { - error_at(location, "invalid named/anonymous mix"); + go_error_at(location, "invalid named/anonymous mix"); saw_error = true; } if (saw_error) @@ -1024,7 +1027,7 @@ Parse::parameter_decl(bool parameters_have_names, else { if (is_varargs == NULL) - error_at(this->location(), "invalid use of %<...%>"); + go_error_at(this->location(), "invalid use of %<...%>"); else *is_varargs = true; this->advance_token(); @@ -1073,12 +1076,12 @@ Parse::parameter_decl(bool parameters_have_names, { if (is_varargs == NULL) { - error_at(this->location(), "invalid use of %<...%>"); + go_error_at(this->location(), "invalid use of %<...%>"); *saw_error = true; } else if (new_count > orig_count + 1) { - error_at(this->location(), "%<...%> only permits one name"); + go_error_at(this->location(), "%<...%> only permits one name"); *saw_error = true; } else @@ -1129,10 +1132,10 @@ Parse::block() Location loc = this->location(); if (this->peek_token()->is_op(OPERATOR_SEMICOLON) && this->advance_token()->is_op(OPERATOR_LCURLY)) - error_at(loc, "unexpected semicolon or newline before %<{%>"); + go_error_at(loc, "unexpected semicolon or newline before %<{%>"); else { - error_at(this->location(), "expected %<{%>"); + go_error_at(this->location(), "expected %<{%>"); return Linemap::unknown_location(); } } @@ -1146,7 +1149,7 @@ Parse::block() if (!token->is_op(OPERATOR_RCURLY)) { if (!token->is_eof() || !saw_errors()) - error_at(this->location(), "expected %<}%>"); + go_error_at(this->location(), "expected %<}%>"); this->gogo_->mark_locals_used(); @@ -1193,10 +1196,10 @@ Parse::interface_type(bool record) Location token_loc = this->location(); if (this->peek_token()->is_op(OPERATOR_SEMICOLON) && this->advance_token()->is_op(OPERATOR_LCURLY)) - error_at(token_loc, "unexpected semicolon or newline before %<{%>"); + go_error_at(token_loc, "unexpected semicolon or newline before %<{%>"); else { - error_at(this->location(), "expected %<{%>"); + go_error_at(this->location(), "expected %<{%>"); return Type::make_error_type(); } } @@ -1214,7 +1217,7 @@ Parse::interface_type(bool record) } if (!this->peek_token()->is_op(OPERATOR_RCURLY)) { - error_at(this->location(), "expected %<}%>"); + go_error_at(this->location(), "expected %<}%>"); while (!this->advance_token()->is_op(OPERATOR_RCURLY)) { if (this->peek_token()->is_eof()) @@ -1250,7 +1253,7 @@ Parse::method_spec(Typed_identifier_list* methods) const Token* token = this->peek_token(); if (!token->is_identifier()) { - error_at(this->location(), "expected identifier"); + go_error_at(this->location(), "expected identifier"); return; } @@ -1262,7 +1265,8 @@ Parse::method_spec(Typed_identifier_list* methods) { // This is a MethodName. if (name == "_") - error_at(this->location(), "methods must have a unique non-blank name"); + go_error_at(this->location(), + "methods must have a unique non-blank name"); name = this->gogo_->pack_hidden_name(name, is_exported); Type* type = this->signature(NULL, location); if (type == NULL) @@ -1279,10 +1283,10 @@ Parse::method_spec(Typed_identifier_list* methods) && !this->peek_token()->is_op(OPERATOR_RCURLY))) { if (this->peek_token()->is_op(OPERATOR_COMMA)) - error_at(this->location(), - "name list not allowed in interface type"); + go_error_at(this->location(), + "name list not allowed in interface type"); else - error_at(location, "expected signature or type name"); + go_error_at(location, "expected signature or type name"); this->gogo_->mark_locals_used(); token = this->peek_token(); while (!token->is_eof() @@ -1305,10 +1309,10 @@ Parse::declaration() { const Token* token = this->peek_token(); - bool saw_nointerface = this->lex_->get_and_clear_nointerface(); - if (saw_nointerface && !token->is_keyword(KEYWORD_FUNC)) - warning_at(token->location(), 0, - "ignoring magic //go:nointerface comment before non-method"); + unsigned int pragmas = this->lex_->get_and_clear_pragmas(); + if (pragmas != 0 && !token->is_keyword(KEYWORD_FUNC)) + go_warning_at(token->location(), 0, + "ignoring magic comment before non-function"); if (token->is_keyword(KEYWORD_CONST)) this->const_decl(); @@ -1317,10 +1321,10 @@ Parse::declaration() else if (token->is_keyword(KEYWORD_VAR)) this->var_decl(); else if (token->is_keyword(KEYWORD_FUNC)) - this->function_decl(saw_nointerface); + this->function_decl(pragmas); else { - error_at(this->location(), "expected declaration"); + go_error_at(this->location(), "expected declaration"); this->advance_token(); } } @@ -1343,7 +1347,7 @@ Parse::decl(void (Parse::*pfn)(void*), void* varg) if (this->peek_token()->is_eof()) { if (!saw_errors()) - error_at(this->location(), "unexpected end of file"); + go_error_at(this->location(), "unexpected end of file"); return; } @@ -1356,7 +1360,7 @@ Parse::decl(void (Parse::*pfn)(void*), void* varg) this->list(pfn, varg, true); if (!this->peek_token()->is_op(OPERATOR_RPAREN)) { - error_at(this->location(), "missing %<)%>"); + go_error_at(this->location(), "missing %<)%>"); while (!this->advance_token()->is_op(OPERATOR_RPAREN)) { if (this->peek_token()->is_eof()) @@ -1382,7 +1386,7 @@ Parse::list(void (Parse::*pfn)(void*), void* varg, bool follow_is_paren) || this->peek_token()->is_op(OPERATOR_COMMA)) { if (this->peek_token()->is_op(OPERATOR_COMMA)) - error_at(this->location(), "unexpected comma"); + go_error_at(this->location(), "unexpected comma"); if (this->advance_token()->is_op(follow)) break; (this->*pfn)(varg); @@ -1413,7 +1417,8 @@ Parse::const_decl() this->advance_token(); else if (!this->peek_token()->is_op(OPERATOR_RPAREN)) { - error_at(this->location(), "expected %<;%> or %<)%> or newline"); + go_error_at(this->location(), + "expected %<;%> or %<)%> or newline"); if (!this->skip_past_error(OPERATOR_RPAREN)) return; } @@ -1446,7 +1451,7 @@ Parse::const_spec(Type** last_type, Expression_list** last_expr_list) { if (*last_expr_list == NULL) { - error_at(this->location(), "expected %<=%>"); + go_error_at(this->location(), "expected %<=%>"); return; } type = *last_type; @@ -1473,7 +1478,7 @@ Parse::const_spec(Type** last_type, Expression_list** last_expr_list) { if (pe == expr_list->end()) { - error_at(this->location(), "not enough initializers"); + go_error_at(this->location(), "not enough initializers"); return; } if (type != NULL) @@ -1493,7 +1498,7 @@ Parse::const_spec(Type** last_type, Expression_list** last_expr_list) } } if (pe != expr_list->end()) - error_at(this->location(), "too many initializers"); + go_error_at(this->location(), "too many initializers"); this->increment_iota(); @@ -1510,7 +1515,7 @@ Parse::type_decl() this->decl(&Parse::type_spec, NULL); } -// TypeSpec = identifier Type . +// TypeSpec = identifier ["="] Type . void Parse::type_spec(void*) @@ -1518,7 +1523,7 @@ Parse::type_spec(void*) const Token* token = this->peek_token(); if (!token->is_identifier()) { - error_at(this->location(), "expected identifier"); + go_error_at(this->location(), "expected identifier"); return; } std::string name = token->identifier(); @@ -1526,6 +1531,13 @@ Parse::type_spec(void*) Location location = token->location(); token = this->advance_token(); + bool is_alias = false; + if (token->is_op(OPERATOR_EQ)) + { + is_alias = true; + token = this->advance_token(); + } + // The scope of the type name starts at the point where the // identifier appears in the source code. We implement this by // declaring the type before we read the type definition. @@ -1537,18 +1549,18 @@ Parse::type_spec(void*) } Type* type; - if (name == "_" && this->peek_token()->is_keyword(KEYWORD_INTERFACE)) + if (name == "_" && token->is_keyword(KEYWORD_INTERFACE)) { // We call Parse::interface_type explicity here because we do not want // to record an interface with a blank type name. type = this->interface_type(false); } - else if (!this->peek_token()->is_op(OPERATOR_SEMICOLON)) + else if (!token->is_op(OPERATOR_SEMICOLON)) type = this->type(); else { - error_at(this->location(), - "unexpected semicolon or newline in type declaration"); + go_error_at(this->location(), + "unexpected semicolon or newline in type declaration"); type = Type::make_error_type(); this->advance_token(); } @@ -1570,13 +1582,15 @@ Parse::type_spec(void*) && (ftype->forward_declaration_type()->named_object() == named_type)) { - error_at(location, "invalid recursive type"); + go_error_at(location, "invalid recursive type"); type = Type::make_error_type(); } - this->gogo_->define_type(named_type, - Type::make_named_type(named_type, type, - location)); + Named_type* nt = Type::make_named_type(named_type, type, location); + if (is_alias) + nt->set_is_alias(); + + this->gogo_->define_type(named_type, nt); go_assert(named_type->package() == NULL); } else @@ -1669,7 +1683,7 @@ Parse::init_vars(const Typed_identifier_list* til, Type* type, if (init != NULL && init->size() != til->size()) { if (init->empty() || !init->front()->is_error_expression()) - error_at(location, "wrong number of initializations"); + go_error_at(location, "wrong number of initializations"); init = NULL; if (type == NULL) type = Type::make_error_type(); @@ -1701,7 +1715,7 @@ Parse::init_vars(const Typed_identifier_list* til, Type* type, if (init != NULL) go_assert(pexpr == init->end()); if (is_coloneq && !any_new) - error_at(location, "variables redeclared but no variable is new"); + go_error_at(location, "variables redeclared but no variable is new"); this->finish_init_vars(vars, vals, location); } @@ -1761,7 +1775,7 @@ Parse::init_vars_from_call(const Typed_identifier_list* vars, Type* type, } if (is_coloneq && !any_new) - error_at(location, "variables redeclared but no variable is new"); + go_error_at(location, "variables redeclared but no variable is new"); this->finish_init_vars(ivars, ivals, location); @@ -1804,7 +1818,7 @@ Parse::init_vars_from_map(const Typed_identifier_list* vars, Type* type, Expression* present_var = Expression::make_var_reference(no, location); if (is_coloneq && !any_new) - error_at(location, "variables redeclared but no variable is new"); + go_error_at(location, "variables redeclared but no variable is new"); Statement* s = Statement::make_tuple_map_assignment(val_var, present_var, index, location); @@ -1869,7 +1883,7 @@ Parse::init_vars_from_receive(const Typed_identifier_list* vars, Type* type, Expression* received_var = Expression::make_var_reference(no, location); if (is_coloneq && !any_new) - error_at(location, "variables redeclared but no variable is new"); + go_error_at(location, "variables redeclared but no variable is new"); Statement* s = Statement::make_tuple_receive_assignment(val_var, received_var, @@ -1940,7 +1954,7 @@ Parse::init_vars_from_type_guard(const Typed_identifier_list* vars, location); if (is_coloneq && !any_new) - error_at(location, "variables redeclared but no variable is new"); + go_error_at(location, "variables redeclared but no variable is new"); if (!this->gogo_->in_global_scope()) this->gogo_->add_statement(s); @@ -2106,6 +2120,8 @@ Parse::simple_var_decl_or_assignment(const std::string& name, std::set<std::string> uniq_idents; uniq_idents.insert(name); + std::string dup_name; + Location dup_loc; // We've seen one identifier. If we see a comma now, this could be // "a, *p = 1, 2". @@ -2131,8 +2147,8 @@ Parse::simple_var_decl_or_assignment(const std::string& name, id = this->gogo_->pack_hidden_name(id, is_id_exported); ins = uniq_idents.insert(id); if (!ins.second && !Gogo::is_sink_name(id)) - error_at(id_location, "multiple assignments to %s", - Gogo::message_name(id).c_str()); + go_error_at(id_location, "multiple assignments to %s", + Gogo::message_name(id).c_str()); til.push_back(Typed_identifier(id, NULL, location)); } else @@ -2145,8 +2161,10 @@ Parse::simple_var_decl_or_assignment(const std::string& name, id = this->gogo_->pack_hidden_name(id, is_id_exported); ins = uniq_idents.insert(id); if (!ins.second && !Gogo::is_sink_name(id)) - error_at(id_location, "multiple assignments to %s", - Gogo::message_name(id).c_str()); + { + dup_name = Gogo::message_name(id); + dup_loc = id_location; + } til.push_back(Typed_identifier(id, NULL, location)); } @@ -2182,6 +2200,9 @@ Parse::simple_var_decl_or_assignment(const std::string& name, go_assert(this->peek_token()->is_op(OPERATOR_COLONEQ)); const Token* token = this->advance_token(); + if (!dup_name.empty()) + go_error_at(dup_loc, "multiple assignments to %s", dup_name.c_str()); + if (p_range_clause != NULL && token->is_keyword(KEYWORD_RANGE)) { this->range_clause_decl(&til, p_range_clause); @@ -2229,13 +2250,12 @@ Parse::simple_var_decl_or_assignment(const std::string& name, // __asm__ "(" string_lit ")" . // This extension means a function whose real name is the identifier // inside the asm. This extension will be removed at some future -// date. It has been replaced with //extern comments. - -// SAW_NOINTERFACE is true if we saw a magic //go:nointerface comment, -// which means that we omit the method from the type descriptor. +// date. It has been replaced with //extern or //go:linkname comments. +// +// PRAGMAS is a bitset of magic comments. void -Parse::function_decl(bool saw_nointerface) +Parse::function_decl(unsigned int pragmas) { go_assert(this->peek_token()->is_keyword(KEYWORD_FUNC)); Location location = this->location(); @@ -2250,16 +2270,10 @@ Parse::function_decl(bool saw_nointerface) rec = this->receiver(); token = this->peek_token(); } - else if (saw_nointerface) - { - warning_at(location, 0, - "ignoring magic //go:nointerface comment before non-method"); - saw_nointerface = false; - } if (!token->is_identifier()) { - error_at(this->location(), "expected function name"); + go_error_at(this->location(), "expected function name"); return; } @@ -2277,19 +2291,19 @@ Parse::function_decl(bool saw_nointerface) { if (!this->advance_token()->is_op(OPERATOR_LPAREN)) { - error_at(this->location(), "expected %<(%>"); + go_error_at(this->location(), "expected %<(%>"); return; } token = this->advance_token(); if (!token->is_string()) { - error_at(this->location(), "expected string"); + go_error_at(this->location(), "expected string"); return; } std::string asm_name = token->string_value(); if (!this->advance_token()->is_op(OPERATOR_RPAREN)) { - error_at(this->location(), "expected %<)%>"); + go_error_at(this->location(), "expected %<)%>"); return; } this->advance_token(); @@ -2306,14 +2320,76 @@ Parse::function_decl(bool saw_nointerface) { Location semi_loc = this->location(); if (this->advance_token()->is_op(OPERATOR_LCURLY)) - error_at(this->location(), - "unexpected semicolon or newline before %<{%>"); + go_error_at(this->location(), + "unexpected semicolon or newline before %<{%>"); else this->unget_token(Token::make_operator_token(OPERATOR_SEMICOLON, semi_loc)); } - if (!this->peek_token()->is_op(OPERATOR_LCURLY)) + static struct { + unsigned int bit; + const char* name; + bool decl_ok; + bool func_ok; + bool method_ok; + } pragma_check[] = + { + { GOPRAGMA_NOINTERFACE, "nointerface", false, false, true }, + { GOPRAGMA_NOESCAPE, "noescape", true, false, false }, + { GOPRAGMA_NORACE, "norace", false, true, true }, + { GOPRAGMA_NOSPLIT, "nosplit", false, true, true }, + { GOPRAGMA_NOINLINE, "noinline", false, true, true }, + { GOPRAGMA_SYSTEMSTACK, "systemstack", false, true, true }, + { GOPRAGMA_NOWRITEBARRIER, "nowritebarrier", false, true, true }, + { GOPRAGMA_NOWRITEBARRIERREC, "nowritebarrierrec", false, true, true }, + { GOPRAGMA_CGOUNSAFEARGS, "cgo_unsafe_args", false, true, true }, + { GOPRAGMA_UINTPTRESCAPES, "uintptrescapes", true, true, true }, + }; + + bool is_decl = !this->peek_token()->is_op(OPERATOR_LCURLY); + if (pragmas != 0) + { + for (size_t i = 0; + i < sizeof(pragma_check) / sizeof(pragma_check[0]); + ++i) + { + if ((pragmas & pragma_check[i].bit) == 0) + continue; + + if (is_decl) + { + if (pragma_check[i].decl_ok) + continue; + go_warning_at(location, 0, + ("ignoring magic //go:%s comment " + "before declaration"), + pragma_check[i].name); + } + else if (rec == NULL) + { + if (pragma_check[i].func_ok) + continue; + go_warning_at(location, 0, + ("ignoring magic //go:%s comment " + "before function definition"), + pragma_check[i].name); + } + else + { + if (pragma_check[i].method_ok) + continue; + go_warning_at(location, 0, + ("ignoring magic //go:%s comment " + "before method definition"), + pragma_check[i].name); + } + + pragmas &= ~ pragma_check[i].bit; + } + } + + if (is_decl) { if (named_object == NULL) { @@ -2346,10 +2422,8 @@ Parse::function_decl(bool saw_nointerface) } } - if (saw_nointerface) - warning_at(location, 0, - ("ignoring magic //go:nointerface comment " - "before declaration")); + if (pragmas != 0 && named_object->is_function_declaration()) + named_object->func_declaration_value()->set_pragmas(pragmas); } else { @@ -2365,10 +2439,11 @@ Parse::function_decl(bool saw_nointerface) named_object = this->gogo_->start_function(name, fntype, true, location); Location end_loc = this->block(); this->gogo_->finish_function(end_loc); - if (saw_nointerface + + if (pragmas != 0 && !this->is_erroneous_function_ && named_object->is_function()) - named_object->func_value()->set_nointerface(); + named_object->func_value()->set_pragmas(pragmas); this->is_erroneous_function_ = hold_is_erroneous_function; } } @@ -2384,12 +2459,12 @@ Parse::receiver() return NULL; else if (til == NULL || til->empty()) { - error_at(location, "method has no receiver"); + go_error_at(location, "method has no receiver"); return NULL; } else if (til->size() > 1) { - error_at(location, "method has multiple receivers"); + go_error_at(location, "method has multiple receivers"); return NULL; } else @@ -2428,7 +2503,7 @@ Parse::operand(bool may_be_sink, bool* is_parenthesized) if (!this->advance_token()->is_op(OPERATOR_DOT) || !this->advance_token()->is_identifier()) { - error_at(location, "unexpected reference to package"); + go_error_at(location, "unexpected reference to package"); return Expression::make_error(location); } package = named_object->package_value(); @@ -2448,9 +2523,9 @@ Parse::operand(bool may_be_sink, bool* is_parenthesized) && !named_object->type_value()->is_visible()) { go_assert(package != NULL); - error_at(location, "invalid reference to hidden type %<%s.%s%>", - Gogo::message_name(package->package_name()).c_str(), - Gogo::message_name(id).c_str()); + go_error_at(location, "invalid reference to hidden type %<%s.%s%>", + Gogo::message_name(package->package_name()).c_str(), + Gogo::message_name(id).c_str()); return Expression::make_error(location); } @@ -2462,14 +2537,14 @@ Parse::operand(bool may_be_sink, bool* is_parenthesized) std::string n1 = Gogo::message_name(package->package_name()); std::string n2 = Gogo::message_name(id); if (!is_exported) - error_at(location, - ("invalid reference to unexported identifier " - "%<%s.%s%>"), - n1.c_str(), n2.c_str()); + go_error_at(location, + ("invalid reference to unexported identifier " + "%<%s.%s%>"), + n1.c_str(), n2.c_str()); else - error_at(location, - "reference to undefined identifier %<%s.%s%>", - n1.c_str(), n2.c_str()); + go_error_at(location, + "reference to undefined identifier %<%s.%s%>", + n1.c_str(), n2.c_str()); return Expression::make_error(location); } @@ -2506,7 +2581,7 @@ Parse::operand(bool may_be_sink, bool* is_parenthesized) return Expression::make_sink(location); else { - error_at(location, "cannot use _ as value"); + go_error_at(location, "cannot use _ as value"); return Expression::make_error(location); } case Named_object::NAMED_OBJECT_FUNC: @@ -2591,7 +2666,7 @@ Parse::operand(bool may_be_sink, bool* is_parenthesized) ret = this->expression(PRECEDENCE_NORMAL, may_be_sink, true, NULL, NULL); if (!this->peek_token()->is_op(OPERATOR_RPAREN)) - error_at(this->location(), "missing %<)%>"); + go_error_at(this->location(), "missing %<)%>"); else this->advance_token(); if (is_parenthesized != NULL) @@ -2611,7 +2686,7 @@ Parse::operand(bool may_be_sink, bool* is_parenthesized) break; } - error_at(this->location(), "expected operand"); + go_error_at(this->location(), "expected operand"); return Expression::make_error(this->location()); } @@ -2658,7 +2733,7 @@ Parse::enclosing_var_reference(Named_object* in_function, Named_object* var, ins.first->index(), location); e = Expression::make_unary(OPERATOR_MULT, e, location); - return e; + return Expression::make_enclosing_var_reference(e, var, location); } // CompositeLit = LiteralType LiteralValue . @@ -2816,10 +2891,11 @@ Parse::composite_lit(Type* type, int depth, Location location) else { if (token->is_op(OPERATOR_SEMICOLON)) - error_at(this->location(), - "need trailing comma before newline in composite literal"); + go_error_at(this->location(), + ("need trailing comma before newline " + "in composite literal")); else - error_at(this->location(), "expected %<,%> or %<}%>"); + go_error_at(this->location(), "expected %<,%> or %<}%>"); this->gogo_->mark_locals_used(); int depth = 0; @@ -2959,6 +3035,21 @@ Parse::create_closure(Named_object* function, Enclosing_vars* enclosing_vars, Struct_type* st = closure_var->var_value()->type()->deref()->struct_type(); Expression* cv = Expression::make_struct_composite_literal(st, initializer, location); + + // When compiling the runtime, closures do not escape. When escape + // analysis becomes the default, and applies to closures, this + // should be changed to make it an error if a closure escapes. + if (this->gogo_->compiling_runtime() + && this->gogo_->package_name() == "runtime") + { + Temporary_statement* ctemp = Statement::make_temporary(st, cv, location); + this->gogo_->add_statement(ctemp); + Expression* ref = Expression::make_temporary_reference(ctemp, location); + Expression* addr = Expression::make_unary(OPERATOR_AND, ref, location); + addr->unary_expression()->set_does_not_escape(); + return addr; + } + return Expression::make_heap_expression(cv, location); } @@ -3012,13 +3103,13 @@ Parse::primary_expr(bool may_be_sink, bool may_be_composite_lit, Type* t = ret->type(); if (t->named_type() != NULL || t->forward_declaration_type() != NULL) - error_at(start_loc, - _("parentheses required around this composite literal " - "to avoid parsing ambiguity")); + go_error_at(start_loc, + _("parentheses required around this composite " + "literal to avoid parsing ambiguity")); } else if (operand_is_parenthesized) - error_at(start_loc, - "cannot parenthesize type in composite literal"); + go_error_at(start_loc, + "cannot parenthesize type in composite literal"); ret = this->composite_lit(ret->type(), 0, ret->location()); } else if (this->peek_token()->is_op(OPERATOR_LPAREN)) @@ -3032,12 +3123,12 @@ Parse::primary_expr(bool may_be_sink, bool may_be_composite_lit, this->advance_token(); if (this->peek_token()->is_op(OPERATOR_ELLIPSIS)) { - error_at(this->location(), - "invalid use of %<...%> in type conversion"); + go_error_at(this->location(), + "invalid use of %<...%> in type conversion"); this->advance_token(); } if (!this->peek_token()->is_op(OPERATOR_RPAREN)) - error_at(this->location(), "expected %<)%>"); + go_error_at(this->location(), "expected %<)%>"); else this->advance_token(); if (expr->is_error_expression()) @@ -3049,8 +3140,8 @@ Parse::primary_expr(bool may_be_sink, bool may_be_composite_lit, && t->array_type()->length() != NULL && t->array_type()->length()->is_nil_expression()) { - error_at(ret->location(), - "use of %<[...]%> outside of array literal"); + go_error_at(ret->location(), + "use of %<[...]%> outside of array literal"); ret = Expression::make_error(loc); } else @@ -3116,7 +3207,7 @@ Parse::selector(Expression* left, bool* is_type_switch) token->is_identifier_exported()); if (token->identifier() == "_") { - error_at(this->location(), "invalid use of %<_%>"); + go_error_at(this->location(), "invalid use of %<_%>"); name = Gogo::erroneous_name(); } this->advance_token(); @@ -3134,14 +3225,14 @@ Parse::selector(Expression* left, bool* is_type_switch) *is_type_switch = true; else { - error_at(this->location(), - "use of %<.(type)%> outside type switch"); + go_error_at(this->location(), + "use of %<.(type)%> outside type switch"); type = Type::make_error_type(); } this->advance_token(); } if (!this->peek_token()->is_op(OPERATOR_RPAREN)) - error_at(this->location(), "missing %<)%>"); + go_error_at(this->location(), "missing %<)%>"); else this->advance_token(); if (is_type_switch != NULL && *is_type_switch) @@ -3150,7 +3241,7 @@ Parse::selector(Expression* left, bool* is_type_switch) } else { - error_at(this->location(), "expected identifier or %<(%>"); + go_error_at(this->location(), "expected identifier or %<(%>"); return left; } } @@ -3179,7 +3270,8 @@ Parse::index(Expression* expr) end = Expression::make_nil(this->location()); else if (this->peek_token()->is_op(OPERATOR_COLON)) { - error_at(this->location(), "middle index required in 3-index slice"); + go_error_at(this->location(), + "middle index required in 3-index slice"); end = Expression::make_error(this->location()); } else @@ -3191,14 +3283,15 @@ Parse::index(Expression* expr) { if (this->advance_token()->is_op(OPERATOR_RSQUARE)) { - error_at(this->location(), "final index required in 3-index slice"); + go_error_at(this->location(), + "final index required in 3-index slice"); cap = Expression::make_error(this->location()); } else cap = this->expression(PRECEDENCE_NORMAL, false, true, NULL, NULL); } if (!this->peek_token()->is_op(OPERATOR_RSQUARE)) - error_at(this->location(), "missing %<]%>"); + go_error_at(this->location(), "missing %<]%>"); else this->advance_token(); return Expression::make_index(expr, start, end, cap, location); @@ -3228,7 +3321,7 @@ Parse::call(Expression* func) token = this->advance_token(); if (!token->is_op(OPERATOR_RPAREN)) { - error_at(this->location(), "missing %<)%>"); + go_error_at(this->location(), "missing %<)%>"); if (!this->skip_past_error(OPERATOR_RPAREN)) return Expression::make_error(this->location()); } @@ -3295,7 +3388,7 @@ Parse::id_to_expression(const std::string& name, Location location, case Named_object::NAMED_OBJECT_ERRONEOUS: return Expression::make_error(location); default: - error_at(this->location(), "unexpected type of identifier"); + go_error_at(this->location(), "unexpected type of identifier"); return Expression::make_error(location); } } @@ -3500,7 +3593,7 @@ Parse::unary_expr(bool may_be_sink, bool may_be_composite_lit, if (ct == NULL) { // This is probably impossible. - error_at(location, "expected channel type"); + go_error_at(location, "expected channel type"); return Expression::make_error(location); } else if (ct->may_receive()) @@ -3572,7 +3665,7 @@ Parse::reassociate_chan_direction(Channel_type *ct, Location location) Channel_type* ele = ct->element_type()->channel_type(); if (ele == NULL) { - error_at(location, "parse error"); + go_error_at(location, "parse error"); return Type::make_error_type(); } Type* sub = ele; @@ -3641,7 +3734,7 @@ Parse::statement(Label* label) this->for_stat(label); break; default: - error_at(this->location(), "expected statement"); + go_error_at(this->location(), "expected statement"); this->advance_token(); break; } @@ -3690,7 +3783,7 @@ Parse::statement(Label* label) break; default: - error_at(this->location(), "expected statement"); + go_error_at(this->location(), "expected statement"); this->advance_token(); break; } @@ -3786,7 +3879,7 @@ Parse::labeled_stmt(const std::string& label_name, Location location) if (label != NULL) label->set_is_used(); - error_at(location, "missing statement after label"); + go_error_at(location, "missing statement after label"); this->unget_token(Token::make_operator_token(OPERATOR_SEMICOLON, location)); return; @@ -3899,7 +3992,7 @@ Parse::simple_stat(bool may_be_composite_lit, bool* return_exp, if (token->is_op(OPERATOR_COLONEQ)) { if (!exp->is_error_expression()) - error_at(token->location(), "non-name on left side of %<:=%>"); + go_error_at(token->location(), "non-name on left side of %<:=%>"); this->gogo_->mark_locals_used(); while (!token->is_op(OPERATOR_SEMICOLON) && !token->is_eof()) @@ -3936,7 +4029,7 @@ Parse::statement_list() else { if (!this->peek_token()->is_eof() || !saw_errors()) - error_at(this->location(), "expected %<;%> or %<}%> or newline"); + go_error_at(this->location(), "expected %<;%> or %<}%> or newline"); if (!this->skip_past_error(OPERATOR_RCURLY)) return; } @@ -3978,11 +4071,6 @@ void Parse::inc_dec_stat(Expression* exp) { const Token* token = this->peek_token(); - - // Lvalue maps require special handling. - if (exp->index_expression() != NULL) - exp->index_expression()->set_is_lvalue(); - if (token->is_op(OPERATOR_PLUSPLUS)) this->gogo_->add_statement(Statement::make_inc_statement(exp)); else if (token->is_op(OPERATOR_MINUSMINUS)) @@ -4048,7 +4136,7 @@ Parse::tuple_assignment(Expression_list* lhs, bool may_be_composite_lit, && !token->is_op(OPERATOR_ANDEQ) && !token->is_op(OPERATOR_BITCLEAREQ)) { - error_at(this->location(), "expected assignment operator"); + go_error_at(this->location(), "expected assignment operator"); return; } Operator op = token->op(); @@ -4059,17 +4147,10 @@ Parse::tuple_assignment(Expression_list* lhs, bool may_be_composite_lit, if (lhs == NULL) return; - // Map expressions act differently when they are lvalues. - for (Expression_list::iterator plv = lhs->begin(); - plv != lhs->end(); - ++plv) - if ((*plv)->index_expression() != NULL) - (*plv)->index_expression()->set_is_lvalue(); - if (p_range_clause != NULL && token->is_keyword(KEYWORD_RANGE)) { if (op != OPERATOR_EQ) - error_at(this->location(), "range clause requires %<=%>"); + go_error_at(this->location(), "range clause requires %<=%>"); this->range_clause_expr(lhs, p_range_clause); return; } @@ -4087,7 +4168,7 @@ Parse::tuple_assignment(Expression_list* lhs, bool may_be_composite_lit, if ((*pe)->is_error_expression()) return; if (op != OPERATOR_EQ && (*pe)->is_sink_expression()) - error_at((*pe)->location(), "cannot use _ as value"); + go_error_at((*pe)->location(), "cannot use _ as value"); } for (Expression_list::const_iterator pe = vals->begin(); pe != vals->end(); @@ -4107,7 +4188,7 @@ Parse::tuple_assignment(Expression_list* lhs, bool may_be_composite_lit, if (lhs->size() > 1) { if (op != OPERATOR_EQ) - error_at(location, "multiple values only permitted with %<=%>"); + go_error_at(location, "multiple values only permitted with %<=%>"); s = Statement::make_tuple_assignment(lhs, vals, location); } else @@ -4127,7 +4208,7 @@ Parse::tuple_assignment(Expression_list* lhs, bool may_be_composite_lit, && (call = (*vals->begin())->call_expression()) != NULL) { if (op != OPERATOR_EQ) - error_at(location, "multiple results only permitted with %<=%>"); + go_error_at(location, "multiple results only permitted with %<=%>"); call->set_expected_result_count(lhs->size()); delete vals; vals = new Expression_list; @@ -4141,31 +4222,19 @@ Parse::tuple_assignment(Expression_list* lhs, bool may_be_composite_lit, && (map_index = (*vals->begin())->index_expression()) != NULL) { if (op != OPERATOR_EQ) - error_at(location, "two values from map requires %<=%>"); + go_error_at(location, "two values from map requires %<=%>"); Expression* val = lhs->front(); Expression* present = lhs->back(); Statement* s = Statement::make_tuple_map_assignment(val, present, map_index, location); this->gogo_->add_statement(s); } - else if (lhs->size() == 1 - && vals->size() == 2 - && (map_index = lhs->front()->index_expression()) != NULL) - { - if (op != OPERATOR_EQ) - error_at(location, "assigning tuple to map index requires %<=%>"); - Expression* val = vals->front(); - Expression* should_set = vals->back(); - Statement* s = Statement::make_map_assignment(map_index, val, should_set, - location); - this->gogo_->add_statement(s); - } else if (lhs->size() == 2 && vals->size() == 1 && (receive = (*vals->begin())->receive_expression()) != NULL) { if (op != OPERATOR_EQ) - error_at(location, "two values from receive requires %<=%>"); + go_error_at(location, "two values from receive requires %<=%>"); Expression* val = lhs->front(); Expression* success = lhs->back(); Expression* channel = receive->channel(); @@ -4179,7 +4248,7 @@ Parse::tuple_assignment(Expression_list* lhs, bool may_be_composite_lit, && (type_guard = (*vals->begin())->type_guard_expression()) != NULL) { if (op != OPERATOR_EQ) - error_at(location, "two values from type guard requires %<=%>"); + go_error_at(location, "two values from type guard requires %<=%>"); Expression* val = lhs->front(); Expression* ok = lhs->back(); Expression* expr = type_guard->expr(); @@ -4191,7 +4260,8 @@ Parse::tuple_assignment(Expression_list* lhs, bool may_be_composite_lit, } else { - error_at(location, "number of variables does not match number of values"); + go_error_at(location, ("number of variables does not " + "match number of values")); } } @@ -4215,7 +4285,7 @@ Parse::go_or_defer_stat() Call_expression* call_expr = expr->call_expression(); if (is_parenthesized || call_expr == NULL) { - error_at(expr_location, "argument to go/defer must be function call"); + go_error_at(expr_location, "argument to go/defer must be function call"); return; } @@ -4258,8 +4328,8 @@ Parse::return_stat() if (no == NULL) go_assert(saw_errors()); else if (!no->is_result_variable()) - error_at(location, "%qs is shadowed during return", - (*p)->message_name().c_str()); + go_error_at(location, "%qs is shadowed during return", + (*p)->message_name().c_str()); } } } @@ -4297,19 +4367,19 @@ Parse::if_stat() else if (saw_simple_stat) { if (saw_send_stmt) - error_at(this->location(), - ("send statement used as value; " - "use select for non-blocking send")); + go_error_at(this->location(), + ("send statement used as value; " + "use select for non-blocking send")); else - error_at(this->location(), - "expected %<;%> after statement in if expression"); + go_error_at(this->location(), + "expected %<;%> after statement in if expression"); if (!this->expression_may_start_here()) cond = Expression::make_error(this->location()); } if (cond == NULL && this->peek_token()->is_op(OPERATOR_LCURLY)) { - error_at(this->location(), - "missing condition in if statement"); + go_error_at(this->location(), + "missing condition in if statement"); cond = Expression::make_error(this->location()); } if (cond == NULL) @@ -4321,7 +4391,7 @@ Parse::if_stat() { Location semi_loc = this->location(); if (this->advance_token()->is_op(OPERATOR_LCURLY)) - error_at(semi_loc, "missing %<{%> after if clause"); + go_error_at(semi_loc, "missing %<{%> after if clause"); // Otherwise we will get an error when we call this->block // below. } @@ -4335,7 +4405,7 @@ Parse::if_stat() { Location semi_loc = this->location(); if (this->advance_token()->is_keyword(KEYWORD_ELSE)) - error_at(this->location(), + go_error_at(this->location(), "unexpected semicolon or newline before %<else%>"); else this->unget_token(Token::make_operator_token(OPERATOR_SEMICOLON, @@ -4353,7 +4423,7 @@ Parse::if_stat() this->block(); else { - error_at(this->location(), "expected %<if%> or %<{%>"); + go_error_at(this->location(), "expected %<if%> or %<{%>"); this->statement(NULL); } else_block = this->gogo_->finish_block(this->location()); @@ -4407,12 +4477,12 @@ Parse::switch_stat(Label* label) else if (saw_simple_stat) { if (saw_send_stmt) - error_at(this->location(), - ("send statement used as value; " - "use select for non-blocking send")); + go_error_at(this->location(), + ("send statement used as value; " + "use select for non-blocking send")); else - error_at(this->location(), - "expected %<;%> after statement in switch expression"); + go_error_at(this->location(), + "expected %<;%> after statement in switch expression"); } if (!this->peek_token()->is_op(OPERATOR_LCURLY)) { @@ -4445,7 +4515,8 @@ Parse::switch_stat(Label* label) if (switch_val == NULL || !switch_val->is_error_expression()) { - error_at(id_loc, "expected type switch assignment"); + go_error_at(id_loc, + "expected type switch assignment"); switch_val = Expression::make_error(id_loc); } } @@ -4470,10 +4541,10 @@ Parse::switch_stat(Label* label) Location token_loc = this->location(); if (this->peek_token()->is_op(OPERATOR_SEMICOLON) && this->advance_token()->is_op(OPERATOR_LCURLY)) - error_at(token_loc, "missing %<{%> after switch clause"); + go_error_at(token_loc, "missing %<{%> after switch clause"); else if (this->peek_token()->is_op(OPERATOR_COLONEQ)) { - error_at(token_loc, "invalid variable name"); + go_error_at(token_loc, "invalid variable name"); this->advance_token(); this->expression(PRECEDENCE_NORMAL, false, false, &type_switch.found, NULL); @@ -4493,7 +4564,7 @@ Parse::switch_stat(Label* label) } else { - error_at(this->location(), "expected %<{%>"); + go_error_at(this->location(), "expected %<{%>"); if (have_type_switch_block) this->gogo_->add_block(this->gogo_->finish_block(this->location()), location); @@ -4540,7 +4611,7 @@ Parse::expr_switch_body(Label* label, Expression* switch_val, if (this->peek_token()->is_eof()) { if (!saw_errors()) - error_at(this->location(), "missing %<}%>"); + go_error_at(this->location(), "missing %<}%>"); return NULL; } this->expr_case_clause(case_clauses, &saw_default); @@ -4568,7 +4639,7 @@ Parse::expr_case_clause(Case_clauses* clauses, bool* saw_default) if (!this->peek_token()->is_op(OPERATOR_COLON)) { if (!saw_errors()) - error_at(this->location(), "expected %<:%>"); + go_error_at(this->location(), "expected %<:%>"); return; } else @@ -4590,14 +4661,15 @@ Parse::expr_case_clause(Case_clauses* clauses, bool* saw_default) if (this->advance_token()->is_op(OPERATOR_SEMICOLON)) this->advance_token(); if (this->peek_token()->is_op(OPERATOR_RCURLY)) - error_at(fallthrough_loc, _("cannot fallthrough final case in switch")); + go_error_at(fallthrough_loc, + _("cannot fallthrough final case in switch")); } if (is_default) { if (*saw_default) { - error_at(location, "multiple defaults in switch"); + go_error_at(location, "multiple defaults in switch"); return; } *saw_default = true; @@ -4627,7 +4699,7 @@ Parse::expr_switch_case(bool* is_default) else { if (!saw_errors()) - error_at(this->location(), "expected %<case%> or %<default%>"); + go_error_at(this->location(), "expected %<case%> or %<default%>"); if (!token->is_op(OPERATOR_RCURLY)) this->advance_token(); return NULL; @@ -4647,8 +4719,8 @@ Parse::type_switch_body(Label* label, const Type_switch& type_switch, { if (Gogo::is_sink_name(var_name)) { - error_at(type_switch.location, - "no new variables on left side of %<:=%>"); + go_error_at(type_switch.location, + "no new variables on left side of %<:=%>"); var_name.clear(); } else @@ -4672,7 +4744,7 @@ Parse::type_switch_body(Label* label, const Type_switch& type_switch, { if (this->peek_token()->is_eof()) { - error_at(this->location(), "missing %<}%>"); + go_error_at(this->location(), "missing %<}%>"); return NULL; } this->type_case_clause(var_name, init, case_clauses, &saw_default, @@ -4700,8 +4772,8 @@ Parse::type_switch_body(Label* label, const Type_switch& type_switch, } } if (!used) - error_at(type_switch.location, "%qs declared and not used", - Gogo::message_name(var_name).c_str()); + go_error_at(type_switch.location, "%qs declared and not used", + Gogo::message_name(var_name).c_str()); } return statement; } @@ -4722,7 +4794,7 @@ Parse::type_case_clause(const std::string& var_name, Expression* init, this->type_switch_case(&types, &is_default); if (!this->peek_token()->is_op(OPERATOR_COLON)) - error_at(this->location(), "expected %<:%>"); + go_error_at(this->location(), "expected %<:%>"); else this->advance_token(); @@ -4752,8 +4824,8 @@ Parse::type_case_clause(const std::string& var_name, Expression* init, if (this->peek_token()->is_keyword(KEYWORD_FALLTHROUGH)) { - error_at(this->location(), - "fallthrough is not permitted in a type switch"); + go_error_at(this->location(), + "fallthrough is not permitted in a type switch"); if (this->advance_token()->is_op(OPERATOR_SEMICOLON)) this->advance_token(); } @@ -4763,7 +4835,7 @@ Parse::type_case_clause(const std::string& var_name, Expression* init, go_assert(types.empty()); if (*saw_default) { - error_at(location, "multiple defaults in type switch"); + go_error_at(location, "multiple defaults in type switch"); return; } *saw_default = true; @@ -4821,7 +4893,7 @@ Parse::type_switch_case(std::vector<Type*>* types, bool* is_default) } else { - error_at(this->location(), "expected %<case%> or %<default%>"); + go_error_at(this->location(), "expected %<case%> or %<default%>"); if (!token->is_op(OPERATOR_RCURLY)) this->advance_token(); } @@ -4841,10 +4913,10 @@ Parse::select_stat(Label* label) Location token_loc = token->location(); if (token->is_op(OPERATOR_SEMICOLON) && this->advance_token()->is_op(OPERATOR_LCURLY)) - error_at(token_loc, "unexpected semicolon or newline before %<{%>"); + go_error_at(token_loc, "unexpected semicolon or newline before %<{%>"); else { - error_at(this->location(), "expected %<{%>"); + go_error_at(this->location(), "expected %<{%>"); return; } } @@ -4860,7 +4932,7 @@ Parse::select_stat(Label* label) { if (this->peek_token()->is_eof()) { - error_at(this->location(), "expected %<}%>"); + go_error_at(this->location(), "expected %<}%>"); return; } this->comm_clause(select_clauses, &saw_default); @@ -4891,17 +4963,10 @@ Parse::comm_clause(Select_clauses* clauses, bool* saw_default) bool got_case = this->comm_case(&is_send, &channel, &val, &closed, &varname, &closedname, &is_default); - if (!is_send - && varname.empty() - && closedname.empty() - && val != NULL - && val->index_expression() != NULL) - val->index_expression()->set_is_lvalue(); - if (this->peek_token()->is_op(OPERATOR_COLON)) this->advance_token(); else - error_at(this->location(), "expected colon"); + go_error_at(this->location(), "expected colon"); this->gogo_->start_block(this->location()); @@ -4932,7 +4997,7 @@ Parse::comm_clause(Select_clauses* clauses, bool* saw_default) { if (*saw_default) { - error_at(location, "multiple defaults in select"); + go_error_at(location, "multiple defaults in select"); return; } *saw_default = true; @@ -4971,7 +5036,7 @@ Parse::comm_case(bool* is_send, Expression** channel, Expression** val, } else { - error_at(this->location(), "expected %<case%> or %<default%>"); + go_error_at(this->location(), "expected %<case%> or %<default%>"); if (!token->is_op(OPERATOR_RCURLY)) this->advance_token(); return false; @@ -5008,13 +5073,13 @@ Parse::send_or_recv_stmt(bool* is_send, Expression** channel, Expression** val, if (re == NULL) { if (!e->is_error_expression()) - error_at(this->location(), "expected receive expression"); + go_error_at(this->location(), "expected receive expression"); return false; } if (recv_var == "_") { - error_at(recv_var_loc, - "no new variables on left side of %<:=%>"); + go_error_at(recv_var_loc, + "no new variables on left side of %<:=%>"); recv_var = Gogo::erroneous_name(); } *is_send = false; @@ -5043,14 +5108,14 @@ Parse::send_or_recv_stmt(bool* is_send, Expression** channel, Expression** val, if (re == NULL) { if (!e->is_error_expression()) - error_at(this->location(), + go_error_at(this->location(), "expected receive expression"); return false; } if (recv_var == "_" && recv_closed == "_") { - error_at(recv_var_loc, - "no new variables on left side of %<:=%>"); + go_error_at(recv_var_loc, + "no new variables on left side of %<:=%>"); recv_var = Gogo::erroneous_name(); } *is_send = false; @@ -5117,7 +5182,7 @@ Parse::send_or_recv_stmt(bool* is_send, Expression** channel, Expression** val, { if (!this->advance_token()->is_op(OPERATOR_CHANOP)) { - error_at(this->location(), "missing %<<-%>"); + go_error_at(this->location(), "missing %<<-%>"); return false; } *is_send = false; @@ -5142,9 +5207,9 @@ Parse::send_or_recv_stmt(bool* is_send, Expression** channel, Expression** val, if (saw_comma) { if (closed_is_id) - error_at(this->location(), "expected %<=%> or %<:=%>"); + go_error_at(this->location(), "expected %<=%> or %<:=%>"); else - error_at(this->location(), "expected %<=%>"); + go_error_at(this->location(), "expected %<=%>"); return false; } @@ -5158,7 +5223,7 @@ Parse::send_or_recv_stmt(bool* is_send, Expression** channel, Expression** val, return true; } - error_at(this->location(), "expected %<<-%> or %<=%>"); + go_error_at(this->location(), "expected %<<-%> or %<=%>"); return false; } @@ -5185,8 +5250,8 @@ Parse::for_stat(Label* label) { if (token->is_keyword(KEYWORD_VAR)) { - error_at(this->location(), - "var declaration not allowed in for initializer"); + go_error_at(this->location(), + "var declaration not allowed in for initializer"); this->var_decl(); } @@ -5203,17 +5268,18 @@ Parse::for_stat(Label* label) if (cond == NULL && !range_clause.found) { if (saw_send_stmt) - error_at(this->location(), - ("send statement used as value; " - "use select for non-blocking send")); + go_error_at(this->location(), + ("send statement used as value; " + "use select for non-blocking send")); else - error_at(this->location(), "parse error in for statement"); + go_error_at(this->location(), + "parse error in for statement"); } } else { if (range_clause.found) - error_at(this->location(), "parse error after range clause"); + go_error_at(this->location(), "parse error after range clause"); if (cond != NULL) { @@ -5233,7 +5299,7 @@ Parse::for_stat(Label* label) { Location semi_loc = this->location(); if (this->advance_token()->is_op(OPERATOR_LCURLY)) - error_at(semi_loc, "missing %<{%> after for clause"); + go_error_at(semi_loc, "missing %<{%> after for clause"); // Otherwise we will get an error when we call this->block // below. } @@ -5304,7 +5370,7 @@ Parse::for_clause(Expression** cond, Block** post) *cond = NULL; else if (this->peek_token()->is_op(OPERATOR_LCURLY)) { - error_at(this->location(), "missing %<{%> after for clause"); + go_error_at(this->location(), "missing %<{%> after for clause"); *cond = NULL; *post = NULL; return; @@ -5312,7 +5378,7 @@ Parse::for_clause(Expression** cond, Block** post) else *cond = this->expression(PRECEDENCE_NORMAL, false, true, NULL, NULL); if (!this->peek_token()->is_op(OPERATOR_SEMICOLON)) - error_at(this->location(), "expected semicolon"); + go_error_at(this->location(), "expected semicolon"); else this->advance_token(); @@ -5340,7 +5406,7 @@ Parse::range_clause_decl(const Typed_identifier_list* til, p_range_clause->found = true; if (til->size() > 2) - error_at(this->location(), "too many variables for range clause"); + go_error_at(this->location(), "too many variables for range clause"); this->advance_token(); Expression* expr = this->expression(PRECEDENCE_NORMAL, false, false, NULL, @@ -5375,7 +5441,7 @@ Parse::range_clause_decl(const Typed_identifier_list* til, } if (!any_new) - error_at(location, "variables redeclared but no variable is new"); + go_error_at(location, "variables redeclared but no variable is new"); } // The = version of RangeClause. This is called with a list of @@ -5391,7 +5457,7 @@ Parse::range_clause_expr(const Expression_list* vals, go_assert(vals->size() >= 1); if (vals->size() > 2) - error_at(this->location(), "too many variables for range clause"); + go_error_at(this->location(), "too many variables for range clause"); this->advance_token(); p_range_clause->range = this->expression(PRECEDENCE_NORMAL, false, false, @@ -5477,8 +5543,8 @@ Parse::break_stat() { if (this->break_stack_ == NULL || this->break_stack_->empty()) { - error_at(this->location(), - "break statement not within for or switch or select"); + go_error_at(this->location(), + "break statement not within for or switch or select"); return; } enclosing = this->break_stack_->back().first; @@ -5494,8 +5560,8 @@ Parse::break_stat() this->gogo_->add_label_reference(token->identifier(), Linemap::unknown_location(), false); - error_at(token->location(), "invalid break label %qs", - Gogo::message_name(token->identifier()).c_str()); + go_error_at(token->location(), "invalid break label %qs", + Gogo::message_name(token->identifier()).c_str()); this->advance_token(); return; } @@ -5534,7 +5600,7 @@ Parse::continue_stat() { if (this->continue_stack_ == NULL || this->continue_stack_->empty()) { - error_at(this->location(), "continue statement not within for"); + go_error_at(this->location(), "continue statement not within for"); return; } enclosing = this->continue_stack_->back().first; @@ -5550,8 +5616,8 @@ Parse::continue_stat() this->gogo_->add_label_reference(token->identifier(), Linemap::unknown_location(), false); - error_at(token->location(), "invalid continue label %qs", - Gogo::message_name(token->identifier()).c_str()); + go_error_at(token->location(), "invalid continue label %qs", + Gogo::message_name(token->identifier()).c_str()); this->advance_token(); return; } @@ -5579,7 +5645,7 @@ Parse::goto_stat() Location location = this->location(); const Token* token = this->advance_token(); if (!token->is_identifier()) - error_at(this->location(), "expected label for goto"); + go_error_at(this->location(), "expected label for goto"); else { Label* label = this->gogo_->add_label_reference(token->identifier(), @@ -5600,7 +5666,7 @@ Parse::package_clause() std::string name; if (!token->is_keyword(KEYWORD_PACKAGE)) { - error_at(this->location(), "program must start with package clause"); + go_error_at(this->location(), "program must start with package clause"); name = "ERROR"; } else @@ -5611,14 +5677,14 @@ Parse::package_clause() name = token->identifier(); if (name == "_") { - error_at(this->location(), "invalid package name _"); + go_error_at(this->location(), "invalid package name _"); name = Gogo::erroneous_name(); } this->advance_token(); } else { - error_at(this->location(), "package name must be an identifier"); + go_error_at(this->location(), "package name must be an identifier"); name = "ERROR"; } } @@ -5659,13 +5725,13 @@ Parse::import_spec(void*) if (!token->is_string()) { - error_at(this->location(), "import statement not a string"); + go_error_at(this->location(), "import statement not a string"); this->advance_token(); return; } this->gogo_->import_package(token->string_value(), local_name, - is_local_name_exported, location); + is_local_name_exported, true, location); this->advance_token(); } @@ -5682,8 +5748,8 @@ Parse::program() if (token->is_op(OPERATOR_SEMICOLON)) token = this->advance_token(); else - error_at(this->location(), - "expected %<;%> or newline after package clause"); + go_error_at(this->location(), + "expected %<;%> or newline after package clause"); while (token->is_keyword(KEYWORD_IMPORT)) { @@ -5692,8 +5758,8 @@ Parse::program() if (token->is_op(OPERATOR_SEMICOLON)) token = this->advance_token(); else - error_at(this->location(), - "expected %<;%> or newline after import declaration"); + go_error_at(this->location(), + "expected %<;%> or newline after import declaration"); } while (!token->is_eof()) @@ -5702,7 +5768,7 @@ Parse::program() this->declaration(); else { - error_at(this->location(), "expected declaration"); + go_error_at(this->location(), "expected declaration"); this->gogo_->mark_locals_used(); do this->advance_token(); @@ -5719,12 +5785,13 @@ Parse::program() else if (!token->is_eof() || !saw_errors()) { if (token->is_op(OPERATOR_CHANOP)) - error_at(this->location(), - ("send statement used as value; " - "use select for non-blocking send")); + go_error_at(this->location(), + ("send statement used as value; " + "use select for non-blocking send")); else - error_at(this->location(), - "expected %<;%> or newline after top level declaration"); + go_error_at(this->location(), + ("expected %<;%> or newline after top " + "level declaration")); this->skip_past_error(OPERATOR_INVALID); } } @@ -5785,30 +5852,16 @@ Parse::verify_not_sink(Expression* expr) { if (expr->is_sink_expression()) { - error_at(expr->location(), "cannot use _ as value"); + go_error_at(expr->location(), "cannot use _ as value"); expr = Expression::make_error(expr->location()); } // If this can not be a sink, and it is a variable, then we are // using the variable, not just assigning to it. - Var_expression* ve = expr->var_expression(); - if (ve != NULL) - this->mark_var_used(ve->named_object()); - else if (expr->deref()->field_reference_expression() != NULL - && this->gogo_->current_function() != NULL) - { - // We could be looking at a variable referenced from a closure. - // If so, we need to get the enclosed variable and mark it as used. - Function* this_function = this->gogo_->current_function()->func_value(); - Named_object* closure = this_function->closure_var(); - if (closure != NULL) - { - unsigned int var_index = - expr->deref()->field_reference_expression()->field_index(); - this->mark_var_used(this_function->enclosing_var(var_index - 1)); - } - } - + if (expr->var_expression() != NULL) + this->mark_var_used(expr->var_expression()->named_object()); + else if (expr->enclosed_var_expression() != NULL) + this->mark_var_used(expr->enclosed_var_expression()->variable()); return expr; } diff --git a/gcc/go/gofrontend/parse.h b/gcc/go/gofrontend/parse.h index 734071a737..e13dcc9754 100644 --- a/gcc/go/gofrontend/parse.h +++ b/gcc/go/gofrontend/parse.h @@ -81,7 +81,8 @@ class Parse Expression* expr; Type_switch() - : found(false), name(), location(UNKNOWN_LOCATION), expr(NULL) + : found(false), name(), location(Linemap::unknown_location()), + expr(NULL) { } }; @@ -209,7 +210,7 @@ class Parse void simple_var_decl_or_assignment(const std::string&, Location, bool may_be_composite_lit, Range_clause*, Type_switch*); - void function_decl(bool saw_nointerface); + void function_decl(unsigned int pragmas); Typed_identifier* receiver(); Expression* operand(bool may_be_sink, bool *is_parenthesized); Expression* enclosing_var_reference(Named_object*, Named_object*, diff --git a/gcc/go/gofrontend/runtime.cc b/gcc/go/gofrontend/runtime.cc index de68f64b59..a921449802 100644 --- a/gcc/go/gofrontend/runtime.cc +++ b/gcc/go/gofrontend/runtime.cc @@ -54,8 +54,6 @@ enum Runtime_function_type RFT_SLICE, // Go type map[any]any, C type struct __go_map *. RFT_MAP, - // Pointer to map iteration type. - RFT_MAPITER, // Go type chan any, C type struct __go_channel *. RFT_CHAN, // Go type non-empty interface, C type struct __go_interface. @@ -66,8 +64,14 @@ enum Runtime_function_type RFT_FUNC_PTR, // Pointer to Go type descriptor. RFT_TYPE, - // Pointer to map descriptor. - RFT_MAPDESCRIPTOR, + // [2]string. + RFT_ARRAY2STRING, + // [3]string. + RFT_ARRAY3STRING, + // [4]string. + RFT_ARRAY4STRING, + // [5]string. + RFT_ARRAY5STRING, NUMBER_OF_RUNTIME_FUNCTION_TYPES }; @@ -153,10 +157,6 @@ runtime_function_type(Runtime_function_type bft) t = Type::make_map_type(any, any, bloc); break; - case RFT_MAPITER: - t = Type::make_pointer_type(Runtime::map_iteration_type()); - break; - case RFT_CHAN: t = Type::make_channel_type(true, true, any); break; @@ -189,8 +189,48 @@ runtime_function_type(Runtime_function_type bft) t = Type::make_type_descriptor_ptr_type(); break; - case RFT_MAPDESCRIPTOR: - t = Type::make_pointer_type(Map_type::make_map_descriptor_type()); + case RFT_ARRAY2STRING: + { + Array_type* at = + Type::make_array_type(Type::make_string_type(), + Expression::make_integer_ul(2, NULL, + bloc)); + at->set_is_array_incomparable(); + t = at; + } + break; + + case RFT_ARRAY3STRING: + { + Array_type* at = + Type::make_array_type(Type::make_string_type(), + Expression::make_integer_ul(3, NULL, + bloc)); + at->set_is_array_incomparable(); + t = at; + } + break; + + case RFT_ARRAY4STRING: + { + Array_type* at = + Type::make_array_type(Type::make_string_type(), + Expression::make_integer_ul(4, NULL, + bloc)); + at->set_is_array_incomparable(); + t = at; + } + break; + + case RFT_ARRAY5STRING: + { + Array_type* at = + Type::make_array_type(Type::make_string_type(), + Expression::make_integer_ul(5, NULL, + bloc)); + at->set_is_array_incomparable(); + t = at; + } break; } @@ -225,7 +265,6 @@ convert_to_runtime_function_type(Runtime_function_type bft, Expression* e, case RFT_COMPLEX128: case RFT_STRING: case RFT_POINTER: - case RFT_MAPITER: case RFT_FUNC_PTR: { Type* t = runtime_function_type(bft); @@ -239,16 +278,15 @@ convert_to_runtime_function_type(Runtime_function_type bft, Expression* e, case RFT_CHAN: case RFT_IFACE: case RFT_EFACE: + case RFT_ARRAY2STRING: + case RFT_ARRAY3STRING: + case RFT_ARRAY4STRING: + case RFT_ARRAY5STRING: return Expression::make_unsafe_cast(runtime_function_type(bft), e, loc); case RFT_TYPE: go_assert(e->type() == Type::make_type_descriptor_ptr_type()); return e; - - case RFT_MAPDESCRIPTOR: - go_assert(e->type()->points_to() - == Map_type::make_map_descriptor_type()); - return e; } } @@ -389,16 +427,37 @@ Runtime::make_call(Runtime::Function code, Location loc, return Expression::make_call(func, args, false, loc); } -// The type we use for a map iteration. This is really a struct which -// is four pointers long. This must match the runtime struct -// __go_hash_iter. +// Get the runtime code for a named builtin function. This is used as a helper +// when creating function references for call expressions. Every reference to +// a builtin runtime function should have the associated runtime code. If the +// name is ambiguous and can refer to many runtime codes, return +// NUMBER_OF_FUNCTIONS. -Type* -Runtime::map_iteration_type() +Runtime::Function +Runtime::name_to_code(const std::string& name) { - const unsigned long map_iteration_size = 4; - Expression* iexpr = - Expression::make_integer_ul(map_iteration_size, NULL, - Linemap::predeclared_location()); - return Type::make_array_type(runtime_function_type(RFT_POINTER), iexpr); + Function code = Runtime::NUMBER_OF_FUNCTIONS; + + // Aliases seen in function declaration code. + // TODO(cmang): Add other aliases. + if (name == "new") + code = Runtime::NEW; + else if (name == "close") + code = Runtime::CLOSE; + else if (name == "copy") + code = Runtime::SLICECOPY; + else if (name == "append") + code = Runtime::GROWSLICE; + else if (name == "delete") + code = Runtime::MAPDELETE; + else + { + // Look through the known names for a match. + for (size_t i = 0; i < Runtime::NUMBER_OF_FUNCTIONS; i++) + { + if (strcmp(runtime_functions[i].name, name.c_str()) == 0) + code = static_cast<Runtime::Function>(i); + } + } + return code; } diff --git a/gcc/go/gofrontend/runtime.def b/gcc/go/gofrontend/runtime.def index 2e79263a63..90bf34f7e9 100644 --- a/gcc/go/gofrontend/runtime.def +++ b/gcc/go/gofrontend/runtime.def @@ -31,41 +31,50 @@ // The standard C memcmp function, used for struct comparisons. DEF_GO_RUNTIME(MEMCMP, "__go_memcmp", P3(POINTER, POINTER, UINTPTR), R1(INT)) -// Range over a string, returning the next index. -DEF_GO_RUNTIME(STRINGITER, "runtime.stringiter", P2(STRING, INT), R1(INT)) +// Decode a non-ASCII rune from a string. +DEF_GO_RUNTIME(DECODERUNE, "runtime.decoderune", P2(STRING, INT), + R2(RUNE, INT)) -// Range over a string, returning the next index and character. -DEF_GO_RUNTIME(STRINGITER2, "runtime.stringiter2", P2(STRING, INT), - R2(INT, RUNE)) - -// Concatenate two strings. -DEF_GO_RUNTIME(STRING_PLUS, "__go_string_plus", P2(STRING, STRING), R1(STRING)) +// Concatenate strings. +DEF_GO_RUNTIME(CONCATSTRINGS, "runtime.concatstrings", P2(POINTER, SLICE), + R1(STRING)) +DEF_GO_RUNTIME(CONCATSTRING2, "runtime.concatstring2", + P2(POINTER, ARRAY2STRING), R1(STRING)) +DEF_GO_RUNTIME(CONCATSTRING3, "runtime.concatstring3", + P2(POINTER, ARRAY3STRING), R1(STRING)) +DEF_GO_RUNTIME(CONCATSTRING4, "runtime.concatstring4", + P2(POINTER, ARRAY4STRING), R1(STRING)) +DEF_GO_RUNTIME(CONCATSTRING5, "runtime.concatstring5", + P2(POINTER, ARRAY5STRING), R1(STRING)) + +// Compare two strings for equality. +DEF_GO_RUNTIME(EQSTRING, "runtime.eqstring", P2(STRING, STRING), R1(BOOL)) // Compare two strings. -DEF_GO_RUNTIME(STRCMP, "__go_strcmp", P2(STRING, STRING), R1(INT)) +DEF_GO_RUNTIME(CMPSTRING, "runtime.cmpstring", P2(STRING, STRING), R1(INT)) // Take a slice of a string. DEF_GO_RUNTIME(STRING_SLICE, "__go_string_slice", P3(STRING, INT, INT), R1(STRING)) // Convert an integer to a string. -DEF_GO_RUNTIME(INT_TO_STRING, "__go_int_to_string", P1(INT), R1(STRING)) +DEF_GO_RUNTIME(INTSTRING, "runtime.intstring", P2(POINTER, INT64), R1(STRING)) -// Convert a byte array to a string. -DEF_GO_RUNTIME(BYTE_ARRAY_TO_STRING, "__go_byte_array_to_string", - P2(POINTER, INT), R1(STRING)) +// Convert a []byte to a string. +DEF_GO_RUNTIME(SLICEBYTETOSTRING, "runtime.slicebytetostring", + P2(POINTER, SLICE), R1(STRING)) -// Convert an int array to a string. -DEF_GO_RUNTIME(INT_ARRAY_TO_STRING, "__go_int_array_to_string", - P2(POINTER, INT), R1(STRING)) +// Convert a []rune to a string. +DEF_GO_RUNTIME(SLICERUNETOSTRING, "runtime.slicerunetostring", + P2(POINTER, SLICE), R1(STRING)) -// Convert a string to a byte slice. -DEF_GO_RUNTIME(STRING_TO_BYTE_ARRAY, "__go_string_to_byte_array", - P1(STRING), R1(SLICE)) +// Convert a string to a []byte. +DEF_GO_RUNTIME(STRINGTOSLICEBYTE, "runtime.stringtoslicebyte", + P2(POINTER, STRING), R1(SLICE)) -// Convert a string to an int slice. -DEF_GO_RUNTIME(STRING_TO_INT_ARRAY, "__go_string_to_int_array", - P1(STRING), R1(SLICE)) +// Convert a string to a []rune. +DEF_GO_RUNTIME(STRINGTOSLICERUNE, "runtime.stringtoslicerune", + P2(POINTER, STRING), R1(SLICE)) // Complex division. @@ -75,75 +84,63 @@ DEF_GO_RUNTIME(COMPLEX128_DIV, "__go_complex128_div", P2(COMPLEX128, COMPLEX128), R1(COMPLEX128)) // Make a slice. -DEF_GO_RUNTIME(MAKESLICE1, "__go_make_slice1", P2(TYPE, UINTPTR), R1(SLICE)) -DEF_GO_RUNTIME(MAKESLICE2, "__go_make_slice2", P3(TYPE, UINTPTR, UINTPTR), +DEF_GO_RUNTIME(MAKESLICE, "runtime.makeslice", P3(TYPE, INT, INT), R1(SLICE)) -DEF_GO_RUNTIME(MAKESLICE1BIG, "__go_make_slice1_big", P2(TYPE, UINT64), - R1(SLICE)) -DEF_GO_RUNTIME(MAKESLICE2BIG, "__go_make_slice2_big", P3(TYPE, UINT64, UINT64), + +DEF_GO_RUNTIME(MAKESLICE64, "runtime.makeslice64", P3(TYPE, INT64, INT64), R1(SLICE)) // Make a map. -DEF_GO_RUNTIME(MAKEMAP, "__go_new_map", P2(MAPDESCRIPTOR, UINTPTR), R1(MAP)) -DEF_GO_RUNTIME(MAKEMAPBIG, "__go_new_map_big", P2(MAPDESCRIPTOR, UINT64), +DEF_GO_RUNTIME(MAKEMAP, "runtime.makemap", P4(TYPE, INT64, POINTER, POINTER), R1(MAP)) // Build a map from a composite literal. DEF_GO_RUNTIME(CONSTRUCT_MAP, "__go_construct_map", - P6(POINTER, UINTPTR, UINTPTR, UINTPTR, UINTPTR, POINTER), + P5(POINTER, UINTPTR, UINTPTR, UINTPTR, POINTER), R1(MAP)) -// Get the length of a map (the number of entries). -DEF_GO_RUNTIME(MAP_LEN, "__go_map_len", P1(MAP), R1(INT)) - // Look up a key in a map. -DEF_GO_RUNTIME(MAP_INDEX, "__go_map_index", P3(MAP, POINTER, BOOL), +DEF_GO_RUNTIME(MAPACCESS1, "runtime.mapaccess1", P3(TYPE, MAP, POINTER), R1(POINTER)) -// Look up a key in a map returning whether it is present. -DEF_GO_RUNTIME(MAPACCESS2, "runtime.mapaccess2", - P4(TYPE, MAP, POINTER, POINTER), R1(BOOL)) +// Look up a key in a map when the value is large. +DEF_GO_RUNTIME(MAPACCESS1_FAT, "runtime.mapaccess1_fat", + P4(TYPE, MAP, POINTER, POINTER), R1(POINTER)) -// Tuple assignment to a map element. -DEF_GO_RUNTIME(MAPASSIGN2, "runtime.mapassign2", - P4(MAP, POINTER, POINTER, BOOL), R0()) +// Look up a key in a map returning the value and whether it is +// present. +DEF_GO_RUNTIME(MAPACCESS2, "runtime.mapaccess2", P3(TYPE, MAP, POINTER), + R2(POINTER, BOOL)) -// Delete a key from a map. -DEF_GO_RUNTIME(MAPDELETE, "runtime.mapdelete", P2(MAP, POINTER), R0()) +// Look up a key in a map, returning the value and whether it is +// present, when the value is large. +DEF_GO_RUNTIME(MAPACCESS2_FAT, "runtime.mapaccess2_fat", + P4(TYPE, MAP, POINTER, POINTER), R2(POINTER, BOOL)) -// Begin a range over a map. -DEF_GO_RUNTIME(MAPITERINIT, "runtime.mapiterinit", P2(MAP, MAPITER), R0()) +// Assignment to a key in a map. +DEF_GO_RUNTIME(MAPASSIGN, "runtime.mapassign", P3(TYPE, MAP, POINTER), + R1(POINTER)) -// Range over a map, returning the next key. -DEF_GO_RUNTIME(MAPITER1, "runtime.mapiter1", P2(MAPITER, POINTER), R0()) +// Delete a key from a map. +DEF_GO_RUNTIME(MAPDELETE, "runtime.mapdelete", P3(TYPE, MAP, POINTER), R0()) -// Range over a map, returning the next key and value. -DEF_GO_RUNTIME(MAPITER2, "runtime.mapiter2", P3(MAPITER, POINTER, POINTER), +// Begin a range over a map. +DEF_GO_RUNTIME(MAPITERINIT, "runtime.mapiterinit", P3(TYPE, MAP, POINTER), R0()) // Range over a map, moving to the next map entry. -DEF_GO_RUNTIME(MAPITERNEXT, "runtime.mapiternext", P1(MAPITER), R0()) +DEF_GO_RUNTIME(MAPITERNEXT, "runtime.mapiternext", P1(POINTER), R0()) // Make a channel. -DEF_GO_RUNTIME(MAKECHAN, "__go_new_channel", P2(TYPE, UINTPTR), R1(CHAN)) -DEF_GO_RUNTIME(MAKECHANBIG, "__go_new_channel_big", P2(TYPE, UINT64), R1(CHAN)) - -// Get the length of a channel (the number of unread values). -DEF_GO_RUNTIME(CHAN_LEN, "__go_chan_len", P1(CHAN), R1(INT)) - -// Get the capacity of a channel (the size of the buffer). -DEF_GO_RUNTIME(CHAN_CAP, "__go_chan_cap", P1(CHAN), R1(INT)) +DEF_GO_RUNTIME(MAKECHAN, "runtime.makechan", P2(TYPE, INT64), R1(CHAN)) -// Send a small value on a channel. -DEF_GO_RUNTIME(SEND_SMALL, "__go_send_small", P3(TYPE, CHAN, UINT64), R0()) - -// Send a big value on a channel. -DEF_GO_RUNTIME(SEND_BIG, "__go_send_big", P3(TYPE, CHAN, POINTER), R0()) +// Send a value on a channel. +DEF_GO_RUNTIME(CHANSEND, "runtime.chansend1", P3(TYPE, CHAN, POINTER), R0()) // Receive a value from a channel. -DEF_GO_RUNTIME(RECEIVE, "__go_receive", P3(TYPE, CHAN, POINTER), R0()) +DEF_GO_RUNTIME(CHANRECV1, "runtime.chanrecv1", P3(TYPE, CHAN, POINTER), R0()) // Receive a value from a channel returning whether it is closed. DEF_GO_RUNTIME(CHANRECV2, "runtime.chanrecv2", P3(TYPE, CHAN, POINTER), @@ -151,7 +148,7 @@ DEF_GO_RUNTIME(CHANRECV2, "runtime.chanrecv2", P3(TYPE, CHAN, POINTER), // Start building a select statement. -DEF_GO_RUNTIME(NEWSELECT, "runtime.newselect", P1(INT32), R1(POINTER)) +DEF_GO_RUNTIME(NEWSELECT, "runtime.newselect", P3(POINTER, INT64, INT32), R0()) // Add a default clause to a select statement. DEF_GO_RUNTIME(SELECTDEFAULT, "runtime.selectdefault", @@ -176,44 +173,50 @@ DEF_GO_RUNTIME(SELECTGO, "runtime.selectgo", P1(POINTER), R1(INT32)) // Panic. -DEF_GO_RUNTIME(PANIC, "__go_panic", P1(EFACE), R0()) +DEF_GO_RUNTIME(GOPANIC, "runtime.gopanic", P1(EFACE), R0()) // Recover. -DEF_GO_RUNTIME(RECOVER, "__go_recover", P0(), R1(EFACE)) +DEF_GO_RUNTIME(GORECOVER, "runtime.gorecover", P0(), R1(EFACE)) // Recover when called directly from defer. -DEF_GO_RUNTIME(DEFERRED_RECOVER, "__go_deferred_recover", P0(), R1(EFACE)) +DEF_GO_RUNTIME(DEFERREDRECOVER, "runtime.deferredrecover", P0(), R1(EFACE)) // Decide whether this function can call recover. -DEF_GO_RUNTIME(CAN_RECOVER, "__go_can_recover", P1(POINTER), R1(BOOL)) - -// Get the return address of the function. -DEF_GO_RUNTIME(RETURN_ADDRESS, "__go_return_address", P1(INT), R1(POINTER)) +DEF_GO_RUNTIME(CANRECOVER, "runtime.canrecover", P1(POINTER), R1(BOOL)) // Set the return address for defer in a defer thunk. -DEF_GO_RUNTIME(SET_DEFER_RETADDR, "__go_set_defer_retaddr", P1(POINTER), +DEF_GO_RUNTIME(SETDEFERRETADDR, "runtime.setdeferretaddr", P1(POINTER), R1(BOOL)) // Check for a deferred function in an exception handler. -DEF_GO_RUNTIME(CHECK_DEFER, "__go_check_defer", P1(BOOLPTR), R0()) +DEF_GO_RUNTIME(CHECKDEFER, "runtime.checkdefer", P1(BOOLPTR), R0()) // Run deferred functions. -DEF_GO_RUNTIME(UNDEFER, "__go_undefer", P1(BOOLPTR), R0()) +DEF_GO_RUNTIME(DEFERRETURN, "runtime.deferreturn", P1(BOOLPTR), R0()) // Panic with a runtime error. DEF_GO_RUNTIME(RUNTIME_ERROR, "__go_runtime_error", P1(INT32), R0()) // Close. -DEF_GO_RUNTIME(CLOSE, "__go_builtin_close", P1(CHAN), R0()) +DEF_GO_RUNTIME(CLOSE, "runtime.closechan", P1(CHAN), R0()) // Copy. -DEF_GO_RUNTIME(COPY, "__go_copy", P3(POINTER, POINTER, UINTPTR), R0()) +DEF_GO_RUNTIME(SLICECOPY, "runtime.slicecopy", P3(SLICE, SLICE, UINTPTR), + R1(INT)) -// Append. -DEF_GO_RUNTIME(APPEND, "__go_append", P4(SLICE, POINTER, UINTPTR, UINTPTR), - R1(SLICE)) +// Copy from string. +DEF_GO_RUNTIME(SLICESTRINGCOPY, "runtime.slicestringcopy", P2(SLICE, STRING), + R1(INT)) + +// Copy of value containing pointers. +DEF_GO_RUNTIME(TYPEDSLICECOPY, "runtime.typedslicecopy", + P3(TYPE, SLICE, SLICE), R1(INT)) + + +// Grow a slice for append. +DEF_GO_RUNTIME(GROWSLICE, "runtime.growslice", P3(TYPE, SLICE, INT), R1(SLICE)) // Register roots (global variables) for the garbage collector. @@ -227,7 +230,8 @@ DEF_GO_RUNTIME(NEW, "__go_new", P2(TYPE, UINTPTR), R1(POINTER)) DEF_GO_RUNTIME(GO, "__go_go", P2(FUNC_PTR, POINTER), R0()) // Defer a function. -DEF_GO_RUNTIME(DEFER, "__go_defer", P3(BOOLPTR, FUNC_PTR, POINTER), R0()) +DEF_GO_RUNTIME(DEFERPROC, "runtime.deferproc", P3(BOOLPTR, FUNC_PTR, POINTER), + R0()) // Convert an empty interface to an empty interface, returning ok. @@ -260,22 +264,27 @@ DEF_GO_RUNTIME(IFACEE2T2, "runtime.ifaceE2T2", P3(TYPE, EFACE, POINTER), DEF_GO_RUNTIME(IFACEI2T2, "runtime.ifaceI2T2", P3(TYPE, IFACE, POINTER), R1(BOOL)) -// A type assertion from one interface type to another. This is -// used for a type assertion. -DEF_GO_RUNTIME(ASSERT_INTERFACE, "__go_assert_interface", P2(TYPE, TYPE), R1(POINTER)) - -// Convert one interface type to another. This is used for an -// assignment. -DEF_GO_RUNTIME(CONVERT_INTERFACE, "__go_convert_interface", P2(TYPE, TYPE), +// Return the interface method table for the second type converted to +// the first type which is a (possibly empty) interface type. Panics +// if the second type is nil (indicating a nil interface value) or if +// the conversion is not possible. Used for type assertions. This is +// like REQUIREITAB, but for type assertions. +DEF_GO_RUNTIME(ASSERTITAB, "runtime.assertitab", P2(TYPE, TYPE), R1(POINTER)) + +// Return the interface method table for the second type converted to +// the first type, which is a non-empty interface type. Return nil if +// the second type is nil, indicating a nil interface value. Panics +// if the conversion is not possible. Used for assignments. This is +// like ASSERTITAB, but for assignments. +DEF_GO_RUNTIME(REQUIREITAB, "runtime.requireitab", P2(TYPE, TYPE), R1(POINTER)) // Check whether an interface type may be converted to a // non-interface type. -DEF_GO_RUNTIME(CHECK_INTERFACE_TYPE, "__go_check_interface_type", - P3(TYPE, TYPE, TYPE), R0()) +DEF_GO_RUNTIME(ASSERTI2T, "runtime.assertI2T", P3(TYPE, TYPE, TYPE), R0()) -// Return whether we can convert an interface type to a type. -DEF_GO_RUNTIME(IFACEI2TP, "runtime.ifaceI2Tp", P2(TYPE, TYPE), R1(BOOL)) +// Return whether we can convert a type to an interface type. +DEF_GO_RUNTIME(IFACET2IP, "runtime.ifaceT2Ip", P2(TYPE, TYPE), R1(BOOL)) // Get the type descriptor of an empty interface. DEF_GO_RUNTIME(EFACETYPE, "runtime.efacetype", P1(EFACE), R1(TYPE)) @@ -288,63 +297,65 @@ DEF_GO_RUNTIME(IFACETYPE, "runtime.ifacetype", P1(IFACE), R1(TYPE)) DEF_GO_RUNTIME(IFACETYPEEQ, "runtime.ifacetypeeq", P2(TYPE, TYPE), R1(BOOL)) // Compare two empty interface values. -DEF_GO_RUNTIME(EMPTY_INTERFACE_COMPARE, "__go_empty_interface_compare", - P2(EFACE, EFACE), R1(INT)) +DEF_GO_RUNTIME(EFACEEQ, "runtime.efaceeq", P2(EFACE, EFACE), R1(BOOL)) // Compare an empty interface value to a non-interface value. -DEF_GO_RUNTIME(EMPTY_INTERFACE_VALUE_COMPARE, - "__go_empty_interface_value_compare", - P3(EFACE, TYPE, POINTER), R1(INT)) +DEF_GO_RUNTIME(EFACEVALEQ, "runtime.efacevaleq", P3(EFACE, TYPE, POINTER), + R1(BOOL)) // Compare two non-empty interface values. -DEF_GO_RUNTIME(INTERFACE_COMPARE, "__go_interface_compare", - P2(IFACE, IFACE), R1(INT)) +DEF_GO_RUNTIME(IFACEEQ, "runtime.ifaceeq", P2(IFACE, IFACE), R1(BOOL)) // Compare a non-empty interface value to a non-interface value. -DEF_GO_RUNTIME(INTERFACE_VALUE_COMPARE, "__go_interface_value_compare", - P3(IFACE, TYPE, POINTER), R1(INT)) +DEF_GO_RUNTIME(IFACEVALEQ, "runtime.ifacevaleq", P3(IFACE, TYPE, POINTER), + R1(BOOL)) // Compare a non-empty interface value to an interface value. -DEF_GO_RUNTIME(INTERFACE_EMPTY_COMPARE, "__go_interface_empty_compare", - P2(IFACE, EFACE), R1(INT)) +DEF_GO_RUNTIME(IFACEEFACEEQ, "runtime.ifaceefaceeq", P2(IFACE, EFACE), + R1(BOOL)) + + +// Lock the printer (for print/println). +DEF_GO_RUNTIME(PRINTLOCK, "runtime.printlock", P0(), R0()) +// Unlock the printer (for print/println). +DEF_GO_RUNTIME(PRINTUNLOCK, "runtime.printunlock", P0(), R0()) // Print a string (for print/println). -DEF_GO_RUNTIME(PRINT_STRING, "__go_print_string", P1(STRING), R0()) +DEF_GO_RUNTIME(PRINTSTRING, "runtime.printstring", P1(STRING), R0()) // Print a uint64 (for print/println). -DEF_GO_RUNTIME(PRINT_UINT64, "__go_print_uint64", P1(UINT64), R0()) +DEF_GO_RUNTIME(PRINTUINT, "runtime.printuint", P1(UINT64), R0()) // Print a int64 (for print/println). -DEF_GO_RUNTIME(PRINT_INT64, "__go_print_int64", P1(INT64), R0()) +DEF_GO_RUNTIME(PRINTINT, "runtime.printint", P1(INT64), R0()) // Print a float64 (for print/println). -DEF_GO_RUNTIME(PRINT_DOUBLE, "__go_print_double", P1(FLOAT64), R0()) +DEF_GO_RUNTIME(PRINTFLOAT, "runtime.printfloat", P1(FLOAT64), R0()) // Print a complex128 (for print/println). -DEF_GO_RUNTIME(PRINT_COMPLEX, "__go_print_complex", P1(COMPLEX128), R0()) +DEF_GO_RUNTIME(PRINTCOMPLEX, "runtime.printcomplex", P1(COMPLEX128), R0()) // Print a bool (for print/println). -DEF_GO_RUNTIME(PRINT_BOOL, "__go_print_bool", P1(BOOL), R0()) +DEF_GO_RUNTIME(PRINTBOOL, "runtime.printbool", P1(BOOL), R0()) // Print a pointer/map/channel/function (for print/println). -DEF_GO_RUNTIME(PRINT_POINTER, "__go_print_pointer", P1(POINTER), R0()) +DEF_GO_RUNTIME(PRINTPOINTER, "runtime.printpointer", P1(POINTER), R0()) // Print an empty interface (for print/println). -DEF_GO_RUNTIME(PRINT_EMPTY_INTERFACE, "__go_print_empty_interface", - P1(EFACE), R0()) +DEF_GO_RUNTIME(PRINTEFACE, "runtime.printeface", P1(EFACE), R0()) // Print a non-empty interface (for print/println). -DEF_GO_RUNTIME(PRINT_INTERFACE, "__go_print_interface", P1(IFACE), R0()) +DEF_GO_RUNTIME(PRINTIFACE, "runtime.printiface", P1(IFACE), R0()) // Print a slice (for print/println). -DEF_GO_RUNTIME(PRINT_SLICE, "__go_print_slice", P1(SLICE), R0()) +DEF_GO_RUNTIME(PRINTSLICE, "runtime.printslice", P1(SLICE), R0()) // Print a space (for println). -DEF_GO_RUNTIME(PRINT_SPACE, "__go_print_space", P0(), R0()) +DEF_GO_RUNTIME(PRINTSP, "runtime.printsp", P0(), R0()) // Print a newline (for println). -DEF_GO_RUNTIME(PRINT_NL, "__go_print_nl", P0(), R0()) +DEF_GO_RUNTIME(PRINTNL, "runtime.printnl", P0(), R0()) // Used for field tracking for data analysis. diff --git a/gcc/go/gofrontend/runtime.h b/gcc/go/gofrontend/runtime.h index be5dcbe25d..e92510b33d 100644 --- a/gcc/go/gofrontend/runtime.h +++ b/gcc/go/gofrontend/runtime.h @@ -39,9 +39,9 @@ class Runtime static void convert_types(Gogo*); - // Return the type used for iterations over maps. - static Type* - map_iteration_type(); + // Return the runtime code for a named builtin function. + static Function + name_to_code(const std::string&); private: static Named_object* diff --git a/gcc/go/gofrontend/statements.cc b/gcc/go/gofrontend/statements.cc index a84203a5fe..d6ab4ccd67 100644 --- a/gcc/go/gofrontend/statements.cc +++ b/gcc/go/gofrontend/statements.cc @@ -7,6 +7,7 @@ #include "go-system.h" #include "go-c.h" +#include "go-diagnostics.h" #include "types.h" #include "expressions.h" #include "gogo.h" @@ -14,7 +15,6 @@ #include "backend.h" #include "statements.h" #include "ast-dump.h" -#include "dataflow.h" // Class Statement. @@ -164,7 +164,7 @@ Statement::set_is_error() void Statement::report_error(const char* msg) { - error_at(this->location_, "%s", msg); + go_error_at(this->location_, "%s", msg); this->set_is_error(); } @@ -191,6 +191,21 @@ class Error_statement : public Statement do_dump_statement(Ast_dump_context*) const; }; +// +// Helper to tack on available source position information +// at the end of a statement. +// +static std::string +dsuffix(Location location) +{ + std::string lstr = Linemap::location_to_string(location); + if (lstr == "") + return lstr; + std::string rval(" // "); + rval += lstr; + return rval; +} + // Dump the AST representation for an error statement. void @@ -270,6 +285,7 @@ Variable_declaration_statement::do_flatten(Gogo* gogo, Named_object* function, Bstatement* Variable_declaration_statement::do_get_backend(Translate_context* context) { + Bfunction* bfunction = context->function()->func_value()->get_decl(); Variable* var = this->var_->var_value(); Bvariable* bvar = this->var_->get_backend_variable(context->gogo(), context->function()); @@ -278,7 +294,7 @@ Variable_declaration_statement::do_get_backend(Translate_context* context) if (!var->is_in_heap()) { go_assert(binit != NULL); - return context->backend()->init_statement(bvar, binit); + return context->backend()->init_statement(bfunction, bvar, binit); } // Something takes the address of this variable, so the value is @@ -301,12 +317,12 @@ Variable_declaration_statement::do_get_backend(Translate_context* context) Expression* e = Expression::make_temporary_reference(temp, loc); e = Expression::make_unary(OPERATOR_MULT, e, loc); Bexpression* be = e->get_backend(context); - set = context->backend()->assignment_statement(be, binit, loc); + set = context->backend()->assignment_statement(bfunction, be, binit, loc); } Expression* ref = Expression::make_temporary_reference(temp, loc); Bexpression* bref = ref->get_backend(context); - Bstatement* sinit = context->backend()->init_statement(bvar, bref); + Bstatement* sinit = context->backend()->init_statement(bfunction, bvar, bref); std::vector<Bstatement*> stats; stats.reserve(3); @@ -338,7 +354,7 @@ Variable_declaration_statement::do_dump_statement( ast_dump_context->ostream() << "= "; ast_dump_context->dump_expression(var->init()); } - ast_dump_context->ostream() << std::endl; + ast_dump_context->ostream() << dsuffix(location()) << std::endl; } // Make a variable declaration. @@ -429,9 +445,9 @@ Temporary_statement::do_check_types(Gogo*) if (!Type::are_assignable(this->type_, this->init_->type(), &reason)) { if (reason.empty()) - error_at(this->location(), "incompatible types in assignment"); + go_error_at(this->location(), "incompatible types in assignment"); else - error_at(this->location(), "incompatible types in assignment (%s)", + go_error_at(this->location(), "incompatible types in assignment (%s)", reason.c_str()); this->set_is_error(); } @@ -533,7 +549,7 @@ Temporary_statement::do_dump_statement(Ast_dump_context* ast_dump_context) const ast_dump_context->ostream() << " = "; ast_dump_context->dump_expression(this->init_); } - ast_dump_context->ostream() << std::endl; + ast_dump_context->ostream() << dsuffix(location()) << std::endl; } // Make and initialize a temporary variable in BLOCK. @@ -545,6 +561,106 @@ Statement::make_temporary(Type* type, Expression* init, return new Temporary_statement(type, init, location); } +// The Move_subexpressions class is used to move all top-level +// subexpressions of an expression. This is used for things like +// index expressions in which we must evaluate the index value before +// it can be changed by a multiple assignment. + +class Move_subexpressions : public Traverse +{ + public: + Move_subexpressions(int skip, Block* block) + : Traverse(traverse_expressions), + skip_(skip), block_(block) + { } + + protected: + int + expression(Expression**); + + private: + // The number of subexpressions to skip moving. This is used to + // avoid moving the array itself, as we only need to move the index. + int skip_; + // The block where new temporary variables should be added. + Block* block_; +}; + +int +Move_subexpressions::expression(Expression** pexpr) +{ + if (this->skip_ > 0) + --this->skip_; + else if ((*pexpr)->temporary_reference_expression() == NULL + && !(*pexpr)->is_nil_expression() + && !(*pexpr)->is_constant()) + { + Location loc = (*pexpr)->location(); + Temporary_statement* temp = Statement::make_temporary(NULL, *pexpr, loc); + this->block_->add_statement(temp); + *pexpr = Expression::make_temporary_reference(temp, loc); + } + // We only need to move top-level subexpressions. + return TRAVERSE_SKIP_COMPONENTS; +} + +// The Move_ordered_evals class is used to find any subexpressions of +// an expression that have an evaluation order dependency. It creates +// temporary variables to hold them. + +class Move_ordered_evals : public Traverse +{ + public: + Move_ordered_evals(Block* block) + : Traverse(traverse_expressions), + block_(block) + { } + + protected: + int + expression(Expression**); + + private: + // The block where new temporary variables should be added. + Block* block_; +}; + +int +Move_ordered_evals::expression(Expression** pexpr) +{ + // We have to look at subexpressions first. + if ((*pexpr)->traverse_subexpressions(this) == TRAVERSE_EXIT) + return TRAVERSE_EXIT; + + int i; + if ((*pexpr)->must_eval_subexpressions_in_order(&i)) + { + Move_subexpressions ms(i, this->block_); + if ((*pexpr)->traverse_subexpressions(&ms) == TRAVERSE_EXIT) + return TRAVERSE_EXIT; + } + + if ((*pexpr)->must_eval_in_order()) + { + Call_expression* call = (*pexpr)->call_expression(); + if (call != NULL && call->is_multi_value_arg()) + { + // A call expression which returns multiple results as an argument + // to another call must be handled specially. We can't create a + // temporary because there is no type to give it. Instead, group + // the caller and this multi-valued call argument and use a temporary + // variable to hold them. + return TRAVERSE_SKIP_COMPONENTS; + } + + Location loc = (*pexpr)->location(); + Temporary_statement* temp = Statement::make_temporary(NULL, *pexpr, loc); + this->block_->add_statement(temp); + *pexpr = Expression::make_temporary_reference(temp, loc); + } + return TRAVERSE_SKIP_COMPONENTS; +} + // Class Assignment_statement. // Traversal. @@ -564,6 +680,72 @@ Assignment_statement::do_traverse_assignments(Traverse_assignments* tassign) return true; } +// Lower an assignment to a map index expression to a runtime function +// call. + +Statement* +Assignment_statement::do_lower(Gogo*, Named_object*, Block* enclosing, + Statement_inserter*) +{ + Map_index_expression* mie = this->lhs_->map_index_expression(); + if (mie != NULL) + { + Location loc = this->location(); + + Expression* map = mie->map(); + Map_type* mt = map->type()->map_type(); + if (mt == NULL) + { + go_assert(saw_errors()); + return Statement::make_error_statement(loc); + } + + Block* b = new Block(enclosing, loc); + + // Move out any subexpressions on the left hand side to make + // sure that functions are called in the required order. + Move_ordered_evals moe(b); + mie->traverse_subexpressions(&moe); + + // Copy the key into a temporary so that we can take its address + // without pushing the value onto the heap. + + // var key_temp KEY_TYPE = MAP_INDEX + Temporary_statement* key_temp = Statement::make_temporary(mt->key_type(), + mie->index(), + loc); + b->add_statement(key_temp); + + // Copy the value into a temporary to ensure that it is + // evaluated before we add the key to the map. This may matter + // if the value is itself a reference to the map. + + // var val_temp VAL_TYPE = RHS + Temporary_statement* val_temp = Statement::make_temporary(mt->val_type(), + this->rhs_, + loc); + b->add_statement(val_temp); + + // *mapassign(TYPE, MAP, &key_temp) = RHS + Expression* a1 = Expression::make_type_descriptor(mt, loc); + Expression* a2 = mie->map(); + Temporary_reference_expression* ref = + Expression::make_temporary_reference(key_temp, loc); + Expression* a3 = Expression::make_unary(OPERATOR_AND, ref, loc); + Expression* call = Runtime::make_call(Runtime::MAPASSIGN, loc, 3, + a1, a2, a3); + Type* ptrval_type = Type::make_pointer_type(mt->val_type()); + call = Expression::make_cast(ptrval_type, call, loc); + Expression* indir = Expression::make_unary(OPERATOR_MULT, call, loc); + ref = Expression::make_temporary_reference(val_temp, loc); + b->add_statement(Statement::make_assignment(indir, ref, loc)); + + return Statement::make_block_statement(b, loc); + } + + return this; +} + // Set types for the assignment. void @@ -608,10 +790,10 @@ Assignment_statement::do_check_types(Gogo*) if (!Type::are_assignable(lhs_type, rhs_type, &reason)) { if (reason.empty()) - error_at(this->location(), "incompatible types in assignment"); + go_error_at(this->location(), "incompatible types in assignment"); else - error_at(this->location(), "incompatible types in assignment (%s)", - reason.c_str()); + go_error_at(this->location(), "incompatible types in assignment (%s)", + reason.c_str()); this->set_is_error(); } @@ -650,6 +832,84 @@ Assignment_statement::do_flatten(Gogo*, Named_object*, Block*, return this; } + +// Helper class to locate a root Var_expression within an expression +// tree and mark it as being in an "lvalue" or assignment +// context. Examples: +// +// x, y = 40, foo(w) +// x[2] = bar(v) +// x.z.w[blah(v + u)], y.another = 2, 3 +// +// In the code above, vars "x" and "y" appear in lvalue / assignment +// context, whereas the other vars "v", "u", etc are in rvalue context. +// +// Note: at the moment the Var_expression version of "do_copy()" +// defaults to returning the original object, not a new object, +// meaning that a given Var_expression can be referenced from more +// than one place in the tree. This means that when we want to mark a +// Var_expression as having lvalue semantics, we need to make a copy +// of it. Example: +// +// mystruct.myfield += 42 +// +// When this is lowered to eliminate the += operator, we get a tree +// +// mystruct.myfield = mystruct.field + 42 +// +// in which the "mystruct" same Var_expression is referenced on both +// LHS and RHS subtrees. This in turn means that if we try to mark the +// LHS Var_expression the RHS Var_expression will also be marked. To +// address this issue, the code below clones any var_expression before +// applying an lvalue marking. +// + +class Mark_lvalue_varexprs : public Traverse +{ + public: + Mark_lvalue_varexprs() + : Traverse(traverse_expressions) + { } + + protected: + int + expression(Expression**); + + private: +}; + +int Mark_lvalue_varexprs::expression(Expression** ppexpr) +{ + Expression* e = *ppexpr; + + Var_expression* ve = e->var_expression(); + if (ve) + { + ve = new Var_expression(ve->named_object(), ve->location()); + ve->set_in_lvalue_pos(); + *ppexpr = ve; + return TRAVERSE_EXIT; + } + + Field_reference_expression* fre = e->field_reference_expression(); + if (fre != NULL) + return TRAVERSE_CONTINUE; + + Array_index_expression* aie = e->array_index_expression(); + if (aie != NULL) + { + Mark_lvalue_varexprs mlve; + aie->array()->traverse_subexpressions(&mlve); + return TRAVERSE_EXIT; + } + + Unary_expression* ue = e->unary_expression(); + if (ue && ue->op() == OPERATOR_MULT) + return TRAVERSE_CONTINUE; + + return TRAVERSE_EXIT; +} + // Convert an assignment statement to the backend representation. Bstatement* @@ -658,15 +918,21 @@ Assignment_statement::do_get_backend(Translate_context* context) if (this->lhs_->is_sink_expression()) { Bexpression* rhs = this->rhs_->get_backend(context); - return context->backend()->expression_statement(rhs); + Bfunction* bfunction = context->function()->func_value()->get_decl(); + return context->backend()->expression_statement(bfunction, rhs); } + Mark_lvalue_varexprs mlve; + Expression::traverse(&this->lhs_, &mlve); + Bexpression* lhs = this->lhs_->get_backend(context); Expression* conv = Expression::convert_for_assignment(context->gogo(), this->lhs_->type(), this->rhs_, this->location()); Bexpression* rhs = conv->get_backend(context); - return context->backend()->assignment_statement(lhs, rhs, this->location()); + Bfunction* bfunction = context->function()->func_value()->get_decl(); + return context->backend()->assignment_statement(bfunction, lhs, rhs, + this->location()); } // Dump the AST representation for an assignment statement. @@ -679,7 +945,7 @@ Assignment_statement::do_dump_statement(Ast_dump_context* ast_dump_context) ast_dump_context->dump_expression(this->lhs_); ast_dump_context->ostream() << " = " ; ast_dump_context->dump_expression(this->rhs_); - ast_dump_context->ostream() << std::endl; + ast_dump_context->ostream() << dsuffix(location()) << std::endl; } // Make an assignment statement. @@ -691,106 +957,6 @@ Statement::make_assignment(Expression* lhs, Expression* rhs, return new Assignment_statement(lhs, rhs, location); } -// The Move_subexpressions class is used to move all top-level -// subexpressions of an expression. This is used for things like -// index expressions in which we must evaluate the index value before -// it can be changed by a multiple assignment. - -class Move_subexpressions : public Traverse -{ - public: - Move_subexpressions(int skip, Block* block) - : Traverse(traverse_expressions), - skip_(skip), block_(block) - { } - - protected: - int - expression(Expression**); - - private: - // The number of subexpressions to skip moving. This is used to - // avoid moving the array itself, as we only need to move the index. - int skip_; - // The block where new temporary variables should be added. - Block* block_; -}; - -int -Move_subexpressions::expression(Expression** pexpr) -{ - if (this->skip_ > 0) - --this->skip_; - else if ((*pexpr)->temporary_reference_expression() == NULL - && !(*pexpr)->is_nil_expression() - && !(*pexpr)->is_constant()) - { - Location loc = (*pexpr)->location(); - Temporary_statement* temp = Statement::make_temporary(NULL, *pexpr, loc); - this->block_->add_statement(temp); - *pexpr = Expression::make_temporary_reference(temp, loc); - } - // We only need to move top-level subexpressions. - return TRAVERSE_SKIP_COMPONENTS; -} - -// The Move_ordered_evals class is used to find any subexpressions of -// an expression that have an evaluation order dependency. It creates -// temporary variables to hold them. - -class Move_ordered_evals : public Traverse -{ - public: - Move_ordered_evals(Block* block) - : Traverse(traverse_expressions), - block_(block) - { } - - protected: - int - expression(Expression**); - - private: - // The block where new temporary variables should be added. - Block* block_; -}; - -int -Move_ordered_evals::expression(Expression** pexpr) -{ - // We have to look at subexpressions first. - if ((*pexpr)->traverse_subexpressions(this) == TRAVERSE_EXIT) - return TRAVERSE_EXIT; - - int i; - if ((*pexpr)->must_eval_subexpressions_in_order(&i)) - { - Move_subexpressions ms(i, this->block_); - if ((*pexpr)->traverse_subexpressions(&ms) == TRAVERSE_EXIT) - return TRAVERSE_EXIT; - } - - if ((*pexpr)->must_eval_in_order()) - { - Call_expression* call = (*pexpr)->call_expression(); - if (call != NULL && call->is_multi_value_arg()) - { - // A call expression which returns multiple results as an argument - // to another call must be handled specially. We can't create a - // temporary because there is no type to give it. Instead, group - // the caller and this multi-valued call argument and use a temporary - // variable to hold them. - return TRAVERSE_SKIP_COMPONENTS; - } - - Location loc = (*pexpr)->location(); - Temporary_statement* temp = Statement::make_temporary(NULL, *pexpr, loc); - this->block_->add_statement(temp); - *pexpr = Expression::make_temporary_reference(temp, loc); - } - return TRAVERSE_SKIP_COMPONENTS; -} - // An assignment operation statement. class Assignment_operation_statement : public Statement @@ -920,7 +1086,7 @@ Assignment_operation_statement::do_dump_statement( ast_dump_context->dump_expression(this->lhs_); ast_dump_context->dump_operator(this->op_); ast_dump_context->dump_expression(this->rhs_); - ast_dump_context->ostream() << std::endl; + ast_dump_context->ostream() << dsuffix(location()) << std::endl; } // Make an assignment operation statement. @@ -1066,7 +1232,7 @@ Tuple_assignment_statement::do_dump_statement( ast_dump_context->dump_expression_list(this->lhs_); ast_dump_context->ostream() << " = "; ast_dump_context->dump_expression_list(this->rhs_); - ast_dump_context->ostream() << std::endl; + ast_dump_context->ostream() << dsuffix(location()) << std::endl; } // Make a tuple assignment statement. @@ -1132,7 +1298,7 @@ Tuple_map_assignment_statement::do_traverse(Traverse* traverse) // Lower a tuple map assignment. Statement* -Tuple_map_assignment_statement::do_lower(Gogo*, Named_object*, +Tuple_map_assignment_statement::do_lower(Gogo* gogo, Named_object*, Block* enclosing, Statement_inserter*) { Location loc = this->location(); @@ -1163,10 +1329,11 @@ Tuple_map_assignment_statement::do_lower(Gogo*, Named_object*, Statement::make_temporary(map_type->key_type(), map_index->index(), loc); b->add_statement(key_temp); - // var val_temp VAL_TYPE - Temporary_statement* val_temp = - Statement::make_temporary(map_type->val_type(), NULL, loc); - b->add_statement(val_temp); + // var val_ptr_temp *VAL_TYPE + Type* val_ptr_type = Type::make_pointer_type(map_type->val_type()); + Temporary_statement* val_ptr_temp = Statement::make_temporary(val_ptr_type, + NULL, loc); + b->add_statement(val_ptr_temp); // var present_temp bool Temporary_statement* present_temp = @@ -1176,24 +1343,34 @@ Tuple_map_assignment_statement::do_lower(Gogo*, Named_object*, NULL, loc); b->add_statement(present_temp); - // present_temp = mapaccess2(DESCRIPTOR, MAP, &key_temp, &val_temp) + // val_ptr_temp, present_temp = mapaccess2(DESCRIPTOR, MAP, &key_temp) Expression* a1 = Expression::make_type_descriptor(map_type, loc); Expression* a2 = map_index->map(); Temporary_reference_expression* ref = Expression::make_temporary_reference(key_temp, loc); Expression* a3 = Expression::make_unary(OPERATOR_AND, ref, loc); - ref = Expression::make_temporary_reference(val_temp, loc); - Expression* a4 = Expression::make_unary(OPERATOR_AND, ref, loc); - Expression* call = Runtime::make_call(Runtime::MAPACCESS2, loc, 4, - a1, a2, a3, a4); + Expression* a4 = map_type->fat_zero_value(gogo); + Call_expression* call; + if (a4 == NULL) + call = Runtime::make_call(Runtime::MAPACCESS2, loc, 3, a1, a2, a3); + else + call = Runtime::make_call(Runtime::MAPACCESS2_FAT, loc, 4, a1, a2, a3, a4); + ref = Expression::make_temporary_reference(val_ptr_temp, loc); + ref->set_is_lvalue(); + Expression* res = Expression::make_call_result(call, 0); + res = Expression::make_unsafe_cast(val_ptr_type, res, loc); + Statement* s = Statement::make_assignment(ref, res, loc); + b->add_statement(s); ref = Expression::make_temporary_reference(present_temp, loc); ref->set_is_lvalue(); - Statement* s = Statement::make_assignment(ref, call, loc); + res = Expression::make_call_result(call, 1); + s = Statement::make_assignment(ref, res, loc); b->add_statement(s); - // val = val_temp - ref = Expression::make_temporary_reference(val_temp, loc); - s = Statement::make_assignment(this->val_, ref, loc); + // val = *val__ptr_temp + ref = Expression::make_temporary_reference(val_ptr_temp, loc); + Expression* ind = Expression::make_unary(OPERATOR_MULT, ref, loc); + s = Statement::make_assignment(this->val_, ind, loc); b->add_statement(s); // present = present_temp @@ -1216,7 +1393,7 @@ Tuple_map_assignment_statement::do_dump_statement( ast_dump_context->dump_expression(this->present_); ast_dump_context->ostream() << " = "; ast_dump_context->dump_expression(this->map_index_); - ast_dump_context->ostream() << std::endl; + ast_dump_context->ostream() << dsuffix(location()) << std::endl; } // Make a map assignment statement which returns a pair of values. @@ -1229,140 +1406,6 @@ Statement::make_tuple_map_assignment(Expression* val, Expression* present, return new Tuple_map_assignment_statement(val, present, map_index, location); } -// Assign a pair of entries to a map. -// m[k] = v, p - -class Map_assignment_statement : public Statement -{ - public: - Map_assignment_statement(Expression* map_index, - Expression* val, Expression* should_set, - Location location) - : Statement(STATEMENT_MAP_ASSIGNMENT, location), - map_index_(map_index), val_(val), should_set_(should_set) - { } - - protected: - int - do_traverse(Traverse* traverse); - - bool - do_traverse_assignments(Traverse_assignments*) - { go_unreachable(); } - - Statement* - do_lower(Gogo*, Named_object*, Block*, Statement_inserter*); - - Bstatement* - do_get_backend(Translate_context*) - { go_unreachable(); } - - void - do_dump_statement(Ast_dump_context*) const; - - private: - // A reference to the map index which should be set or deleted. - Expression* map_index_; - // The value to add to the map. - Expression* val_; - // Whether or not to add the value. - Expression* should_set_; -}; - -// Traverse a map assignment. - -int -Map_assignment_statement::do_traverse(Traverse* traverse) -{ - if (this->traverse_expression(traverse, &this->map_index_) == TRAVERSE_EXIT - || this->traverse_expression(traverse, &this->val_) == TRAVERSE_EXIT) - return TRAVERSE_EXIT; - return this->traverse_expression(traverse, &this->should_set_); -} - -// Lower a map assignment to a function call. - -Statement* -Map_assignment_statement::do_lower(Gogo*, Named_object*, Block* enclosing, - Statement_inserter*) -{ - Location loc = this->location(); - - Map_index_expression* map_index = this->map_index_->map_index_expression(); - if (map_index == NULL) - { - this->report_error(_("expected map index on left hand side")); - return Statement::make_error_statement(loc); - } - Map_type* map_type = map_index->get_map_type(); - if (map_type == NULL) - return Statement::make_error_statement(loc); - - Block* b = new Block(enclosing, loc); - - // Evaluate the map first to get order of evaluation right. - // map_temp := m // we are evaluating m[k] = v, p - Temporary_statement* map_temp = Statement::make_temporary(map_type, - map_index->map(), - loc); - b->add_statement(map_temp); - - // var key_temp MAP_KEY_TYPE = k - Temporary_statement* key_temp = - Statement::make_temporary(map_type->key_type(), map_index->index(), loc); - b->add_statement(key_temp); - - // var val_temp MAP_VAL_TYPE = v - Temporary_statement* val_temp = - Statement::make_temporary(map_type->val_type(), this->val_, loc); - b->add_statement(val_temp); - - // var insert_temp bool = p - Temporary_statement* insert_temp = - Statement::make_temporary(Type::lookup_bool_type(), this->should_set_, - loc); - b->add_statement(insert_temp); - - // mapassign2(map_temp, &key_temp, &val_temp, p) - Expression* p1 = Expression::make_temporary_reference(map_temp, loc); - Expression* ref = Expression::make_temporary_reference(key_temp, loc); - Expression* p2 = Expression::make_unary(OPERATOR_AND, ref, loc); - ref = Expression::make_temporary_reference(val_temp, loc); - Expression* p3 = Expression::make_unary(OPERATOR_AND, ref, loc); - Expression* p4 = Expression::make_temporary_reference(insert_temp, loc); - Expression* call = Runtime::make_call(Runtime::MAPASSIGN2, loc, 4, - p1, p2, p3, p4); - Statement* s = Statement::make_statement(call, true); - b->add_statement(s); - - return Statement::make_block_statement(b, loc); -} - -// Dump the AST representation for a map assignment statement. - -void -Map_assignment_statement::do_dump_statement( - Ast_dump_context* ast_dump_context) const -{ - ast_dump_context->print_indent(); - ast_dump_context->dump_expression(this->map_index_); - ast_dump_context->ostream() << " = "; - ast_dump_context->dump_expression(this->val_); - ast_dump_context->ostream() << ", "; - ast_dump_context->dump_expression(this->should_set_); - ast_dump_context->ostream() << std::endl; -} - -// Make a statement which assigns a pair of entries to a map. - -Statement* -Statement::make_map_assignment(Expression* map_index, - Expression* val, Expression* should_set, - Location location) -{ - return new Map_assignment_statement(map_index, val, should_set, location); -} - // A tuple assignment from a receive statement. class Tuple_receive_assignment_statement : public Statement @@ -1492,7 +1535,7 @@ Tuple_receive_assignment_statement::do_dump_statement( ast_dump_context->dump_expression(this->closed_); ast_dump_context->ostream() << " <- "; ast_dump_context->dump_expression(this->channel_); - ast_dump_context->ostream() << std::endl; + ast_dump_context->ostream() << dsuffix(location()) << std::endl; } // Make a nonblocking receive statement. @@ -1686,7 +1729,7 @@ Tuple_type_guard_assignment_statement::do_dump_statement( ast_dump_context->dump_expression(this->expr_); ast_dump_context->ostream() << " . "; ast_dump_context->dump_type(this->type_); - ast_dump_context->ostream() << std::endl; + ast_dump_context->ostream() << dsuffix(location()) << std::endl; } // Make an assignment from a type guard to a pair of variables. @@ -1772,7 +1815,8 @@ Bstatement* Expression_statement::do_get_backend(Translate_context* context) { Bexpression* bexpr = this->expr_->get_backend(context); - return context->backend()->expression_statement(bexpr); + Bfunction* bfunction = context->function()->func_value()->get_decl(); + return context->backend()->expression_statement(bfunction, bexpr); } // Dump the AST representation for an expression statement @@ -1783,7 +1827,7 @@ Expression_statement::do_dump_statement(Ast_dump_context* ast_dump_context) { ast_dump_context->print_indent(); ast_dump_context->dump_expression(expr_); - ast_dump_context->ostream() << std::endl; + ast_dump_context->ostream() << dsuffix(location()) << std::endl; } // Make an expression statement from an Expression. @@ -1794,40 +1838,6 @@ Statement::make_statement(Expression* expr, bool is_ignored) return new Expression_statement(expr, is_ignored); } -// A block statement--a list of statements which may include variable -// definitions. - -class Block_statement : public Statement -{ - public: - Block_statement(Block* block, Location location) - : Statement(STATEMENT_BLOCK, location), - block_(block) - { } - - protected: - int - do_traverse(Traverse* traverse) - { return this->block_->traverse(traverse); } - - void - do_determine_types() - { this->block_->determine_types(); } - - bool - do_may_fall_through() const - { return this->block_->may_fall_through(); } - - Bstatement* - do_get_backend(Translate_context* context); - - void - do_dump_statement(Ast_dump_context*) const; - - private: - Block* block_; -}; - // Convert a block to the backend representation of a statement. Bstatement* @@ -1907,7 +1917,7 @@ Inc_dec_statement::do_dump_statement(Ast_dump_context* ast_dump_context) const { ast_dump_context->print_indent(); ast_dump_context->dump_expression(expr_); - ast_dump_context->ostream() << (is_inc_? "++": "--") << std::endl; + ast_dump_context->ostream() << (is_inc_? "++": "--") << dsuffix(location()) << std::endl; } // Make an increment statement. @@ -1929,8 +1939,6 @@ Statement::make_dec_statement(Expression* expr) // Class Thunk_statement. This is the base class for go and defer // statements. -Unordered_set(const Struct_type*) Thunk_statement::thunk_types; - // Constructor. Thunk_statement::Thunk_statement(Statement_classification classification, @@ -2313,21 +2321,10 @@ Thunk_statement::build_struct(Function_type* fntype) } Struct_type *st = Type::make_struct_type(fields, location); - - Thunk_statement::thunk_types.insert(st); - + st->set_is_struct_incomparable(); return st; } -// Return whether ST is a type created to hold thunk parameters. - -bool -Thunk_statement::is_thunk_struct(const Struct_type* st) -{ - return (Thunk_statement::thunk_types.find(st) - != Thunk_statement::thunk_types.end()); -} - // Build the thunk we are going to call. This is a brand new, albeit // artificial, function. @@ -2391,7 +2388,7 @@ Thunk_statement::build_thunk(Gogo* gogo, const std::string& thunk_name) { retaddr_label = gogo->add_label_reference("retaddr", location, false); Expression* arg = Expression::make_label_addr(retaddr_label, location); - Expression* call = Runtime::make_call(Runtime::SET_DEFER_RETADDR, + Expression* call = Runtime::make_call(Runtime::SETDEFERRETADDR, location, 1, arg); // This is a hack to prevent the middle-end from deleting the @@ -2532,7 +2529,9 @@ Thunk_statement::build_thunk(Gogo* gogo, const std::string& thunk_name) gogo->flatten_block(function, b); - if (may_call_recover || recover_arg != NULL) + if (may_call_recover + || recover_arg != NULL + || this->classification() == STATEMENT_GO) { // Dig up the call expression, which may have been changed // during lowering. @@ -2546,6 +2545,8 @@ Thunk_statement::build_thunk(Gogo* gogo, const std::string& thunk_name) { if (may_call_recover) ce->set_is_deferred(); + if (this->classification() == STATEMENT_GO) + ce->set_is_concurrent(); if (recover_arg != NULL) ce->set_recover_arg(recover_arg); } @@ -2596,7 +2597,8 @@ Go_statement::do_get_backend(Translate_context* context) Expression* call = Runtime::make_call(Runtime::GO, this->location(), 2, fn, arg); Bexpression* bcall = call->get_backend(context); - return context->backend()->expression_statement(bcall); + Bfunction* bfunction = context->function()->func_value()->get_decl(); + return context->backend()->expression_statement(bfunction, bcall); } // Dump the AST representation for go statement. @@ -2607,7 +2609,7 @@ Go_statement::do_dump_statement(Ast_dump_context* ast_dump_context) const ast_dump_context->print_indent(); ast_dump_context->ostream() << "go "; ast_dump_context->dump_expression(this->call()); - ast_dump_context->ostream() << std::endl; + ast_dump_context->ostream() << dsuffix(location()) << std::endl; } // Make a go statement. @@ -2631,10 +2633,11 @@ Defer_statement::do_get_backend(Translate_context* context) Location loc = this->location(); Expression* ds = context->function()->func_value()->defer_stack(loc); - Expression* call = Runtime::make_call(Runtime::DEFER, loc, 3, + Expression* call = Runtime::make_call(Runtime::DEFERPROC, loc, 3, ds, fn, arg); Bexpression* bcall = call->get_backend(context); - return context->backend()->expression_statement(bcall); + Bfunction* bfunction = context->function()->func_value()->get_decl(); + return context->backend()->expression_statement(bfunction, bcall); } // Dump the AST representation for defer statement. @@ -2645,7 +2648,7 @@ Defer_statement::do_dump_statement(Ast_dump_context* ast_dump_context) const ast_dump_context->print_indent(); ast_dump_context->ostream() << "defer "; ast_dump_context->dump_expression(this->call()); - ast_dump_context->ostream() << std::endl; + ast_dump_context->ostream() << dsuffix(location()) << std::endl; } // Make a defer statement. @@ -2776,11 +2779,12 @@ Return_statement::do_lower(Gogo*, Named_object* function, Block* enclosing, else { if (reason.empty()) - error_at(e->location(), "incompatible type for return value %d", i); + go_error_at(e->location(), + "incompatible type for return value %d", i); else - error_at(e->location(), - "incompatible type for return value %d (%s)", - i, reason.c_str()); + go_error_at(e->location(), + "incompatible type for return value %d (%s)", + i, reason.c_str()); } } go_assert(lhs->size() == rhs->size()); @@ -2838,7 +2842,7 @@ Return_statement::do_dump_statement(Ast_dump_context* ast_dump_context) const ast_dump_context->print_indent(); ast_dump_context->ostream() << "return " ; ast_dump_context->dump_expression_list(this->vals_); - ast_dump_context->ostream() << std::endl; + ast_dump_context->ostream() << dsuffix(location()) << std::endl; } // Make a return statement. @@ -2921,7 +2925,7 @@ Bc_statement::do_dump_statement(Ast_dump_context* ast_dump_context) const ast_dump_context->ostream() << " "; ast_dump_context->dump_label_name(this->label_); } - ast_dump_context->ostream() << std::endl; + ast_dump_context->ostream() << dsuffix(location()) << std::endl; } // Make a break statement. @@ -2941,37 +2945,13 @@ Statement::make_continue_statement(Unnamed_label* label, return new Bc_statement(false, label, location); } -// A goto statement. +// Class Goto_statement. -class Goto_statement : public Statement +int +Goto_statement::do_traverse(Traverse*) { - public: - Goto_statement(Label* label, Location location) - : Statement(STATEMENT_GOTO, location), - label_(label) - { } - - protected: - int - do_traverse(Traverse*) - { return TRAVERSE_CONTINUE; } - - void - do_check_types(Gogo*); - - bool - do_may_fall_through() const - { return false; } - - Bstatement* - do_get_backend(Translate_context*); - - void - do_dump_statement(Ast_dump_context*) const; - - private: - Label* label_; -}; + return TRAVERSE_CONTINUE; +} // Check types for a label. There aren't any types per se, but we use // this to give an error if the label was never defined. @@ -2981,8 +2961,8 @@ Goto_statement::do_check_types(Gogo*) { if (!this->label_->is_defined()) { - error_at(this->location(), "reference to undefined label %qs", - Gogo::message_name(this->label_->name()).c_str()); + go_error_at(this->location(), "reference to undefined label %qs", + Gogo::message_name(this->label_->name()).c_str()); this->set_is_error(); } } @@ -3002,7 +2982,7 @@ void Goto_statement::do_dump_statement(Ast_dump_context* ast_dump_context) const { ast_dump_context->print_indent(); - ast_dump_context->ostream() << "goto " << this->label_->name() << std::endl; + ast_dump_context->ostream() << "goto " << this->label_->name() << dsuffix(location()) << std::endl; } // Make a goto statement. @@ -3013,35 +2993,21 @@ Statement::make_goto_statement(Label* label, Location location) return new Goto_statement(label, location); } -// A goto statement to an unnamed label. +// Class Goto_unnamed_statement. -class Goto_unnamed_statement : public Statement +int +Goto_unnamed_statement::do_traverse(Traverse*) { - public: - Goto_unnamed_statement(Unnamed_label* label, Location location) - : Statement(STATEMENT_GOTO_UNNAMED, location), - label_(label) - { } - - protected: - int - do_traverse(Traverse*) - { return TRAVERSE_CONTINUE; } - - bool - do_may_fall_through() const - { return false; } - - Bstatement* - do_get_backend(Translate_context* context) - { return this->label_->get_goto(context, this->location()); } + return TRAVERSE_CONTINUE; +} - void - do_dump_statement(Ast_dump_context*) const; +// Convert the goto unnamed statement to the backend representation. - private: - Unnamed_label* label_; -}; +Bstatement* +Goto_unnamed_statement::do_get_backend(Translate_context* context) +{ + return this->label_->get_goto(context, this->location()); +} // Dump the AST representation for an unnamed goto statement @@ -3052,7 +3018,7 @@ Goto_unnamed_statement::do_dump_statement( ast_dump_context->print_indent(); ast_dump_context->ostream() << "goto "; ast_dump_context->dump_label_name(this->label_); - ast_dump_context->ostream() << std::endl; + ast_dump_context->ostream() << dsuffix(location()) << std::endl; } // Make a goto statement to an unnamed label. @@ -3083,7 +3049,8 @@ Label_statement::do_get_backend(Translate_context* context) if (this->label_->is_dummy_label()) { Bexpression* bce = context->backend()->boolean_constant_expression(false); - return context->backend()->expression_statement(bce); + Bfunction* bfunction = context->function()->func_value()->get_decl(); + return context->backend()->expression_statement(bfunction, bce); } Blabel* blabel = this->label_->get_backend_label(context); return context->backend()->label_definition_statement(blabel); @@ -3095,7 +3062,7 @@ void Label_statement::do_dump_statement(Ast_dump_context* ast_dump_context) const { ast_dump_context->print_indent(); - ast_dump_context->ostream() << this->label_->name() << ":" << std::endl; + ast_dump_context->ostream() << this->label_->name() << ":" << dsuffix(location()) << std::endl; } // Make a label statement. @@ -3106,32 +3073,26 @@ Statement::make_label_statement(Label* label, Location location) return new Label_statement(label, location); } -// An unnamed label statement. - -class Unnamed_label_statement : public Statement -{ - public: - Unnamed_label_statement(Unnamed_label* label) - : Statement(STATEMENT_UNNAMED_LABEL, label->location()), - label_(label) - { } +// Class Unnamed_label_statement. - protected: - int - do_traverse(Traverse*) - { return TRAVERSE_CONTINUE; } +Unnamed_label_statement::Unnamed_label_statement(Unnamed_label* label) + : Statement(STATEMENT_UNNAMED_LABEL, label->location()), + label_(label) +{ } - Bstatement* - do_get_backend(Translate_context* context) - { return this->label_->get_definition(context); } +int +Unnamed_label_statement::do_traverse(Traverse*) +{ + return TRAVERSE_CONTINUE; +} - void - do_dump_statement(Ast_dump_context*) const; +// Get the backend definition for this unnamed label statement. - private: - // The label. - Unnamed_label* label_; -}; +Bstatement* +Unnamed_label_statement::do_get_backend(Translate_context* context) +{ + return this->label_->get_definition(context); +} // Dump the AST representation for an unnamed label definition statement. @@ -3141,7 +3102,7 @@ Unnamed_label_statement::do_dump_statement(Ast_dump_context* ast_dump_context) { ast_dump_context->print_indent(); ast_dump_context->dump_label_name(this->label_); - ast_dump_context->ostream() << ":" << std::endl; + ast_dump_context->ostream() << ":" << dsuffix(location()) << std::endl; } // Make an unnamed label statement. @@ -3214,7 +3175,9 @@ If_statement::do_get_backend(Translate_context* context) Bblock* else_block = (this->else_block_ == NULL ? NULL : this->else_block_->get_backend(context)); - return context->backend()->if_statement(cond, then_block, else_block, + Bfunction* bfunction = context->function()->func_value()->get_decl(); + return context->backend()->if_statement(bfunction, + cond, then_block, else_block, this->location()); } @@ -3226,7 +3189,7 @@ If_statement::do_dump_statement(Ast_dump_context* ast_dump_context) const ast_dump_context->print_indent(); ast_dump_context->ostream() << "if "; ast_dump_context->dump_expression(this->cond_); - ast_dump_context->ostream() << std::endl; + ast_dump_context->ostream() << dsuffix(location()) << std::endl; if (ast_dump_context->dump_subblocks()) { ast_dump_context->dump_block(this->then_block_); @@ -3425,8 +3388,8 @@ Case_clauses::Case_clause::check_types(Type* type) if (!Type::are_assignable(type, (*p)->type(), NULL) && !Type::are_assignable((*p)->type(), type, NULL)) { - error_at((*p)->location(), - "type mismatch between switch value and case clause"); + go_error_at((*p)->location(), + "type mismatch between switch value and case clause"); return false; } } @@ -3489,7 +3452,7 @@ Case_clauses::Case_clause::get_backend(Translate_context* context, if (!ins.second) { // Value was already present. - error_at(this->location_, "duplicate case in switch"); + go_error_at(this->location_, "duplicate case in switch"); e = Expression::make_error(this->location_); } cases->push_back(e->get_backend(context)); @@ -3540,7 +3503,7 @@ Case_clauses::Case_clause::dump_clause(Ast_dump_context* ast_dump_context) if (this->is_fallthrough_) { ast_dump_context->print_indent(); - ast_dump_context->ostream() << " (fallthrough)" << std::endl; + ast_dump_context->ostream() << " (fallthrough)" << dsuffix(location()) << std::endl; } } @@ -3873,8 +3836,8 @@ Switch_statement::do_lower(Gogo*, Named_object*, Block* enclosing, && !Type::are_compatible_for_comparison(true, this->val_->type(), Type::make_nil_type(), NULL)) { - error_at(this->val_->location(), - "cannot switch on value whose type that may not be compared"); + go_error_at(this->val_->location(), + "cannot switch on value whose type that may not be compared"); return Statement::make_error_statement(loc); } @@ -3931,7 +3894,7 @@ Switch_statement::do_dump_statement(Ast_dump_context* ast_dump_context) const } if (ast_dump_context->dump_subblocks()) { - ast_dump_context->ostream() << " {" << std::endl; + ast_dump_context->ostream() << " {" << dsuffix(location()) << std::endl; this->clauses_->dump_clauses(ast_dump_context); ast_dump_context->print_indent(); ast_dump_context->ostream() << "}"; @@ -4008,10 +3971,10 @@ Type_case_clauses::Type_case_clause::lower(Type* switch_val_type, &reason)) { if (reason.empty()) - error_at(this->location_, "impossible type switch case"); + go_error_at(this->location_, "impossible type switch case"); else - error_at(this->location_, "impossible type switch case (%s)", - reason.c_str()); + go_error_at(this->location_, "impossible type switch case (%s)", + reason.c_str()); } Expression* ref = Expression::make_temporary_reference(descriptor_temp, @@ -4028,7 +3991,7 @@ Type_case_clauses::Type_case_clause::lower(Type* switch_val_type, else cond = Runtime::make_call((type->interface_type() == NULL ? Runtime::IFACETYPEEQ - : Runtime::IFACEI2TP), + : Runtime::IFACET2IP), loc, 2, Expression::make_type_descriptor(type, loc), ref); @@ -4176,7 +4139,7 @@ Type_case_clauses::check_duplicates() const t = Type::make_nil_type(); std::pair<Types_seen::iterator, bool> ins = types_seen.insert(t); if (!ins.second) - error_at(p->location(), "duplicate type in switch"); + go_error_at(p->location(), "duplicate type in switch"); } } @@ -4351,7 +4314,7 @@ Type_switch_statement::do_dump_statement(Ast_dump_context* ast_dump_context) ast_dump_context->ostream() << " .(type)"; if (ast_dump_context->dump_subblocks()) { - ast_dump_context->ostream() << " {" << std::endl; + ast_dump_context->ostream() << " {" << dsuffix(location()) << std::endl; this->clauses_->dump_clauses(ast_dump_context); ast_dump_context->ostream() << "}"; } @@ -4406,7 +4369,7 @@ Send_statement::do_check_types(Gogo*) Channel_type* channel_type = type->channel_type(); if (channel_type == NULL) { - error_at(this->location(), "left operand of %<<-%> must be channel"); + go_error_at(this->location(), "left operand of %<<-%> must be channel"); this->set_is_error(); return; } @@ -4464,7 +4427,6 @@ Send_statement::do_get_backend(Translate_context* context) element_type, this->val_, loc); - bool is_small; bool can_take_address; switch (element_type->base()->classification()) { @@ -4474,25 +4436,18 @@ Send_statement::do_get_backend(Translate_context* context) case Type::TYPE_POINTER: case Type::TYPE_MAP: case Type::TYPE_CHANNEL: - is_small = true; - can_take_address = false; - break; - case Type::TYPE_FLOAT: case Type::TYPE_COMPLEX: case Type::TYPE_STRING: case Type::TYPE_INTERFACE: - is_small = false; can_take_address = false; break; case Type::TYPE_STRUCT: - is_small = false; can_take_address = true; break; case Type::TYPE_ARRAY: - is_small = false; can_take_address = !element_type->is_slice_type(); break; @@ -4518,28 +4473,19 @@ Send_statement::do_get_backend(Translate_context* context) Expression* td = Expression::make_type_descriptor(this->channel_->type(), loc); - Runtime::Function code; Bstatement* btemp = NULL; - if (is_small) - { - // Type is small enough to handle as uint64. - code = Runtime::SEND_SMALL; - val = Expression::make_unsafe_cast(Type::lookup_integer_type("uint64"), - val, loc); - } - else if (can_take_address) - { - // Must pass address of value. The function doesn't change the - // value, so just take its address directly. - code = Runtime::SEND_BIG; + if (can_take_address) + { + // The function doesn't change the value, so just take its + // address directly. val = Expression::make_unary(OPERATOR_AND, val, loc); } else { - // Must pass address of value, but the value is small enough - // that it might be in registers. Copy value into temporary - // variable to take address. - code = Runtime::SEND_BIG; + // The value is not in a variable, or is small enough that it + // might be in a register, and taking the address would push it + // on the stack. Copy it into a temporary variable to take the + // address. Temporary_statement* temp = Statement::make_temporary(element_type, val, loc); Expression* ref = Expression::make_temporary_reference(temp, loc); @@ -4547,11 +4493,13 @@ Send_statement::do_get_backend(Translate_context* context) btemp = temp->get_backend(context); } - Expression* call = Runtime::make_call(code, loc, 3, td, this->channel_, val); + Expression* call = Runtime::make_call(Runtime::CHANSEND, loc, 3, td, + this->channel_, val); context->gogo()->lower_expression(context->function(), NULL, &call); Bexpression* bcall = call->get_backend(context); - Bstatement* s = context->backend()->expression_statement(bcall); + Bfunction* bfunction = context->function()->func_value()->get_decl(); + Bstatement* s = context->backend()->expression_statement(bfunction, bcall); if (btemp == NULL) return s; @@ -4568,7 +4516,7 @@ Send_statement::do_dump_statement(Ast_dump_context* ast_dump_context) const ast_dump_context->dump_expression(this->channel_); ast_dump_context->ostream() << " <- "; ast_dump_context->dump_expression(this->val_); - ast_dump_context->ostream() << std::endl; + ast_dump_context->ostream() << dsuffix(location()) << std::endl; } // Make a send statement. @@ -4625,6 +4573,7 @@ Select_clauses::Select_clause::lower(Gogo* gogo, Named_object* function, Location loc = this->location_; Expression* selref = Expression::make_temporary_reference(sel, loc); + selref = Expression::make_unary(OPERATOR_AND, selref, loc); Expression* index_expr = Expression::make_integer_ul(this->index_, NULL, loc); @@ -4807,30 +4756,14 @@ Select_clauses::Select_clause::check_types() Channel_type* ct = this->channel_->type()->channel_type(); if (ct == NULL) { - error_at(this->channel_->location(), "expected channel"); + go_error_at(this->channel_->location(), "expected channel"); return; } if (this->is_send_ && !ct->may_send()) - error_at(this->location(), "invalid send on receive-only channel"); + go_error_at(this->location(), "invalid send on receive-only channel"); else if (!this->is_send_ && !ct->may_receive()) - error_at(this->location(), "invalid receive on send-only channel"); -} - -// Analyze the dataflow across each case statement. - -void -Select_clauses::Select_clause::analyze_dataflow(Dataflow* dataflow) -{ - if (this->is_default_) - return; - - // For a CommClause, the dataflow analysis should record a definition of - // VAR and CLOSEDVAR - if (this->var_ != NULL && !this->var_->is_sink()) - dataflow->add_def(this->var_, this->channel_, NULL, false); - if (this->closedvar_ != NULL && !this->closedvar_->is_sink()) - dataflow->add_def(this->closedvar_, this->channel_, NULL, false); + go_error_at(this->location(), "invalid receive on send-only channel"); } // Whether this clause may fall through to the statement which follows @@ -4951,17 +4884,6 @@ Select_clauses::check_types() p->check_types(); } -// Analyze the dataflow across each case statement. - -void -Select_clauses::analyze_dataflow(Dataflow* dataflow) -{ - for (Clauses::iterator p = this->clauses_.begin(); - p != this->clauses_.end(); - ++p) - p->analyze_dataflow(dataflow); -} - // Return whether these select clauses fall through to the statement // following the overall select statement. @@ -5011,17 +4933,21 @@ Select_clauses::get_backend(Translate_context* context, if (s == NULL) clauses[i] = g; else - clauses[i] = context->backend()->compound_statement(s, g); + clauses[i] = context->backend()->compound_statement(s, g); } Expression* selref = Expression::make_temporary_reference(sel, location); + selref = Expression::make_unary(OPERATOR_AND, selref, location); Expression* call = Runtime::make_call(Runtime::SELECTGO, location, 1, selref); context->gogo()->lower_expression(context->function(), NULL, &call); Bexpression* bcall = call->get_backend(context); if (count == 0) - return context->backend()->expression_statement(bcall); + { + Bfunction* bfunction = context->function()->func_value()->get_decl(); + return context->backend()->expression_statement(bfunction, bcall); + } std::vector<Bstatement*> statements; statements.reserve(2); @@ -5081,13 +5007,27 @@ Select_statement::do_lower(Gogo* gogo, Named_object* function, go_assert(this->sel_ == NULL); - Expression* size_expr = Expression::make_integer_ul(this->clauses_->size(), - NULL, loc); - Expression* call = Runtime::make_call(Runtime::NEWSELECT, loc, 1, size_expr); - - this->sel_ = Statement::make_temporary(NULL, call, loc); + int ncases = this->clauses_->size(); + Type* selstruct_type = Channel_type::select_type(ncases); + this->sel_ = Statement::make_temporary(selstruct_type, NULL, loc); b->add_statement(this->sel_); + int64_t selstruct_size; + if (!selstruct_type->backend_type_size(gogo, &selstruct_size)) + { + go_assert(saw_errors()); + return Statement::make_error_statement(loc); + } + + Expression* ref = Expression::make_temporary_reference(this->sel_, loc); + ref = Expression::make_unary(OPERATOR_AND, ref, loc); + Expression* selstruct_size_expr = + Expression::make_integer_int64(selstruct_size, NULL, loc); + Expression* size_expr = Expression::make_integer_ul(ncases, NULL, loc); + Expression* call = Runtime::make_call(Runtime::NEWSELECT, loc, 3, + ref, selstruct_size_expr, size_expr); + b->add_statement(Statement::make_statement(call, true)); + this->clauses_->lower(gogo, function, b, this->sel_); this->is_lowered_ = true; b->add_statement(this); @@ -5126,7 +5066,7 @@ Select_statement::do_dump_statement(Ast_dump_context* ast_dump_context) const ast_dump_context->ostream() << "select"; if (ast_dump_context->dump_subblocks()) { - ast_dump_context->ostream() << " {" << std::endl; + ast_dump_context->ostream() << " {" << dsuffix(location()) << std::endl; this->clauses_->dump_clauses(ast_dump_context); ast_dump_context->ostream() << "}"; } @@ -5192,6 +5132,7 @@ For_statement::do_lower(Gogo*, Named_object*, Block* enclosing, } Unnamed_label* top = new Unnamed_label(this->location()); + top->set_derived_from(this); b->add_statement(Statement::make_unnamed_label_statement(top)); s = Statement::make_block_statement(this->statements_, @@ -5233,7 +5174,9 @@ For_statement::do_lower(Gogo*, Named_object*, Block* enclosing, b->set_end_location(end_loc); - return Statement::make_block_statement(b, loc); + Statement* bs = Statement::make_block_statement(b, loc); + bs->block_statement()->set_is_lowered_for_statement(); + return bs; } // Return the break label, creating it if necessary. @@ -5316,7 +5259,7 @@ For_statement::do_dump_statement(Ast_dump_context* ast_dump_context) const ast_dump_context->ostream() << "}"; } - ast_dump_context->ostream() << std::endl; + ast_dump_context->ostream() << dsuffix(location()) << std::endl; } // Make a for statement. @@ -5376,7 +5319,7 @@ For_range_statement::do_lower(Gogo* gogo, Named_object*, Block* enclosing, else if (range_type->is_string_type()) { index_type = Type::lookup_integer_type("int"); - value_type = Type::lookup_integer_type("int32"); + value_type = gogo->lookup_global("rune")->type_value(); } else if (range_type->map_type() != NULL) { @@ -5455,9 +5398,9 @@ For_range_statement::do_lower(Gogo* gogo, Named_object*, Block* enclosing, index_temp, value_temp, &init, &cond, &iter_init, &post); else if (range_type->map_type() != NULL) - this->lower_range_map(gogo, temp_block, body, range_object, range_temp, - index_temp, value_temp, &init, &cond, &iter_init, - &post); + this->lower_range_map(gogo, range_type->map_type(), temp_block, body, + range_object, range_temp, index_temp, value_temp, + &init, &cond, &iter_init, &post); else if (range_type->channel_type() != NULL) this->lower_range_channel(gogo, temp_block, body, range_object, range_temp, index_temp, value_temp, &init, &cond, &iter_init, @@ -5521,7 +5464,7 @@ For_range_statement::make_range_ref(Named_object* range_object, // Return a call to the predeclared function FUNCNAME passing a // reference to the temporary variable ARG. -Expression* +Call_expression* For_range_statement::call_builtin(Gogo* gogo, const char* funcname, Expression* arg, Location loc) @@ -5727,7 +5670,7 @@ For_range_statement::lower_range_slice(Gogo* gogo, // Lower a for range over a string. void -For_range_statement::lower_range_string(Gogo*, +For_range_statement::lower_range_string(Gogo* gogo, Block* enclosing, Block* body_block, Named_object* range_object, @@ -5742,94 +5685,121 @@ For_range_statement::lower_range_string(Gogo*, Location loc = this->location(); // The loop we generate: + // len_temp := len(range) // var next_index_temp int - // for index_temp = 0; ; index_temp = next_index_temp { - // next_index_temp, value_temp = stringiter2(range, index_temp) - // if next_index_temp == 0 { - // break + // for index_temp = 0; index_temp < len_temp; index_temp = next_index_temp { + // value_temp = rune(range[index_temp]) + // if value_temp < utf8.RuneSelf { + // next_index_temp = index_temp + 1 + // } else { + // value_temp, next_index_temp = decoderune(range, index_temp) // } // index = index_temp // value = value_temp - // original body + // // original body // } // Set *PINIT to + // len_temp := len(range) // var next_index_temp int // index_temp = 0 + // var value_temp rune // if value_temp not passed in Block* init = new Block(enclosing, loc); + Expression* ref = this->make_range_ref(range_object, range_temp, loc); + Call_expression* call = this->call_builtin(gogo, "len", ref, loc); + Temporary_statement* len_temp = + Statement::make_temporary(index_temp->type(), call, loc); + init->add_statement(len_temp); + Temporary_statement* next_index_temp = Statement::make_temporary(index_temp->type(), NULL, loc); init->add_statement(next_index_temp); - Expression* zexpr = Expression::make_integer_ul(0, NULL, loc); - - Temporary_reference_expression* ref = + Temporary_reference_expression* index_ref = Expression::make_temporary_reference(index_temp, loc); - ref->set_is_lvalue(); - Statement* s = Statement::make_assignment(ref, zexpr, loc); - + index_ref->set_is_lvalue(); + Expression* zexpr = Expression::make_integer_ul(0, index_temp->type(), loc); + Statement* s = Statement::make_assignment(index_ref, zexpr, loc); init->add_statement(s); + + Type* rune_type; + if (value_temp != NULL) + rune_type = value_temp->type(); + else + { + rune_type = gogo->lookup_global("rune")->type_value(); + value_temp = Statement::make_temporary(rune_type, NULL, loc); + init->add_statement(value_temp); + } + *pinit = init; - // The loop has no condition. + // Set *PCOND to + // index_temp < len_temp - *pcond = NULL; + index_ref = Expression::make_temporary_reference(index_temp, loc); + Expression* len_ref = + Expression::make_temporary_reference(len_temp, loc); + *pcond = Expression::make_binary(OPERATOR_LT, index_ref, len_ref, loc); // Set *PITER_INIT to - // next_index_temp = runtime.stringiter(range, index_temp) - // or - // next_index_temp, value_temp = runtime.stringiter2(range, index_temp) - // followed by - // if next_index_temp == 0 { - // break + // value_temp = rune(range[index_temp]) + // if value_temp < utf8.RuneSelf { + // next_index_temp = index_temp + 1 + // } else { + // value_temp, next_index_temp = decoderune(range, index_temp) // } Block* iter_init = new Block(body_block, loc); - Expression* p1 = this->make_range_ref(range_object, range_temp, loc); - Expression* p2 = Expression::make_temporary_reference(index_temp, loc); - Call_expression* call = Runtime::make_call((value_temp == NULL - ? Runtime::STRINGITER - : Runtime::STRINGITER2), - loc, 2, p1, p2); + ref = this->make_range_ref(range_object, range_temp, loc); + index_ref = Expression::make_temporary_reference(index_temp, loc); + ref = Expression::make_string_index(ref, index_ref, NULL, loc); + ref = Expression::make_cast(rune_type, ref, loc); + Temporary_reference_expression* value_ref = + Expression::make_temporary_reference(value_temp, loc); + value_ref->set_is_lvalue(); + s = Statement::make_assignment(value_ref, ref, loc); + iter_init->add_statement(s); - if (value_temp == NULL) - { - ref = Expression::make_temporary_reference(next_index_temp, loc); - ref->set_is_lvalue(); - s = Statement::make_assignment(ref, call, loc); - } - else - { - Expression_list* lhs = new Expression_list(); + value_ref = Expression::make_temporary_reference(value_temp, loc); + Expression* rune_self = Expression::make_integer_ul(0x80, rune_type, loc); + Expression* cond = Expression::make_binary(OPERATOR_LT, value_ref, rune_self, + loc); - ref = Expression::make_temporary_reference(next_index_temp, loc); - ref->set_is_lvalue(); - lhs->push_back(ref); + Block* then_block = new Block(iter_init, loc); - ref = Expression::make_temporary_reference(value_temp, loc); - ref->set_is_lvalue(); - lhs->push_back(ref); + Temporary_reference_expression* lhs = + Expression::make_temporary_reference(next_index_temp, loc); + lhs->set_is_lvalue(); + index_ref = Expression::make_temporary_reference(index_temp, loc); + Expression* one = Expression::make_integer_ul(1, index_temp->type(), loc); + Expression* sum = Expression::make_binary(OPERATOR_PLUS, index_ref, one, + loc); + s = Statement::make_assignment(lhs, sum, loc); + then_block->add_statement(s); - Expression_list* rhs = new Expression_list(); - rhs->push_back(Expression::make_call_result(call, 0)); - rhs->push_back(Expression::make_call_result(call, 1)); + Block* else_block = new Block(iter_init, loc); - s = Statement::make_tuple_assignment(lhs, rhs, loc); - } - iter_init->add_statement(s); + ref = this->make_range_ref(range_object, range_temp, loc); + index_ref = Expression::make_temporary_reference(index_temp, loc); + call = Runtime::make_call(Runtime::DECODERUNE, loc, 2, ref, index_ref); - ref = Expression::make_temporary_reference(next_index_temp, loc); - zexpr = Expression::make_integer_ul(0, NULL, loc); - Expression* equals = Expression::make_binary(OPERATOR_EQEQ, ref, zexpr, loc); + value_ref = Expression::make_temporary_reference(value_temp, loc); + value_ref->set_is_lvalue(); + Expression* res = Expression::make_call_result(call, 0); + s = Statement::make_assignment(value_ref, res, loc); + else_block->add_statement(s); - Block* then_block = new Block(iter_init, loc); - s = Statement::make_break_statement(this->break_label(), loc); - then_block->add_statement(s); + lhs = Expression::make_temporary_reference(next_index_temp, loc); + lhs->set_is_lvalue(); + res = Expression::make_call_result(call, 1); + s = Statement::make_assignment(lhs, res, loc); + else_block->add_statement(s); - s = Statement::make_if_statement(equals, then_block, NULL, loc); + s = Statement::make_if_statement(cond, then_block, else_block, loc); iter_init->add_statement(s); *piter_init = iter_init; @@ -5839,11 +5809,10 @@ For_range_statement::lower_range_string(Gogo*, Block* post = new Block(enclosing, loc); - Temporary_reference_expression* lhs = - Expression::make_temporary_reference(index_temp, loc); - lhs->set_is_lvalue(); - Expression* rhs = Expression::make_temporary_reference(next_index_temp, loc); - s = Statement::make_assignment(lhs, rhs, loc); + index_ref = Expression::make_temporary_reference(index_temp, loc); + index_ref->set_is_lvalue(); + ref = Expression::make_temporary_reference(next_index_temp, loc); + s = Statement::make_assignment(index_ref, ref, loc); post->add_statement(s); *ppost = post; @@ -5852,7 +5821,8 @@ For_range_statement::lower_range_string(Gogo*, // Lower a for range over a map. void -For_range_statement::lower_range_map(Gogo*, +For_range_statement::lower_range_map(Gogo* gogo, + Map_type* map_type, Block* enclosing, Block* body_block, Named_object* range_object, @@ -5867,13 +5837,13 @@ For_range_statement::lower_range_map(Gogo*, Location loc = this->location(); // The runtime uses a struct to handle ranges over a map. The - // struct is four pointers long. The first pointer is NULL when we - // have completed the iteration. + // struct is built by Map_type::hiter_type for a specific map type. // The loop we generate: // var hiter map_iteration_struct - // for mapiterinit(range, &hiter); hiter[0] != nil; mapiternext(&hiter) { - // mapiter2(hiter, &index_temp, &value_temp) + // for mapiterinit(type, range, &hiter); hiter.key != nil; mapiternext(&hiter) { + // index_temp = *hiter.key + // value_temp = *hiter.val // index = index_temp // value = value_temp // original body @@ -5881,54 +5851,57 @@ For_range_statement::lower_range_map(Gogo*, // Set *PINIT to // var hiter map_iteration_struct - // runtime.mapiterinit(range, &hiter) + // runtime.mapiterinit(type, range, &hiter) Block* init = new Block(enclosing, loc); - Type* map_iteration_type = Runtime::map_iteration_type(); + Type* map_iteration_type = map_type->hiter_type(gogo); Temporary_statement* hiter = Statement::make_temporary(map_iteration_type, NULL, loc); init->add_statement(hiter); - Expression* p1 = this->make_range_ref(range_object, range_temp, loc); + Expression* p1 = Expression::make_type_descriptor(map_type, loc); + Expression* p2 = this->make_range_ref(range_object, range_temp, loc); Expression* ref = Expression::make_temporary_reference(hiter, loc); - Expression* p2 = Expression::make_unary(OPERATOR_AND, ref, loc); - Expression* call = Runtime::make_call(Runtime::MAPITERINIT, loc, 2, p1, p2); + Expression* p3 = Expression::make_unary(OPERATOR_AND, ref, loc); + Expression* call = Runtime::make_call(Runtime::MAPITERINIT, loc, 3, + p1, p2, p3); init->add_statement(Statement::make_statement(call, true)); *pinit = init; // Set *PCOND to - // hiter[0] != nil + // hiter.key != nil ref = Expression::make_temporary_reference(hiter, loc); - Expression* zexpr = Expression::make_integer_ul(0, NULL, loc); - Expression* index = Expression::make_index(ref, zexpr, NULL, NULL, loc); - Expression* ne = Expression::make_binary(OPERATOR_NOTEQ, index, + ref = Expression::make_field_reference(ref, 0, loc); + Expression* ne = Expression::make_binary(OPERATOR_NOTEQ, ref, Expression::make_nil(loc), loc); *pcond = ne; // Set *PITER_INIT to - // mapiter1(hiter, &index_temp) - // or - // mapiter2(hiter, &index_temp, &value_temp) + // index_temp = *hiter.key + // value_temp = *hiter.val Block* iter_init = new Block(body_block, loc); - ref = Expression::make_temporary_reference(hiter, loc); - p1 = Expression::make_unary(OPERATOR_AND, ref, loc); - ref = Expression::make_temporary_reference(index_temp, loc); - p2 = Expression::make_unary(OPERATOR_AND, ref, loc); - if (value_temp == NULL) - call = Runtime::make_call(Runtime::MAPITER1, loc, 2, p1, p2); - else + Expression* lhs = Expression::make_temporary_reference(index_temp, loc); + Expression* rhs = Expression::make_temporary_reference(hiter, loc); + rhs = Expression::make_field_reference(ref, 0, loc); + rhs = Expression::make_unary(OPERATOR_MULT, ref, loc); + Statement* set = Statement::make_assignment(lhs, rhs, loc); + iter_init->add_statement(set); + + if (value_temp != NULL) { - ref = Expression::make_temporary_reference(value_temp, loc); - Expression* p3 = Expression::make_unary(OPERATOR_AND, ref, loc); - call = Runtime::make_call(Runtime::MAPITER2, loc, 3, p1, p2, p3); + lhs = Expression::make_temporary_reference(value_temp, loc); + rhs = Expression::make_temporary_reference(hiter, loc); + rhs = Expression::make_field_reference(rhs, 1, loc); + rhs = Expression::make_unary(OPERATOR_MULT, rhs, loc); + set = Statement::make_assignment(lhs, rhs, loc); + iter_init->add_statement(set); } - iter_init->add_statement(Statement::make_statement(call, true)); *piter_init = iter_init; @@ -6064,7 +6037,7 @@ For_range_statement::do_dump_statement(Ast_dump_context* ast_dump_context) const ast_dump_context->print_indent(); ast_dump_context->ostream() << "}"; } - ast_dump_context->ostream() << std::endl; + ast_dump_context->ostream() << dsuffix(location()) << std::endl; } // Make a for statement with a range clause. diff --git a/gcc/go/gofrontend/statements.h b/gcc/go/gofrontend/statements.h index cf847d04d1..dac99de4ca 100644 --- a/gcc/go/gofrontend/statements.h +++ b/gcc/go/gofrontend/statements.h @@ -19,9 +19,13 @@ class Assignment_statement; class Temporary_statement; class Variable_declaration_statement; class Expression_statement; +class Block_statement; class Return_statement; class Thunk_statement; +class Goto_statement; +class Goto_unnamed_statement; class Label_statement; +class Unnamed_label_statement; class If_statement; class For_statement; class For_range_statement; @@ -47,7 +51,6 @@ class Bexpression; class Bstatement; class Bvariable; class Ast_dump_context; -class Dataflow; // This class is used to traverse assignments made by a statement // which makes assignments. @@ -117,7 +120,6 @@ class Statement STATEMENT_ASSIGNMENT_OPERATION, STATEMENT_TUPLE_ASSIGNMENT, STATEMENT_TUPLE_MAP_ASSIGNMENT, - STATEMENT_MAP_ASSIGNMENT, STATEMENT_TUPLE_RECEIVE_ASSIGNMENT, STATEMENT_TUPLE_TYPE_GUARD_ASSIGNMENT, STATEMENT_INCDEC, @@ -163,11 +165,6 @@ class Statement make_tuple_map_assignment(Expression* val, Expression* present, Expression*, Location); - // Make a statement which assigns a pair of values to a map. - static Statement* - make_map_assignment(Expression*, Expression* val, - Expression* should_set, Location); - // Make an assignment from a nonblocking receive to a pair of // variables. static Statement* @@ -367,6 +364,12 @@ class Statement return this->convert<Expression_statement, STATEMENT_EXPRESSION>(); } + // If this is an block statement, return it. Otherwise return + // NULL. + Block_statement* + block_statement() + { return this->convert<Block_statement, STATEMENT_BLOCK>(); } + // If this is a return statement, return it. Otherwise return NULL. Return_statement* return_statement() @@ -377,11 +380,26 @@ class Statement Thunk_statement* thunk_statement(); + // If this is a goto statement, return it. Otherwise return NULL. + Goto_statement* + goto_statement() + { return this->convert<Goto_statement, STATEMENT_GOTO>(); } + + // If this is a goto_unnamed statement, return it. Otherwise return NULL. + Goto_unnamed_statement* + goto_unnamed_statement() + { return this->convert<Goto_unnamed_statement, STATEMENT_GOTO_UNNAMED>(); } + // If this is a label statement, return it. Otherwise return NULL. Label_statement* label_statement() { return this->convert<Label_statement, STATEMENT_LABEL>(); } + // If this is an unnamed_label statement, return it. Otherwise return NULL. + Unnamed_label_statement* + unnamed_label_statement() + { return this->convert<Unnamed_label_statement, STATEMENT_UNNAMED_LABEL>(); } + // If this is an if statement, return it. Otherwise return NULL. If_statement* if_statement() @@ -562,6 +580,9 @@ class Assignment_statement : public Statement bool do_traverse_assignments(Traverse_assignments*); + virtual Statement* + do_lower(Gogo*, Named_object*, Block*, Statement_inserter*); + void do_determine_types(); @@ -763,6 +784,50 @@ class Expression_statement : public Statement bool is_ignored_; }; +// A block statement--a list of statements which may include variable +// definitions. + +class Block_statement : public Statement +{ + public: + Block_statement(Block* block, Location location) + : Statement(STATEMENT_BLOCK, location), + block_(block), is_lowered_for_statement_(false) + { } + + void + set_is_lowered_for_statement() + { this->is_lowered_for_statement_ = true; } + + bool + is_lowered_for_statement() + { return this->is_lowered_for_statement_; } + + protected: + int + do_traverse(Traverse* traverse) + { return this->block_->traverse(traverse); } + + void + do_determine_types() + { this->block_->determine_types(); } + + bool + do_may_fall_through() const + { return this->block_->may_fall_through(); } + + Bstatement* + do_get_backend(Translate_context* context); + + void + do_dump_statement(Ast_dump_context*) const; + + private: + Block* block_; + // True if this block statement represents a lowered for statement. + bool is_lowered_for_statement_; +}; + // A send statement. class Send_statement : public Statement @@ -860,10 +925,6 @@ class Select_clauses void check_types(); - // Analyze the dataflow across each case statement. - void - analyze_dataflow(Dataflow*); - // Whether the select clauses may fall through to the statement // which follows the overall select statement. bool @@ -920,10 +981,6 @@ class Select_clauses void check_types(); - // Analyze the dataflow across each case statement. - void - analyze_dataflow(Dataflow*); - // Return true if this is the default clause. bool is_default() const @@ -1030,10 +1087,6 @@ class Select_statement : public Statement Unnamed_label* break_label(); - void - analyze_dataflow(Dataflow* dataflow) - { this->clauses_->analyze_dataflow(dataflow); } - protected: int do_traverse(Traverse* traverse) @@ -1088,10 +1141,6 @@ class Thunk_statement : public Statement bool simplify_statement(Gogo*, Named_object*, Block*); - // Return whether ST is a type created to hold thunk parameters. - static bool - is_thunk_struct(const Struct_type *st); - protected: int do_traverse(Traverse* traverse); @@ -1130,9 +1179,6 @@ class Thunk_statement : public Statement void thunk_field_param(int n, char* buf, size_t buflen); - // A list of all the struct types created for thunk statements. - static Unordered_set(const Struct_type*) thunk_types; - // The function call to be executed in a separate thread (go) or // later (defer). Expression* call_; @@ -1175,6 +1221,74 @@ class Defer_statement : public Thunk_statement do_dump_statement(Ast_dump_context*) const; }; +// A goto statement. + +class Goto_statement : public Statement +{ + public: + Goto_statement(Label* label, Location location) + : Statement(STATEMENT_GOTO, location), + label_(label) + { } + + // Return the label being jumped to. + Label* + label() const + { return this->label_; } + + protected: + int + do_traverse(Traverse*); + + void + do_check_types(Gogo*); + + bool + do_may_fall_through() const + { return false; } + + Bstatement* + do_get_backend(Translate_context*); + + void + do_dump_statement(Ast_dump_context*) const; + + private: + Label* label_; +}; + +// A goto statement to an unnamed label. + +class Goto_unnamed_statement : public Statement +{ + public: + Goto_unnamed_statement(Unnamed_label* label, Location location) + : Statement(STATEMENT_GOTO_UNNAMED, location), + label_(label) + { } + + Unnamed_label* + unnamed_label() const + { return this->label_; } + + protected: + int + do_traverse(Traverse*); + + bool + do_may_fall_through() const + { return false; } + + Bstatement* + do_get_backend(Translate_context* context); + + void + do_dump_statement(Ast_dump_context*) const; + + private: + Unnamed_label* label_; +}; + // A label statement. class Label_statement : public Statement @@ -1186,7 +1300,7 @@ class Label_statement : public Statement { } // Return the label itself. - const Label* + Label* label() const { return this->label_; } @@ -1205,6 +1319,28 @@ class Label_statement : public Statement Label* label_; }; +// An unnamed label statement. + +class Unnamed_label_statement : public Statement +{ + public: + Unnamed_label_statement(Unnamed_label* label); + + protected: + int + do_traverse(Traverse*); + + Bstatement* + do_get_backend(Translate_context* context); + + void + do_dump_statement(Ast_dump_context*) const; + + private: + // The label. + Unnamed_label* label_; +}; + // An if statement. class If_statement : public Statement @@ -1364,7 +1500,7 @@ class For_range_statement : public Statement Expression* make_range_ref(Named_object*, Temporary_statement*, Location); - Expression* + Call_expression* call_builtin(Gogo*, const char* funcname, Expression* arg, Location); void @@ -1383,9 +1519,10 @@ class For_range_statement : public Statement Block**, Expression**, Block**, Block**); void - lower_range_map(Gogo*, Block*, Block*, Named_object*, Temporary_statement*, + lower_range_map(Gogo*, Map_type*, Block*, Block*, Named_object*, Temporary_statement*, Temporary_statement*, - Block**, Expression**, Block**, Block**); + Temporary_statement*, Block**, Expression**, Block**, + Block**); void lower_range_channel(Gogo*, Block*, Block*, Named_object*, @@ -1487,7 +1624,7 @@ class Case_clauses public: Case_clause() : cases_(NULL), statements_(NULL), is_default_(false), - is_fallthrough_(false), location_(UNKNOWN_LOCATION) + is_fallthrough_(false), location_(Linemap::unknown_location()) { } Case_clause(Expression_list* cases, bool is_default, Block* statements, @@ -1674,7 +1811,7 @@ class Type_case_clauses public: Type_case_clause() : type_(NULL), statements_(NULL), is_default_(false), - location_(UNKNOWN_LOCATION) + location_(Linemap::unknown_location()) { } Type_case_clause(Type* type, bool is_fallthrough, bool is_default, diff --git a/gcc/go/gofrontend/types.cc b/gcc/go/gofrontend/types.cc index 0443281dd8..f65dbd73c3 100644 --- a/gcc/go/gofrontend/types.cc +++ b/gcc/go/gofrontend/types.cc @@ -6,8 +6,12 @@ #include "go-system.h" +#include <ostream> + #include "go-c.h" #include "gogo.h" +#include "go-diagnostics.h" +#include "go-encode-id.h" #include "operator.h" #include "expressions.h" #include "statements.h" @@ -324,10 +328,10 @@ Type::are_identical(const Type* t1, const Type* t2, bool errors_are_identical, t2 = t2->forwarded(); // Ignore aliases for purposes of type identity. - if (t1->named_type() != NULL && t1->named_type()->is_alias()) - t1 = t1->named_type()->real_type(); - if (t2->named_type() != NULL && t2->named_type()->is_alias()) - t2 = t2->named_type()->real_type(); + while (t1->named_type() != NULL && t1->named_type()->is_alias()) + t1 = t1->named_type()->real_type()->forwarded(); + while (t2->named_type() != NULL && t2->named_type()->is_alias()) + t2 = t2->named_type()->real_type()->forwarded(); if (t1 == t2) return true; @@ -565,6 +569,12 @@ Type::are_compatible_for_comparison(bool is_equality_op, const Type *t1, return t2->named_type()->named_type_is_comparable(reason); else if (t1->struct_type() != NULL) { + if (t1->struct_type()->is_struct_incomparable()) + { + if (reason != NULL) + *reason = _("invalid comparison of generated struct"); + return false; + } const Struct_field_list* fields = t1->struct_type()->fields(); for (Struct_field_list::const_iterator p = fields->begin(); p != fields->end(); @@ -580,6 +590,12 @@ Type::are_compatible_for_comparison(bool is_equality_op, const Type *t1, } else if (t1->array_type() != NULL) { + if (t1->array_type()->is_array_incomparable()) + { + if (reason != NULL) + *reason = _("invalid comparison of generated array"); + return false; + } if (t1->array_type()->length()->is_nil_expression() || !t1->array_type()->element_type()->is_comparable()) { @@ -806,6 +822,8 @@ Type::are_convertible(const Type* lhs, const Type* rhs, std::string* reason) unsigned int Type::hash_for_method(Gogo* gogo) const { + if (this->named_type() != NULL && this->named_type()->is_alias()) + return this->named_type()->real_type()->hash_for_method(gogo); unsigned int ret = 0; if (this->classification_ != TYPE_FORWARD) ret += this->classification_; @@ -1149,15 +1167,16 @@ Bexpression* Type::type_descriptor_pointer(Gogo* gogo, Location location) { Type* t = this->forwarded(); - if (t->named_type() != NULL && t->named_type()->is_alias()) - t = t->named_type()->real_type(); + while (t->named_type() != NULL && t->named_type()->is_alias()) + t = t->named_type()->real_type()->forwarded(); if (t->type_descriptor_var_ == NULL) { t->make_type_descriptor_var(gogo); go_assert(t->type_descriptor_var_ != NULL); } Bexpression* var_expr = - gogo->backend()->var_expression(t->type_descriptor_var_, location); + gogo->backend()->var_expression(t->type_descriptor_var_, + VE_rvalue, location); return gogo->backend()->address_expression(var_expr, location); } @@ -1202,10 +1221,12 @@ Type::make_type_descriptor_var(Gogo* gogo) Type* td_type = Type::make_type_descriptor_type(); Btype* td_btype = td_type->get_backend(gogo); + const char *name = "__go_tdn_unsafe.Pointer"; + std::string asm_name(go_selectively_encode_id(name)); this->type_descriptor_var_ = - gogo->backend()->immutable_struct_reference("__go_tdn_unsafe.Pointer", - td_btype, - bloc); + gogo->backend()->immutable_struct_reference(name, asm_name, + td_btype, + bloc); if (phash != NULL) *phash = this->type_descriptor_var_; @@ -1224,10 +1245,11 @@ Type::make_type_descriptor_var(Gogo* gogo) const Package* dummy; if (this->type_descriptor_defined_elsewhere(nt, &dummy)) { + std::string asm_name(go_selectively_encode_id(var_name)); this->type_descriptor_var_ = - gogo->backend()->immutable_struct_reference(var_name, - initializer_btype, - loc); + gogo->backend()->immutable_struct_reference(var_name, asm_name, + initializer_btype, + loc); if (phash != NULL) *phash = this->type_descriptor_var_; return; @@ -1256,8 +1278,9 @@ Type::make_type_descriptor_var(Gogo* gogo) // ensure that type_descriptor_pointer will work if called while // converting INITIALIZER. + std::string asm_name(go_selectively_encode_id(var_name)); this->type_descriptor_var_ = - gogo->backend()->immutable_struct(var_name, false, is_common, + gogo->backend()->immutable_struct(var_name, asm_name, false, is_common, initializer_btype, loc); if (phash != NULL) *phash = this->type_descriptor_var_; @@ -1316,18 +1339,8 @@ Type::type_descriptor_var_name(Gogo* gogo, Named_type* nt) } } - // FIXME: This adds in pkgpath twice for hidden symbols, which is - // pointless. - const std::string& name(no->name()); - if (!Gogo::is_hidden_name(name)) - ret.append(name); - else - { - ret.append(1, '.'); - ret.append(Gogo::pkgpath_for_symbol(Gogo::hidden_name_pkgpath(name))); - ret.append(1, '.'); - ret.append(Gogo::unpack_hidden_name(name)); - } + std::string mname(Gogo::mangle_possibly_hidden_name(no->name())); + ret.append(mname); return ret; } @@ -1414,7 +1427,9 @@ Type::make_builtin_struct_type(int nfields, ...) va_end(ap); - return Type::make_struct_type(sfl, bloc); + Struct_type* ret = Type::make_struct_type(sfl, bloc); + ret->set_is_struct_incomparable(); + return ret; } // A list of builtin named types. @@ -1450,8 +1465,8 @@ Type::convert_builtin_named_types(Gogo* gogo) } // Return the type of a type descriptor. We should really tie this to -// runtime.Type rather than copying it. This must match commonType in -// libgo/go/runtime/type.go. +// runtime.Type rather than copying it. This must match the struct "_type" +// declared in libgo/go/runtime/type.go. Type* Type::make_type_descriptor_type() @@ -1476,7 +1491,7 @@ Type::make_type_descriptor_type() Typed_identifier_list *params = new Typed_identifier_list(); params->push_back(Typed_identifier("key", unsafe_pointer_type, bloc)); - params->push_back(Typed_identifier("key_size", uintptr_type, bloc)); + params->push_back(Typed_identifier("seed", uintptr_type, bloc)); Typed_identifier_list* results = new Typed_identifier_list(); results->push_back(Typed_identifier("", uintptr_type, bloc)); @@ -1487,7 +1502,6 @@ Type::make_type_descriptor_type() params = new Typed_identifier_list(); params->push_back(Typed_identifier("key1", unsafe_pointer_type, bloc)); params->push_back(Typed_identifier("key2", unsafe_pointer_type, bloc)); - params->push_back(Typed_identifier("key_size", uintptr_type, bloc)); results = new Typed_identifier_list(); results->push_back(Typed_identifier("", Type::lookup_bool_type(), bloc)); @@ -1497,7 +1511,7 @@ Type::make_type_descriptor_type() // Forward declaration for the type descriptor type. Named_object* named_type_descriptor_type = - Named_object::make_type_declaration("commonType", NULL, bloc); + Named_object::make_type_declaration("_type", NULL, bloc); Type* ft = Type::make_forward_declaration(named_type_descriptor_type); Type* pointer_type_descriptor_type = Type::make_pointer_type(ft); @@ -1543,7 +1557,7 @@ Type::make_type_descriptor_type() "ptrToThis", pointer_type_descriptor_type); - Named_type* named = Type::make_builtin_named_type("commonType", + Named_type* named = Type::make_builtin_named_type("_type", type_descriptor_type); named_type_descriptor_type->set_type_value(named); @@ -1566,6 +1580,88 @@ Type::make_type_descriptor_ptr_type() return ret; } +// Return the alignment required by the memequalN function. N is a +// type size: 16, 32, 64, or 128. The memequalN functions are defined +// in libgo/go/runtime/alg.go. + +int64_t +Type::memequal_align(Gogo* gogo, int size) +{ + const char* tn; + switch (size) + { + case 16: + tn = "int16"; + break; + case 32: + tn = "int32"; + break; + case 64: + tn = "int64"; + break; + case 128: + // The code uses [2]int64, which must have the same alignment as + // int64. + tn = "int64"; + break; + default: + go_unreachable(); + } + + Type* t = Type::lookup_integer_type(tn); + + int64_t ret; + if (!t->backend_type_align(gogo, &ret)) + go_unreachable(); + return ret; +} + +// Return whether this type needs specially built type functions. +// This returns true for types that are comparable and either can not +// use an identity comparison, or are a non-standard size. + +bool +Type::needs_specific_type_functions(Gogo* gogo) +{ + Named_type* nt = this->named_type(); + if (nt != NULL && nt->is_alias()) + return false; + if (!this->is_comparable()) + return false; + if (!this->compare_is_identity(gogo)) + return true; + + // We create a few predeclared types for type descriptors; they are + // really just for the backend and don't need hash or equality + // functions. + if (nt != NULL && Linemap::is_predeclared_location(nt->location())) + return false; + + int64_t size, align; + if (!this->backend_type_size(gogo, &size) + || !this->backend_type_align(gogo, &align)) + { + go_assert(saw_errors()); + return false; + } + // This switch matches the one in Type::type_functions. + switch (size) + { + case 0: + case 1: + case 2: + return align < Type::memequal_align(gogo, 16); + case 4: + return align < Type::memequal_align(gogo, 32); + case 8: + return align < Type::memequal_align(gogo, 64); + case 16: + return align < Type::memequal_align(gogo, 128); + default: + return true; + } +} + // Set *HASH_FN and *EQUAL_FN to the runtime functions which compute a // hash code for this type and which compare whether two values of // this type are equal. If NAME is not NULL it is the name of this @@ -1577,6 +1673,18 @@ Type::type_functions(Gogo* gogo, Named_type* name, Function_type* hash_fntype, Function_type* equal_fntype, Named_object** hash_fn, Named_object** equal_fn) { + // If this loop leaves NAME as NULL, then the type does not have a + // name after all. + while (name != NULL && name->is_alias()) + name = name->real_type()->named_type(); + + if (!this->is_comparable()) + { + *hash_fn = NULL; + *equal_fn = NULL; + return; + } + if (hash_fntype == NULL || equal_fntype == NULL) { Location bloc = Linemap::predeclared_location(); @@ -1590,7 +1698,7 @@ Type::type_functions(Gogo* gogo, Named_type* name, Function_type* hash_fntype, Typed_identifier_list* params = new Typed_identifier_list(); params->push_back(Typed_identifier("key", unsafe_pointer_type, bloc)); - params->push_back(Typed_identifier("key_size", uintptr_type, bloc)); + params->push_back(Typed_identifier("seed", uintptr_type, bloc)); Typed_identifier_list* results = new Typed_identifier_list(); results->push_back(Typed_identifier("", uintptr_type, bloc)); @@ -1604,7 +1712,6 @@ Type::type_functions(Gogo* gogo, Named_type* name, Function_type* hash_fntype, bloc)); params->push_back(Typed_identifier("key2", unsafe_pointer_type, bloc)); - params->push_back(Typed_identifier("key_size", uintptr_type, bloc)); Typed_identifier_list* results = new Typed_identifier_list(); results->push_back(Typed_identifier("", Type::lookup_bool_type(), @@ -1618,15 +1725,77 @@ Type::type_functions(Gogo* gogo, Named_type* name, Function_type* hash_fntype, const char* equal_fnname; if (this->compare_is_identity(gogo)) { - hash_fnname = "__go_type_hash_identity"; - equal_fnname = "__go_type_equal_identity"; - } - else if (!this->is_comparable() || - (this->struct_type() != NULL - && Thunk_statement::is_thunk_struct(this->struct_type()))) - { - hash_fnname = "__go_type_hash_error"; - equal_fnname = "__go_type_equal_error"; + int64_t size, align; + if (!this->backend_type_size(gogo, &size) + || !this->backend_type_align(gogo, &align)) + { + go_assert(saw_errors()); + return; + } + bool build_functions = false; + // This switch matches the one in Type::needs_specific_type_functions. + // The alignment tests are because of the memequal functions, + // which assume that the values are aligned as required for an + // integer of that size. + switch (size) + { + case 0: + hash_fnname = "runtime.memhash0"; + equal_fnname = "runtime.memequal0"; + break; + case 1: + hash_fnname = "runtime.memhash8"; + equal_fnname = "runtime.memequal8"; + break; + case 2: + if (align < Type::memequal_align(gogo, 16)) + build_functions = true; + else + { + hash_fnname = "runtime.memhash16"; + equal_fnname = "runtime.memequal16"; + } + break; + case 4: + if (align < Type::memequal_align(gogo, 32)) + build_functions = true; + else + { + hash_fnname = "runtime.memhash32"; + equal_fnname = "runtime.memequal32"; + } + break; + case 8: + if (align < Type::memequal_align(gogo, 64)) + build_functions = true; + else + { + hash_fnname = "runtime.memhash64"; + equal_fnname = "runtime.memequal64"; + } + break; + case 16: + if (align < Type::memequal_align(gogo, 128)) + build_functions = true; + else + { + hash_fnname = "runtime.memhash128"; + equal_fnname = "runtime.memequal128"; + } + break; + default: + build_functions = true; + break; + } + if (build_functions) + { + // We don't have a built-in function for a type of this size + // and alignment. Build a function to use that calls the + // generic hash/equality functions for identity, passing the size. + this->specific_type_functions(gogo, name, size, hash_fntype, + equal_fntype, hash_fn, equal_fn); + return; + } } else { @@ -1648,18 +1817,40 @@ Type::type_functions(Gogo* gogo, Named_type* name, Function_type* hash_fntype, go_unreachable(); case Type::TYPE_FLOAT: - hash_fnname = "__go_type_hash_float"; - equal_fnname = "__go_type_equal_float"; + switch (this->float_type()->bits()) + { + case 32: + hash_fnname = "runtime.f32hash"; + equal_fnname = "runtime.f32equal"; + break; + case 64: + hash_fnname = "runtime.f64hash"; + equal_fnname = "runtime.f64equal"; + break; + default: + go_unreachable(); + } break; case Type::TYPE_COMPLEX: - hash_fnname = "__go_type_hash_complex"; - equal_fnname = "__go_type_equal_complex"; + switch (this->complex_type()->bits()) + { + case 64: + hash_fnname = "runtime.c64hash"; + equal_fnname = "runtime.c64equal"; + break; + case 128: + hash_fnname = "runtime.c128hash"; + equal_fnname = "runtime.c128equal"; + break; + default: + go_unreachable(); + } break; case Type::TYPE_STRING: - hash_fnname = "__go_type_hash_string"; - equal_fnname = "__go_type_equal_string"; + hash_fnname = "runtime.strhash"; + equal_fnname = "runtime.strequal"; break; case Type::TYPE_STRUCT: @@ -1667,7 +1858,7 @@ Type::type_functions(Gogo* gogo, Named_type* name, Function_type* hash_fntype, // This is a struct which can not be compared using a // simple identity function. We need to build a function // for comparison. - this->specific_type_functions(gogo, name, hash_fntype, + this->specific_type_functions(gogo, name, -1, hash_fntype, equal_fntype, hash_fn, equal_fn); return; } @@ -1684,7 +1875,7 @@ Type::type_functions(Gogo* gogo, Named_type* name, Function_type* hash_fntype, // This is an array which can not be compared using a // simple identity function. We need to build a // function for comparison. - this->specific_type_functions(gogo, name, hash_fntype, + this->specific_type_functions(gogo, name, -1, hash_fntype, equal_fntype, hash_fn, equal_fn); return; } @@ -1693,13 +1884,13 @@ Type::type_functions(Gogo* gogo, Named_type* name, Function_type* hash_fntype, case Type::TYPE_INTERFACE: if (this->interface_type()->is_empty()) { - hash_fnname = "__go_type_hash_empty_interface"; - equal_fnname = "__go_type_equal_empty_interface"; + hash_fnname = "runtime.nilinterhash"; + equal_fnname = "runtime.nilinterequal"; } else { - hash_fnname = "__go_type_hash_interface"; - equal_fnname = "__go_type_equal_interface"; + hash_fnname = "runtime.interhash"; + equal_fnname = "runtime.interequal"; } break; @@ -1726,11 +1917,13 @@ Type::type_functions(Gogo* gogo, Named_type* name, Function_type* hash_fntype, Type::Type_functions Type::type_functions_table; -// Handle a type function which is specific to a type: a struct or -// array which can not use an identity comparison. +// Handle a type function which is specific to a type: if SIZE == -1, +// this is a struct or array that can not use an identity comparison. +// Otherwise, it is a type that uses an identity comparison but is not +// one of the standard supported sizes. void -Type::specific_type_functions(Gogo* gogo, Named_type* name, +Type::specific_type_functions(Gogo* gogo, Named_type* name, int64_t size, Function_type* hash_fntype, Function_type* equal_fntype, Named_object** hash_fn, @@ -1816,11 +2009,13 @@ Type::specific_type_functions(Gogo* gogo, Named_type* name, if (!is_defined_elsewhere) { if (gogo->in_global_scope()) - this->write_specific_type_functions(gogo, name, hash_name, hash_fntype, - equal_name, equal_fntype); + this->write_specific_type_functions(gogo, name, size, hash_name, + hash_fntype, equal_name, + equal_fntype); else - gogo->queue_specific_type_function(this, name, hash_name, hash_fntype, - equal_name, equal_fntype); + gogo->queue_specific_type_function(this, name, size, hash_name, + hash_fntype, equal_name, + equal_fntype); } } @@ -1828,7 +2023,7 @@ Type::specific_type_functions(Gogo* gogo, Named_type* name, // written specially. void -Type::write_specific_type_functions(Gogo* gogo, Named_type* name, +Type::write_specific_type_functions(Gogo* gogo, Named_type* name, int64_t size, const std::string& hash_name, Function_type* hash_fntype, const std::string& equal_name, @@ -1842,12 +2037,16 @@ Type::write_specific_type_functions(Gogo* gogo, Named_type* name, return; } + go_assert(this->is_comparable()); + Named_object* hash_fn = gogo->start_function(hash_name, hash_fntype, false, bloc); hash_fn->func_value()->set_is_type_specific_function(); gogo->start_block(bloc); - if (name != NULL && name->real_type()->named_type() != NULL) + if (size != -1) + this->write_identity_hash(gogo, size); + else if (name != NULL && name->real_type()->named_type() != NULL) this->write_named_hash(gogo, name, hash_fntype, equal_fntype); else if (this->struct_type() != NULL) this->struct_type()->write_hash_function(gogo, name, hash_fntype, @@ -1868,7 +2067,9 @@ Type::write_specific_type_functions(Gogo* gogo, Named_type* name, equal_fn->func_value()->set_is_type_specific_function(); gogo->start_block(bloc); - if (name != NULL && name->real_type()->named_type() != NULL) + if (size != -1) + this->write_identity_equal(gogo, size); + else if (name != NULL && name->real_type()->named_type() != NULL) this->write_named_equal(gogo, name); else if (this->struct_type() != NULL) this->struct_type()->write_equal_function(gogo, name); @@ -1887,6 +2088,112 @@ Type::write_specific_type_functions(Gogo* gogo, Named_type* name, equal_fn->func_value()->descriptor(gogo, equal_fn); } +// Write a hash function for a type that can use an identity hash but +// is not one of the standard supported sizes. For example, this +// would be used for the type [3]byte. This builds a return statement +// that returns a call to the memhash function, passing the key and +// seed from the function arguments (already constructed before this +// is called), and the constant size. + +void +Type::write_identity_hash(Gogo* gogo, int64_t size) +{ + Location bloc = Linemap::predeclared_location(); + + Type* unsafe_pointer_type = Type::make_pointer_type(Type::make_void_type()); + Type* uintptr_type = Type::lookup_integer_type("uintptr"); + + Typed_identifier_list* params = new Typed_identifier_list(); + params->push_back(Typed_identifier("key", unsafe_pointer_type, bloc)); + params->push_back(Typed_identifier("seed", uintptr_type, bloc)); + params->push_back(Typed_identifier("size", uintptr_type, bloc)); + + Typed_identifier_list* results = new Typed_identifier_list(); + results->push_back(Typed_identifier("", uintptr_type, bloc)); + + Function_type* memhash_fntype = Type::make_function_type(NULL, params, + results, bloc); + + Named_object* memhash = + Named_object::make_function_declaration("runtime.memhash", NULL, + memhash_fntype, bloc); + memhash->func_declaration_value()->set_asm_name("runtime.memhash"); + + Named_object* key_arg = gogo->lookup("key", NULL); + go_assert(key_arg != NULL); + Named_object* seed_arg = gogo->lookup("seed", NULL); + go_assert(seed_arg != NULL); + + Expression* key_ref = Expression::make_var_reference(key_arg, bloc); + Expression* seed_ref = Expression::make_var_reference(seed_arg, bloc); + Expression* size_arg = Expression::make_integer_int64(size, uintptr_type, + bloc); + Expression_list* args = new Expression_list(); + args->push_back(key_ref); + args->push_back(seed_ref); + args->push_back(size_arg); + Expression* func = Expression::make_func_reference(memhash, NULL, bloc); + Expression* call = Expression::make_call(func, args, false, bloc); + + Expression_list* vals = new Expression_list(); + vals->push_back(call); + Statement* s = Statement::make_return_statement(vals, bloc); + gogo->add_statement(s); +} + +// Write an equality function for a type that can use an identity +// equality comparison but is not one of the standard supported sizes. +// For example, this would be used for the type [3]byte. This builds +// a return statement that returns a call to the memequal function, +// passing the two keys from the function arguments (already +// constructed before this is called), and the constant size. + +void +Type::write_identity_equal(Gogo* gogo, int64_t size) +{ + Location bloc = Linemap::predeclared_location(); + + Type* unsafe_pointer_type = Type::make_pointer_type(Type::make_void_type()); + Type* uintptr_type = Type::lookup_integer_type("uintptr"); + + Typed_identifier_list* params = new Typed_identifier_list(); + params->push_back(Typed_identifier("key1", unsafe_pointer_type, bloc)); + params->push_back(Typed_identifier("key2", unsafe_pointer_type, bloc)); + params->push_back(Typed_identifier("size", uintptr_type, bloc)); + + Typed_identifier_list* results = new Typed_identifier_list(); + results->push_back(Typed_identifier("", Type::lookup_bool_type(), bloc)); + + Function_type* memequal_fntype = Type::make_function_type(NULL, params, + results, bloc); + + Named_object* memequal = + Named_object::make_function_declaration("runtime.memequal", NULL, + memequal_fntype, bloc); + memequal->func_declaration_value()->set_asm_name("runtime.memequal"); + + Named_object* key1_arg = gogo->lookup("key1", NULL); + go_assert(key1_arg != NULL); + Named_object* key2_arg = gogo->lookup("key2", NULL); + go_assert(key2_arg != NULL); + + Expression* key1_ref = Expression::make_var_reference(key1_arg, bloc); + Expression* key2_ref = Expression::make_var_reference(key2_arg, bloc); + Expression* size_arg = Expression::make_integer_int64(size, uintptr_type, + bloc); + Expression_list* args = new Expression_list(); + args->push_back(key1_ref); + args->push_back(key2_ref); + args->push_back(size_arg); + Expression* func = Expression::make_func_reference(memequal, NULL, bloc); + Expression* call = Expression::make_call(func, args, false, bloc); + + Expression_list* vals = new Expression_list(); + vals->push_back(call); + Statement* s = Statement::make_return_statement(vals, bloc); + gogo->add_statement(s); +} + // Write a hash function that simply calls the hash function for a // named type. This is used when one named type is defined as // another. This ensures that this case works when the other named @@ -1900,6 +2207,11 @@ Type::write_named_hash(Gogo* gogo, Named_type* name, Location bloc = Linemap::predeclared_location(); Named_type* base_type = name->real_type()->named_type(); + while (base_type->is_alias()) + { + base_type = base_type->real_type()->named_type(); + go_assert(base_type != NULL); + } go_assert(base_type != NULL); // The pointer to the type we are going to hash. This is an @@ -1907,9 +2219,9 @@ Type::write_named_hash(Gogo* gogo, Named_type* name, Named_object* key_arg = gogo->lookup("key", NULL); go_assert(key_arg != NULL); - // The size of the type we are going to hash. - Named_object* keysz_arg = gogo->lookup("key_size", NULL); - go_assert(keysz_arg != NULL); + // The seed argument to the hash function. + Named_object* seed_arg = gogo->lookup("seed", NULL); + go_assert(seed_arg != NULL); Named_object* hash_fn; Named_object* equal_fn; @@ -1918,10 +2230,10 @@ Type::write_named_hash(Gogo* gogo, Named_type* name, // Call the hash function for the base type. Expression* key_ref = Expression::make_var_reference(key_arg, bloc); - Expression* keysz_ref = Expression::make_var_reference(keysz_arg, bloc); + Expression* seed_ref = Expression::make_var_reference(seed_arg, bloc); Expression_list* args = new Expression_list(); args->push_back(key_ref); - args->push_back(keysz_ref); + args->push_back(seed_ref); Expression* func = Expression::make_func_reference(hash_fn, NULL, bloc); Expression* call = Expression::make_call(func, args, false, bloc); @@ -2042,8 +2354,18 @@ Type::type_descriptor_constructor(Gogo* gogo, int runtime_type_kind, Named_object* equal_fn; this->type_functions(gogo, name, hash_fntype, equal_fntype, &hash_fn, &equal_fn); - vals->push_back(Expression::make_func_reference(hash_fn, NULL, bloc)); - vals->push_back(Expression::make_func_reference(equal_fn, NULL, bloc)); + if (hash_fn == NULL) + vals->push_back(Expression::make_cast(hash_fntype, + Expression::make_nil(bloc), + bloc)); + else + vals->push_back(Expression::make_func_reference(hash_fn, NULL, bloc)); + if (equal_fn == NULL) + vals->push_back(Expression::make_cast(equal_fntype, + Expression::make_nil(bloc), + bloc)); + else + vals->push_back(Expression::make_func_reference(equal_fn, NULL, bloc)); ++p; go_assert(p->is_field_name("gc")); @@ -2097,8 +2419,8 @@ Bexpression* Type::gc_symbol_pointer(Gogo* gogo) { Type* t = this->forwarded(); - if (t->named_type() != NULL && t->named_type()->is_alias()) - t = t->named_type()->real_type(); + while (t->named_type() != NULL && t->named_type()->is_alias()) + t = t->named_type()->real_type()->forwarded(); if (t->gc_symbol_var_ == NULL) { t->make_gc_symbol_var(gogo); @@ -2106,8 +2428,11 @@ Type::gc_symbol_pointer(Gogo* gogo) } Location bloc = Linemap::predeclared_location(); Bexpression* var_expr = - gogo->backend()->var_expression(t->gc_symbol_var_, bloc); - return gogo->backend()->address_expression(var_expr, bloc); + gogo->backend()->var_expression(t->gc_symbol_var_, VE_rvalue, bloc); + Bexpression* addr_expr = + gogo->backend()->address_expression(var_expr, bloc); + Btype* ubtype = Type::lookup_integer_type("uintptr")->get_backend(gogo); + return gogo->backend()->convert_expression(ubtype, addr_expr, bloc); } // A mapping from unnamed types to GC symbol variables. @@ -2152,8 +2477,10 @@ Type::make_gc_symbol_var(Gogo* gogo) const Package* dummy; if (this->type_descriptor_defined_elsewhere(nt, &dummy)) { + std::string asm_name(go_selectively_encode_id(sym_name)); this->gc_symbol_var_ = - gogo->backend()->implicit_variable_reference(sym_name, sym_btype); + gogo->backend()->implicit_variable_reference(sym_name, asm_name, + sym_btype); if (phash != NULL) *phash = this->gc_symbol_var_; return; @@ -2175,11 +2502,28 @@ Type::make_gc_symbol_var(Gogo* gogo) is_common = true; } + // The current garbage collector requires that the GC symbol be + // aligned to at least a four byte boundary. See the use of PRECISE + // and LOOP in libgo/runtime/mgc0.c. + int64_t align; + if (!sym_init->type()->backend_type_align(gogo, &align)) + go_assert(saw_errors()); + if (align < 4) + align = 4; + else + { + // Use default alignment. + align = 0; + } + // Since we are building the GC symbol in this package, we must create the // variable before converting the initializer to its backend representation // because the initializer may refer to the GC symbol for this type. + std::string asm_name(go_selectively_encode_id(sym_name)); this->gc_symbol_var_ = - gogo->backend()->implicit_variable(sym_name, sym_btype, false, true, is_common, 0); + gogo->backend()->implicit_variable(sym_name, asm_name, + sym_btype, false, true, is_common, + align); if (phash != NULL) *phash = this->gc_symbol_var_; @@ -2216,9 +2560,10 @@ Type::gc_symbol_constructor(Gogo* gogo) vals->push_back(Expression::make_integer_ul(GC_END, uintptr_t, bloc)); - Expression* len = Expression::make_integer_ul(vals->size() + 1, NULL, + Expression* len = Expression::make_integer_ul(vals->size(), NULL, bloc); Array_type* gc_symbol_type = Type::make_array_type(uintptr_t, len); + gc_symbol_type->set_is_array_incomparable(); return Expression::make_array_composite_literal(gc_symbol_type, vals, bloc); } @@ -2560,12 +2905,13 @@ Type::backend_type_size(Gogo* gogo, int64_t *psize) if (*psize == -1) { if (this->named_type() != NULL) - error_at(this->named_type()->location(), - "type %s larger than address space", - Gogo::message_name(this->named_type()->name()).c_str()); + go_error_at(this->named_type()->location(), + "type %s larger than address space", + Gogo::message_name(this->named_type()->name()).c_str()); else - error("type %s larger than address space", - this->reflection(gogo).c_str()); + go_error_at(Linemap::unknown_location(), + "type %s larger than address space", + this->reflection(gogo).c_str()); // Make this an error type to avoid knock-on errors. this->classification_ = TYPE_ERROR; @@ -2629,7 +2975,7 @@ Type::import_type(Import* imp) return Interface_type::do_import(imp); else { - error_at(imp->location(), "import error: expected type"); + go_error_at(imp->location(), "import error: expected type"); return Type::make_error_type(); } } @@ -3755,6 +4101,7 @@ Function_type::get_backend_fntype(Gogo* gogo) } Struct_type* st = Type::make_struct_type(sfl, this->location()); + st->set_is_struct_incomparable(); ins.first->second = st->get_backend(gogo); } bresult_struct = ins.first->second; @@ -3836,7 +4183,7 @@ Function_type::do_type_descriptor(Gogo* gogo, Named_type* name) vals->reserve(4); Struct_field_list::const_iterator p = fields->begin(); - go_assert(p->is_field_name("commonType")); + go_assert(p->is_field_name("_type")); vals->push_back(this->type_descriptor_constructor(gogo, RUNTIME_TYPE_KIND_FUNC, name, NULL, true)); @@ -4349,7 +4696,7 @@ Pointer_type::do_type_descriptor(Gogo* gogo, Named_type* name) vals->reserve(2); Struct_field_list::const_iterator p = fields->begin(); - go_assert(p->is_field_name("commonType")); + go_assert(p->is_field_name("_type")); vals->push_back(this->type_descriptor_constructor(gogo, RUNTIME_TYPE_KIND_PTR, name, methods, false)); @@ -4502,10 +4849,7 @@ class Call_multiple_result_type : public Type protected: bool do_has_pointer() const - { - go_assert(saw_errors()); - return false; - } + { return false; } bool do_compare_is_identity(Gogo*) @@ -4576,7 +4920,10 @@ Struct_field::field_name() const if (dt->forward_declaration_type() != NULL) return dt->forward_declaration_type()->name(); else if (dt->named_type() != NULL) - return dt->named_type()->name(); + { + // Note that this can be an alias name. + return dt->named_type()->name(); + } else if (t->is_error_type() || dt->is_error_type()) { static const std::string error_string = "*error*"; @@ -4720,13 +5067,13 @@ Struct_type::do_verify() { if (t->named_type() != NULL && t->points_to() != NULL) { - error_at(p->location(), "embedded type may not be a pointer"); + go_error_at(p->location(), "embedded type may not be a pointer"); p->set_type(Type::make_error_type()); } else if (t->points_to() != NULL && t->points_to()->interface_type() != NULL) { - error_at(p->location(), + go_error_at(p->location(), "embedded type may not be pointer to interface"); p->set_type(Type::make_error_type()); } @@ -4759,6 +5106,8 @@ bool Struct_type::is_identical(const Struct_type* t, bool errors_are_identical) const { + if (this->is_struct_incomparable_ != t->is_struct_incomparable_) + return false; const Struct_field_list* fields1 = this->fields(); const Struct_field_list* fields2 = t->fields(); if (fields1 == NULL || fields2 == NULL) @@ -4843,6 +5192,44 @@ Struct_type::do_compare_is_identity(Gogo* gogo) return true; } +// Return whether this struct type is reflexive--whether a value of +// this type is always equal to itself. + +bool +Struct_type::do_is_reflexive() +{ + const Struct_field_list* fields = this->fields_; + if (fields == NULL) + return true; + for (Struct_field_list::const_iterator pf = fields->begin(); + pf != fields->end(); + ++pf) + { + if (!pf->type()->is_reflexive()) + return false; + } + return true; +} + +// Return whether this struct type needs a key update when used as a +// map key. + +bool +Struct_type::do_needs_key_update() +{ + const Struct_field_list* fields = this->fields_; + if (fields == NULL) + return false; + for (Struct_field_list::const_iterator pf = fields->begin(); + pf != fields->end(); + ++pf) + { + if (pf->type()->needs_key_update()) + return true; + } + return false; +} + // Build identity and hash functions for this struct. // Hash code. @@ -4858,7 +5245,10 @@ Struct_type::do_hash_for_method(Gogo* gogo) const ++pf) ret = (ret << 1) + pf->type()->hash_for_method(gogo); } - return ret <<= 2; + ret <<= 2; + if (this->is_struct_incomparable_) + ret <<= 1; + return ret; } // Find the local field NAME. @@ -5219,7 +5609,7 @@ Struct_type::do_type_descriptor(Gogo* gogo, Named_type* name) go_assert(methods == NULL || name == NULL); Struct_field_list::const_iterator ps = fields->begin(); - go_assert(ps->is_field_name("commonType")); + go_assert(ps->is_field_name("_type")); vals->push_back(this->type_descriptor_constructor(gogo, RUNTIME_TYPE_KIND_STRUCT, name, methods, true)); @@ -5311,25 +5701,26 @@ Struct_type::write_hash_function(Gogo* gogo, Named_type*, go_assert(key_arg != NULL); Type* key_arg_type = key_arg->var_value()->type(); - Type* uintptr_type = Type::lookup_integer_type("uintptr"); + // The seed argument to the hash function. + Named_object* seed_arg = gogo->lookup("seed", NULL); + go_assert(seed_arg != NULL); - // Get a 0. - Expression* zero = Expression::make_integer_ul(0, uintptr_type, bloc); + Type* uintptr_type = Type::lookup_integer_type("uintptr"); - // Make a temporary to hold the return value, initialized to 0. - Temporary_statement* retval = Statement::make_temporary(uintptr_type, zero, + // Make a temporary to hold the return value, initialized to the seed. + Expression* ref = Expression::make_var_reference(seed_arg, bloc); + Temporary_statement* retval = Statement::make_temporary(uintptr_type, ref, bloc); gogo->add_statement(retval); // Make a temporary to hold the key as a uintptr. - Expression* ref = Expression::make_var_reference(key_arg, bloc); + ref = Expression::make_var_reference(key_arg, bloc); ref = Expression::make_cast(uintptr_type, ref, bloc); Temporary_statement* key = Statement::make_temporary(uintptr_type, ref, bloc); gogo->add_statement(key); // Loop over the struct fields. - bool first = true; const Struct_field_list* fields = this->fields_; for (Struct_field_list::const_iterator pf = fields->begin(); pf != fields->end(); @@ -5338,19 +5729,6 @@ Struct_type::write_hash_function(Gogo* gogo, Named_type*, if (Gogo::is_sink_name(pf->field_name())) continue; - if (first) - first = false; - else - { - // Multiply retval by 33. - Expression* i33 = Expression::make_integer_ul(33, uintptr_type, - bloc); - ref = Expression::make_temporary_reference(retval, bloc); - Statement* s = Statement::make_assignment_operation(OPERATOR_MULTEQ, - ref, i33, bloc); - gogo->add_statement(s); - } - // Get a pointer to the value of this field. Expression* offset = Expression::make_struct_field_offset(this, &*pf); ref = Expression::make_temporary_reference(key, bloc); @@ -5358,29 +5736,25 @@ Struct_type::write_hash_function(Gogo* gogo, Named_type*, bloc); subkey = Expression::make_cast(key_arg_type, subkey, bloc); - // Get the size of this field. - Expression* size = Expression::make_type_info(pf->type(), - Expression::TYPE_INFO_SIZE); - // Get the hash function to use for the type of this field. Named_object* hash_fn; Named_object* equal_fn; pf->type()->type_functions(gogo, pf->type()->named_type(), hash_fntype, equal_fntype, &hash_fn, &equal_fn); - // Call the hash function for the field. + // Call the hash function for the field, passing retval as the seed. + ref = Expression::make_temporary_reference(retval, bloc); Expression_list* args = new Expression_list(); args->push_back(subkey); - args->push_back(size); + args->push_back(ref); Expression* func = Expression::make_func_reference(hash_fn, NULL, bloc); Expression* call = Expression::make_call(func, args, false, bloc); - // Add the field's hash value to retval. + // Set retval to the result. Temporary_reference_expression* tref = Expression::make_temporary_reference(retval, bloc); tref->set_is_lvalue(); - Statement* s = Statement::make_assignment_operation(OPERATOR_PLUSEQ, - tref, call, bloc); + Statement* s = Statement::make_assignment(tref, call, bloc); gogo->add_statement(s); } @@ -5478,7 +5852,12 @@ Struct_type::do_reflection(Gogo* gogo, std::string* ret) const else ret->append(Gogo::unpack_hidden_name(p->field_name())); ret->push_back(' '); - this->append_reflection(p->type(), gogo, ret); + if (p->is_anonymous() + && p->type()->named_type() != NULL + && p->type()->named_type()->is_alias()) + p->type()->named_type()->append_reflection_type_name(gogo, true, ret); + else + this->append_reflection(p->type(), gogo, ret); if (p->has_tag()) { @@ -5549,15 +5928,24 @@ Struct_type::do_mangled_name(Gogo* gogo, std::string* ret) const if (p->is_anonymous()) ret->append("0_"); else - { - std::string n = Gogo::unpack_hidden_name(p->field_name()); + { + + std::string n(Gogo::mangle_possibly_hidden_name(p->field_name())); char buf[20]; snprintf(buf, sizeof buf, "%u_", static_cast<unsigned int>(n.length())); ret->append(buf); ret->append(n); } - this->append_mangled_name(p->type(), gogo, ret); + + // For an anonymous field with an alias type, the field name + // is the alias name. + if (p->is_anonymous() + && p->type()->named_type() != NULL + && p->type()->named_type()->is_alias()) + p->type()->named_type()->append_mangled_type_name(gogo, true, ret); + else + this->append_mangled_name(p->type(), gogo, ret); if (p->has_tag()) { const std::string& tag(p->tag()); @@ -5585,6 +5973,9 @@ Struct_type::do_mangled_name(Gogo* gogo, std::string* ret) const } } + if (this->is_struct_incomparable_) + ret->push_back('x'); + ret->push_back('e'); } @@ -5683,6 +6074,281 @@ Struct_type::do_import(Import* imp) return Type::make_struct_type(fields, imp->location()); } +// Whether we can write this struct type to a C header file. +// We can't if any of the fields are structs defined in a different package. + +bool +Struct_type::can_write_to_c_header( + std::vector<const Named_object*>* requires, + std::vector<const Named_object*>* declare) const +{ + const Struct_field_list* fields = this->fields_; + if (fields == NULL || fields->empty()) + return false; + for (Struct_field_list::const_iterator p = fields->begin(); + p != fields->end(); + ++p) + { + if (p->is_anonymous()) + return false; + if (!this->can_write_type_to_c_header(p->type(), requires, declare)) + return false; + } + return true; +} + +// Whether we can write the type T to a C header file. + +bool +Struct_type::can_write_type_to_c_header( + const Type* t, + std::vector<const Named_object*>* requires, + std::vector<const Named_object*>* declare) const +{ + t = t->forwarded(); + switch (t->classification()) + { + case TYPE_ERROR: + case TYPE_FORWARD: + return false; + + case TYPE_VOID: + case TYPE_BOOLEAN: + case TYPE_INTEGER: + case TYPE_FLOAT: + case TYPE_COMPLEX: + case TYPE_STRING: + case TYPE_FUNCTION: + case TYPE_MAP: + case TYPE_CHANNEL: + case TYPE_INTERFACE: + return true; + + case TYPE_POINTER: + // Don't try to handle a pointer to an array. + if (t->points_to()->array_type() != NULL + && !t->points_to()->is_slice_type()) + return false; + + if (t->points_to()->named_type() != NULL + && t->points_to()->struct_type() != NULL) + declare->push_back(t->points_to()->named_type()->named_object()); + return true; + + case TYPE_STRUCT: + return t->struct_type()->can_write_to_c_header(requires, declare); + + case TYPE_ARRAY: + if (t->is_slice_type()) + return true; + return this->can_write_type_to_c_header(t->array_type()->element_type(), + requires, declare); + + case TYPE_NAMED: + { + const Named_object* no = t->named_type()->named_object(); + if (no->package() != NULL) + { + if (t->is_unsafe_pointer_type()) + return true; + return false; + } + if (t->struct_type() != NULL) + { + requires->push_back(no); + return t->struct_type()->can_write_to_c_header(requires, declare); + } + return this->can_write_type_to_c_header(t->base(), requires, declare); + } + + case TYPE_CALL_MULTIPLE_RESULT: + case TYPE_NIL: + case TYPE_SINK: + default: + go_unreachable(); + } +} + +// Write this struct to a C header file. + +void +Struct_type::write_to_c_header(std::ostream& os) const +{ + const Struct_field_list* fields = this->fields_; + for (Struct_field_list::const_iterator p = fields->begin(); + p != fields->end(); + ++p) + { + os << '\t'; + this->write_field_to_c_header(os, p->field_name(), p->type()); + os << ';' << std::endl; + } +} + +// Write the type of a struct field to a C header file. + +void +Struct_type::write_field_to_c_header(std::ostream& os, const std::string& name, + const Type *t) const +{ + bool print_name = true; + t = t->forwarded(); + switch (t->classification()) + { + case TYPE_VOID: + os << "void"; + break; + + case TYPE_BOOLEAN: + os << "_Bool"; + break; + + case TYPE_INTEGER: + { + const Integer_type* it = t->integer_type(); + if (it->is_unsigned()) + os << 'u'; + os << "int" << it->bits() << "_t"; + } + break; + + case TYPE_FLOAT: + switch (t->float_type()->bits()) + { + case 32: + os << "float"; + break; + case 64: + os << "double"; + break; + default: + go_unreachable(); + } + break; + + case TYPE_COMPLEX: + switch (t->complex_type()->bits()) + { + case 64: + os << "float _Complex"; + break; + case 128: + os << "double _Complex"; + break; + default: + go_unreachable(); + } + break; + + case TYPE_STRING: + os << "String"; + break; + + case TYPE_FUNCTION: + os << "FuncVal*"; + break; + + case TYPE_POINTER: + { + std::vector<const Named_object*> requires; + std::vector<const Named_object*> declare; + if (!this->can_write_type_to_c_header(t->points_to(), &requires, + &declare)) + os << "void*"; + else + { + this->write_field_to_c_header(os, "", t->points_to()); + os << '*'; + } + } + break; + + case TYPE_MAP: + os << "Map*"; + break; + + case TYPE_CHANNEL: + os << "Chan*"; + break; + + case TYPE_INTERFACE: + if (t->interface_type()->is_empty()) + os << "Eface"; + else + os << "Iface"; + break; + + case TYPE_STRUCT: + os << "struct {" << std::endl; + t->struct_type()->write_to_c_header(os); + os << "\t}"; + break; + + case TYPE_ARRAY: + if (t->is_slice_type()) + os << "Slice"; + else + { + const Type *ele = t; + std::vector<const Type*> array_types; + while (ele->array_type() != NULL && !ele->is_slice_type()) + { + array_types.push_back(ele); + ele = ele->array_type()->element_type(); + } + this->write_field_to_c_header(os, "", ele); + os << ' ' << Gogo::message_name(name); + print_name = false; + while (!array_types.empty()) + { + ele = array_types.back(); + array_types.pop_back(); + os << '['; + Numeric_constant nc; + if (!ele->array_type()->length()->numeric_constant_value(&nc)) + go_unreachable(); + mpz_t val; + if (!nc.to_int(&val)) + go_unreachable(); + char* s = mpz_get_str(NULL, 10, val); + os << s; + free(s); + mpz_clear(val); + os << ']'; + } + } + break; + + case TYPE_NAMED: + { + const Named_object* no = t->named_type()->named_object(); + if (t->struct_type() != NULL) + os << "struct " << no->message_name(); + else if (t->is_unsafe_pointer_type()) + os << "void*"; + else if (t == Type::lookup_integer_type("uintptr")) + os << "uintptr_t"; + else + { + this->write_field_to_c_header(os, name, t->base()); + print_name = false; + } + } + break; + + case TYPE_ERROR: + case TYPE_FORWARD: + case TYPE_CALL_MULTIPLE_RESULT: + case TYPE_NIL: + case TYPE_SINK: + default: + go_unreachable(); + } + + if (print_name && !name.empty()) + os << ' ' << Gogo::message_name(name); +} + // Make a struct type. Struct_type* @@ -5703,6 +6369,9 @@ Array_type::is_identical(const Array_type* t, bool errors_are_identical) const errors_are_identical, NULL)) return false; + if (this->is_array_incomparable_ != t->is_array_incomparable_) + return false; + Expression* l1 = this->length(); Expression* l2 = t->length(); @@ -5769,7 +6438,7 @@ Array_type::verify_length() if (!this->length_->is_constant()) { - error_at(this->length_->location(), "array bound is not constant"); + go_error_at(this->length_->location(), "array bound is not constant"); return false; } @@ -5778,9 +6447,9 @@ Array_type::verify_length() { if (this->length_->type()->integer_type() != NULL || this->length_->type()->float_type() != NULL) - error_at(this->length_->location(), "array bound is not constant"); + go_error_at(this->length_->location(), "array bound is not constant"); else - error_at(this->length_->location(), "array bound is not numeric"); + go_error_at(this->length_->location(), "array bound is not numeric"); return false; } @@ -5792,15 +6461,15 @@ Array_type::verify_length() case Numeric_constant::NC_UL_VALID: if (sizeof(val) >= tbits / 8 && val >> (tbits - 1) != 0) { - error_at(this->length_->location(), "array bound overflows"); + go_error_at(this->length_->location(), "array bound overflows"); return false; } break; case Numeric_constant::NC_UL_NOTINT: - error_at(this->length_->location(), "array bound truncated to integer"); + go_error_at(this->length_->location(), "array bound truncated to integer"); return false; case Numeric_constant::NC_UL_NEGATIVE: - error_at(this->length_->location(), "negative array bound"); + go_error_at(this->length_->location(), "negative array bound"); return false; case Numeric_constant::NC_UL_BIG: { @@ -5811,7 +6480,7 @@ Array_type::verify_length() mpz_clear(val); if (bits >= tbits) { - error_at(this->length_->location(), "array bound overflows"); + go_error_at(this->length_->location(), "array bound overflows"); return false; } } @@ -5867,9 +6536,14 @@ Array_type::do_compare_is_identity(Gogo* gogo) unsigned int Array_type::do_hash_for_method(Gogo* gogo) const { + unsigned int ret; + // There is no very convenient way to get a hash code for the // length. - return this->element_type_->hash_for_method(gogo) + 1; + ret = this->element_type_->hash_for_method(gogo) + 1; + if (this->is_array_incomparable_) + ret <<= 1; + return ret; } // Write the hash function for an array which can not use the identify @@ -5888,18 +6562,20 @@ Array_type::write_hash_function(Gogo* gogo, Named_type* name, go_assert(key_arg != NULL); Type* key_arg_type = key_arg->var_value()->type(); - Type* uintptr_type = Type::lookup_integer_type("uintptr"); + // The seed argument to the hash function. + Named_object* seed_arg = gogo->lookup("seed", NULL); + go_assert(seed_arg != NULL); - // Get a 0. - Expression* zero = Expression::make_integer_ul(0, uintptr_type, bloc); + Type* uintptr_type = Type::lookup_integer_type("uintptr"); - // Make a temporary to hold the return value, initialized to 0. - Temporary_statement* retval = Statement::make_temporary(uintptr_type, zero, + // Make a temporary to hold the return value, initialized to the seed. + Expression* ref = Expression::make_var_reference(seed_arg, bloc); + Temporary_statement* retval = Statement::make_temporary(uintptr_type, ref, bloc); gogo->add_statement(retval); // Make a temporary to hold the key as a uintptr. - Expression* ref = Expression::make_var_reference(key_arg, bloc); + ref = Expression::make_var_reference(key_arg, bloc); ref = Expression::make_cast(uintptr_type, ref, bloc); Temporary_statement* key = Statement::make_temporary(uintptr_type, ref, bloc); @@ -5924,14 +6600,6 @@ Array_type::write_hash_function(Gogo* gogo, Named_type* name, gogo->start_block(bloc); - // Multiply retval by 33. - Expression* i33 = Expression::make_integer_ul(33, uintptr_type, bloc); - - ref = Expression::make_temporary_reference(retval, bloc); - Statement* s = Statement::make_assignment_operation(OPERATOR_MULTEQ, ref, - i33, bloc); - gogo->add_statement(s); - // Get the hash function for the element type. Named_object* hash_fn; Named_object* equal_fn; @@ -5947,18 +6615,19 @@ Array_type::write_hash_function(Gogo* gogo, Named_type* name, Expression* ele_size = Expression::make_type_info(this->element_type_, Expression::TYPE_INFO_SIZE); - // Get the hash of this element. + // Get the hash of this element, passing retval as the seed. + ref = Expression::make_temporary_reference(retval, bloc); Expression_list* args = new Expression_list(); args->push_back(subkey); - args->push_back(ele_size); + args->push_back(ref); Expression* func = Expression::make_func_reference(hash_fn, NULL, bloc); Expression* call = Expression::make_call(func, args, false, bloc); - // Add the element's hash value to retval. + // Set retval to the result. Temporary_reference_expression* tref = Expression::make_temporary_reference(retval, bloc); tref->set_is_lvalue(); - s = Statement::make_assignment_operation(OPERATOR_PLUSEQ, tref, call, bloc); + Statement* s = Statement::make_assignment(tref, call, bloc); gogo->add_statement(s); // Increase the element pointer. @@ -6340,7 +7009,7 @@ Array_type::array_type_descriptor(Gogo* gogo, Named_type* name) vals->reserve(3); Struct_field_list::const_iterator p = fields->begin(); - go_assert(p->is_field_name("commonType")); + go_assert(p->is_field_name("_type")); vals->push_back(this->type_descriptor_constructor(gogo, RUNTIME_TYPE_KIND_ARRAY, name, NULL, true)); @@ -6379,7 +7048,7 @@ Array_type::slice_type_descriptor(Gogo* gogo, Named_type* name) vals->reserve(2); Struct_field_list::const_iterator p = fields->begin(); - go_assert(p->is_field_name("commonType")); + go_assert(p->is_field_name("_type")); vals->push_back(this->type_descriptor_constructor(gogo, RUNTIME_TYPE_KIND_SLICE, name, NULL, true)); @@ -6563,6 +7232,8 @@ Array_type::do_mangled_name(Gogo* gogo, std::string* ret) const ret->append(s); free(s); mpz_clear(val); + if (this->is_array_incomparable_) + ret->push_back('x'); } ret->push_back('e'); } @@ -6577,6 +7248,103 @@ Type::make_array_type(Type* element_type, Expression* length) // Class Map_type. +Named_object* Map_type::zero_value; +int64_t Map_type::zero_value_size; +int64_t Map_type::zero_value_align; + +// If this map requires the "fat" functions, return the pointer to +// pass as the zero value to those functions. Otherwise, in the +// normal case, return NULL. The map requires the "fat" functions if +// the value size is larger than max_zero_size bytes. max_zero_size +// must match maxZero in libgo/go/runtime/hashmap.go. + +Expression* +Map_type::fat_zero_value(Gogo* gogo) +{ + int64_t valsize; + if (!this->val_type_->backend_type_size(gogo, &valsize)) + { + go_assert(saw_errors()); + return NULL; + } + if (valsize <= Map_type::max_zero_size) + return NULL; + + if (Map_type::zero_value_size < valsize) + Map_type::zero_value_size = valsize; + + int64_t valalign; + if (!this->val_type_->backend_type_align(gogo, &valalign)) + { + go_assert(saw_errors()); + return NULL; + } + + if (Map_type::zero_value_align < valalign) + Map_type::zero_value_align = valalign; + + Location bloc = Linemap::predeclared_location(); + + if (Map_type::zero_value == NULL) + { + // The final type will be set in backend_zero_value. + Type* uint8_type = Type::lookup_integer_type("uint8"); + Expression* size = Expression::make_integer_ul(0, NULL, bloc); + Array_type* array_type = Type::make_array_type(uint8_type, size); + array_type->set_is_array_incomparable(); + Variable* var = new Variable(array_type, NULL, true, false, false, bloc); + Map_type::zero_value = Named_object::make_variable("go$zerovalue", NULL, + var); + } + + Expression* z = Expression::make_var_reference(Map_type::zero_value, bloc); + z = Expression::make_unary(OPERATOR_AND, z, bloc); + Type* unsafe_ptr_type = Type::make_pointer_type(Type::make_void_type()); + z = Expression::make_cast(unsafe_ptr_type, z, bloc); + return z; +} + +// Return whether VAR is the map zero value. + +bool +Map_type::is_zero_value(Variable* var) +{ + return (Map_type::zero_value != NULL + && Map_type::zero_value->var_value() == var); +} + +// Return the backend representation for the zero value. + +Bvariable* +Map_type::backend_zero_value(Gogo* gogo) +{ + Location bloc = Linemap::predeclared_location(); + + go_assert(Map_type::zero_value != NULL); + + Type* uint8_type = Type::lookup_integer_type("uint8"); + Btype* buint8_type = uint8_type->get_backend(gogo); + + Type* int_type = Type::lookup_integer_type("int"); + + Expression* e = Expression::make_integer_int64(Map_type::zero_value_size, + int_type, bloc); + Translate_context context(gogo, NULL, NULL, NULL); + Bexpression* blength = e->get_backend(&context); + + Btype* barray_type = gogo->backend()->array_type(buint8_type, blength); + + std::string zname = Map_type::zero_value->name(); + std::string asm_name(go_selectively_encode_id(zname)); + Bvariable* zvar = + gogo->backend()->implicit_variable(zname, asm_name, + barray_type, false, true, true, + Map_type::zero_value_align); + gogo->backend()->implicit_variable_set_init(zvar, zname, barray_type, + false, true, true, NULL); + return zvar; +} + // Traversal. int @@ -6595,7 +7363,7 @@ Map_type::do_verify() { // The runtime support uses "map[void]void". if (!this->key_type_->is_comparable() && !this->key_type_->is_void_type()) - error_at(this->location_, "invalid map key type"); + go_error_at(this->location_, "invalid map key type"); return true; } @@ -6621,8 +7389,8 @@ Map_type::do_hash_for_method(Gogo* gogo) const } // Get the backend representation for a map type. A map type is -// represented as a pointer to a struct. The struct is __go_map in -// libgo/map.h. +// represented as a pointer to a struct. The struct is hmap in +// runtime/hashmap.go. Btype* Map_type::do_get_backend(Gogo* gogo) @@ -6630,33 +7398,55 @@ Map_type::do_get_backend(Gogo* gogo) static Btype* backend_map_type; if (backend_map_type == NULL) { - std::vector<Backend::Btyped_identifier> bfields(4); + std::vector<Backend::Btyped_identifier> bfields(9); Location bloc = Linemap::predeclared_location(); - Type* pdt = Type::make_type_descriptor_ptr_type(); - bfields[0].name = "__descriptor"; - bfields[0].btype = pdt->get_backend(gogo); + Type* int_type = Type::lookup_integer_type("int"); + bfields[0].name = "count"; + bfields[0].btype = int_type->get_backend(gogo); bfields[0].location = bloc; - Type* uintptr_type = Type::lookup_integer_type("uintptr"); - bfields[1].name = "__element_count"; - bfields[1].btype = uintptr_type->get_backend(gogo); + Type* uint8_type = Type::lookup_integer_type("uint8"); + bfields[1].name = "flags"; + bfields[1].btype = uint8_type->get_backend(gogo); bfields[1].location = bloc; - bfields[2].name = "__bucket_count"; + bfields[2].name = "B"; bfields[2].btype = bfields[1].btype; bfields[2].location = bloc; + Type* uint16_type = Type::lookup_integer_type("uint16"); + bfields[3].name = "noverflow"; + bfields[3].btype = uint16_type->get_backend(gogo); + bfields[3].location = bloc; + + Type* uint32_type = Type::lookup_integer_type("uint32"); + bfields[4].name = "hash0"; + bfields[4].btype = uint32_type->get_backend(gogo); + bfields[4].location = bloc; + Btype* bvt = gogo->backend()->void_type(); Btype* bpvt = gogo->backend()->pointer_type(bvt); - Btype* bppvt = gogo->backend()->pointer_type(bpvt); - bfields[3].name = "__buckets"; - bfields[3].btype = bppvt; - bfields[3].location = bloc; + bfields[5].name = "buckets"; + bfields[5].btype = bpvt; + bfields[5].location = bloc; + + bfields[6].name = "oldbuckets"; + bfields[6].btype = bpvt; + bfields[6].location = bloc; + + Type* uintptr_type = Type::lookup_integer_type("uintptr"); + bfields[7].name = "nevacuate"; + bfields[7].btype = uintptr_type->get_backend(gogo); + bfields[7].location = bloc; + + bfields[8].name = "overflow"; + bfields[8].btype = bpvt; + bfields[8].location = bloc; Btype *bt = gogo->backend()->struct_type(bfields); - bt = gogo->backend()->named_type("__go_map", bt, bloc); + bt = gogo->backend()->named_type("runtime.hmap", bt, bloc); backend_map_type = gogo->backend()->pointer_type(bt); } return backend_map_type; @@ -6672,12 +7462,24 @@ Map_type::make_map_type_descriptor_type() { Type* tdt = Type::make_type_descriptor_type(); Type* ptdt = Type::make_type_descriptor_ptr_type(); + Type* uint8_type = Type::lookup_integer_type("uint8"); + Type* uint16_type = Type::lookup_integer_type("uint16"); + Type* bool_type = Type::lookup_bool_type(); Struct_type* sf = - Type::make_builtin_struct_type(3, + Type::make_builtin_struct_type(12, "", tdt, "key", ptdt, - "elem", ptdt); + "elem", ptdt, + "bucket", ptdt, + "hmap", ptdt, + "keysize", uint8_type, + "indirectkey", bool_type, + "valuesize", uint8_type, + "indirectvalue", bool_type, + "bucketsize", uint16_type, + "reflexivekey", bool_type, + "needkeyupdate", bool_type); ret = Type::make_builtin_named_type("MapType", sf); } @@ -6693,14 +7495,51 @@ Map_type::do_type_descriptor(Gogo* gogo, Named_type* name) Location bloc = Linemap::predeclared_location(); Type* mtdt = Map_type::make_map_type_descriptor_type(); + Type* uint8_type = Type::lookup_integer_type("uint8"); + Type* uint16_type = Type::lookup_integer_type("uint16"); + + int64_t keysize; + if (!this->key_type_->backend_type_size(gogo, &keysize)) + { + go_error_at(this->location_, "error determining map key type size"); + return Expression::make_error(this->location_); + } + + int64_t valsize; + if (!this->val_type_->backend_type_size(gogo, &valsize)) + { + go_error_at(this->location_, "error determining map value type size"); + return Expression::make_error(this->location_); + } + + int64_t ptrsize; + if (!Type::make_pointer_type(uint8_type)->backend_type_size(gogo, &ptrsize)) + { + go_assert(saw_errors()); + return Expression::make_error(this->location_); + } + + Type* bucket_type = this->bucket_type(gogo, keysize, valsize); + if (bucket_type == NULL) + { + go_assert(saw_errors()); + return Expression::make_error(this->location_); + } + + int64_t bucketsize; + if (!bucket_type->backend_type_size(gogo, &bucketsize)) + { + go_assert(saw_errors()); + return Expression::make_error(this->location_); + } const Struct_field_list* fields = mtdt->struct_type()->fields(); Expression_list* vals = new Expression_list(); - vals->reserve(3); + vals->reserve(12); Struct_field_list::const_iterator p = fields->begin(); - go_assert(p->is_field_name("commonType")); + go_assert(p->is_field_name("_type")); vals->push_back(this->type_descriptor_constructor(gogo, RUNTIME_TYPE_KIND_MAP, name, NULL, true)); @@ -6714,130 +7553,271 @@ Map_type::do_type_descriptor(Gogo* gogo, Named_type* name) vals->push_back(Expression::make_type_descriptor(this->val_type_, bloc)); ++p; - go_assert(p == fields->end()); - - return Expression::make_struct_composite_literal(mtdt, vals, bloc); -} - -// A mapping from map types to map descriptors. - -Map_type::Map_descriptors Map_type::map_descriptors; + go_assert(p->is_field_name("bucket")); + vals->push_back(Expression::make_type_descriptor(bucket_type, bloc)); -// Build a map descriptor for this type. Return a pointer to it. - -Bexpression* -Map_type::map_descriptor_pointer(Gogo* gogo, Location location) -{ - Bvariable* bvar = this->map_descriptor(gogo); - Bexpression* var_expr = gogo->backend()->var_expression(bvar, location); - return gogo->backend()->address_expression(var_expr, location); -} + ++p; + go_assert(p->is_field_name("hmap")); + Type* hmap_type = this->hmap_type(bucket_type); + vals->push_back(Expression::make_type_descriptor(hmap_type, bloc)); -// Build a map descriptor for this type. + ++p; + go_assert(p->is_field_name("keysize")); + if (keysize > Map_type::max_key_size) + vals->push_back(Expression::make_integer_int64(ptrsize, uint8_type, bloc)); + else + vals->push_back(Expression::make_integer_int64(keysize, uint8_type, bloc)); -Bvariable* -Map_type::map_descriptor(Gogo* gogo) -{ - std::pair<Map_type*, Bvariable*> val(this, NULL); - std::pair<Map_type::Map_descriptors::iterator, bool> ins = - Map_type::map_descriptors.insert(val); - if (!ins.second) - return ins.first->second; + ++p; + go_assert(p->is_field_name("indirectkey")); + vals->push_back(Expression::make_boolean(keysize > Map_type::max_key_size, + bloc)); - Type* key_type = this->key_type_; - Type* val_type = this->val_type_; + ++p; + go_assert(p->is_field_name("valuesize")); + if (valsize > Map_type::max_val_size) + vals->push_back(Expression::make_integer_int64(ptrsize, uint8_type, bloc)); + else + vals->push_back(Expression::make_integer_int64(valsize, uint8_type, bloc)); - // The map entry type is a struct with three fields. Build that - // struct so that we can get the offsets of the key and value within - // a map entry. The first field should technically be a pointer to - // this type itself, but since we only care about field offsets we - // just use pointer to bool. - Type* pbool = Type::make_pointer_type(Type::make_boolean_type()); - Struct_type* map_entry_type = - Type::make_builtin_struct_type(3, - "__next", pbool, - "__key", key_type, - "__val", val_type); + ++p; + go_assert(p->is_field_name("indirectvalue")); + vals->push_back(Expression::make_boolean(valsize > Map_type::max_val_size, + bloc)); - Type* map_descriptor_type = Map_type::make_map_descriptor_type(); + ++p; + go_assert(p->is_field_name("bucketsize")); + vals->push_back(Expression::make_integer_int64(bucketsize, uint16_type, + bloc)); - const Struct_field_list* fields = - map_descriptor_type->struct_type()->fields(); + ++p; + go_assert(p->is_field_name("reflexivekey")); + vals->push_back(Expression::make_boolean(this->key_type_->is_reflexive(), + bloc)); - Expression_list* vals = new Expression_list(); - vals->reserve(4); + ++p; + go_assert(p->is_field_name("needkeyupdate")); + vals->push_back(Expression::make_boolean(this->key_type_->needs_key_update(), + bloc)); - Location bloc = Linemap::predeclared_location(); + ++p; + go_assert(p == fields->end()); - Struct_field_list::const_iterator p = fields->begin(); + return Expression::make_struct_composite_literal(mtdt, vals, bloc); +} - go_assert(p->is_field_name("__map_descriptor")); - vals->push_back(Expression::make_type_descriptor(this, bloc)); +// Return the bucket type to use for a map type. This must correspond +// to libgo/go/runtime/hashmap.go. - ++p; - go_assert(p->is_field_name("__entry_size")); - Expression::Type_info type_info = Expression::TYPE_INFO_SIZE; - vals->push_back(Expression::make_type_info(map_entry_type, type_info)); +Type* +Map_type::bucket_type(Gogo* gogo, int64_t keysize, int64_t valsize) +{ + if (this->bucket_type_ != NULL) + return this->bucket_type_; - Struct_field_list::const_iterator pf = map_entry_type->fields()->begin(); - ++pf; - go_assert(pf->is_field_name("__key")); + Type* key_type = this->key_type_; + if (keysize > Map_type::max_key_size) + key_type = Type::make_pointer_type(key_type); - ++p; - go_assert(p->is_field_name("__key_offset")); - vals->push_back(Expression::make_struct_field_offset(map_entry_type, &*pf)); + Type* val_type = this->val_type_; + if (valsize > Map_type::max_val_size) + val_type = Type::make_pointer_type(val_type); + + Expression* bucket_size = Expression::make_integer_ul(Map_type::bucket_size, + NULL, this->location_); + + Type* uint8_type = Type::lookup_integer_type("uint8"); + Array_type* topbits_type = Type::make_array_type(uint8_type, bucket_size); + topbits_type->set_is_array_incomparable(); + Array_type* keys_type = Type::make_array_type(key_type, bucket_size); + keys_type->set_is_array_incomparable(); + Array_type* values_type = Type::make_array_type(val_type, bucket_size); + values_type->set_is_array_incomparable(); + + // If keys and values have no pointers, the map implementation can + // keep a list of overflow pointers on the side so that buckets can + // be marked as having no pointers. Arrange for the bucket to have + // no pointers by changing the type of the overflow field to uintptr + // in this case. See comment on the hmap.overflow field in + // libgo/go/runtime/hashmap.go. + Type* overflow_type; + if (!key_type->has_pointer() && !val_type->has_pointer()) + overflow_type = Type::lookup_integer_type("uintptr"); + else + { + // This should really be a pointer to the bucket type itself, + // but that would require us to construct a Named_type for it to + // give it a way to refer to itself. Since nothing really cares + // (except perhaps for someone using a debugger) just use an + // unsafe pointer. + overflow_type = Type::make_pointer_type(Type::make_void_type()); + } + + // Make sure the overflow pointer is the last memory in the struct, + // because the runtime assumes it can use size-ptrSize as the offset + // of the overflow pointer. We double-check that property below + // once the offsets and size are computed. + + int64_t topbits_field_size, topbits_field_align; + int64_t keys_field_size, keys_field_align; + int64_t values_field_size, values_field_align; + int64_t overflow_field_size, overflow_field_align; + if (!topbits_type->backend_type_size(gogo, &topbits_field_size) + || !topbits_type->backend_type_field_align(gogo, &topbits_field_align) + || !keys_type->backend_type_size(gogo, &keys_field_size) + || !keys_type->backend_type_field_align(gogo, &keys_field_align) + || !values_type->backend_type_size(gogo, &values_field_size) + || !values_type->backend_type_field_align(gogo, &values_field_align) + || !overflow_type->backend_type_size(gogo, &overflow_field_size) + || !overflow_type->backend_type_field_align(gogo, &overflow_field_align)) + { + go_assert(saw_errors()); + return NULL; + } - ++pf; - go_assert(pf->is_field_name("__val")); + Struct_type* ret; + int64_t max_align = std::max(std::max(topbits_field_align, keys_field_align), + values_field_align); + if (max_align <= overflow_field_align) + ret = make_builtin_struct_type(4, + "topbits", topbits_type, + "keys", keys_type, + "values", values_type, + "overflow", overflow_type); + else + { + size_t off = topbits_field_size; + off = ((off + keys_field_align - 1) + &~ static_cast<size_t>(keys_field_align - 1)); + off += keys_field_size; + off = ((off + values_field_align - 1) + &~ static_cast<size_t>(values_field_align - 1)); + off += values_field_size; + + int64_t padded_overflow_field_size = + ((overflow_field_size + max_align - 1) + &~ static_cast<size_t>(max_align - 1)); + + size_t ovoff = off; + ovoff = ((ovoff + max_align - 1) + &~ static_cast<size_t>(max_align - 1)); + size_t pad = (ovoff - off + + padded_overflow_field_size - overflow_field_size); + + Expression* pad_expr = Expression::make_integer_ul(pad, NULL, + this->location_); + Array_type* pad_type = Type::make_array_type(uint8_type, pad_expr); + pad_type->set_is_array_incomparable(); + + ret = make_builtin_struct_type(5, + "topbits", topbits_type, + "keys", keys_type, + "values", values_type, + "pad", pad_type, + "overflow", overflow_type); + } + + // Verify that the overflow field is just before the end of the + // bucket type. + + Btype* btype = ret->get_backend(gogo); + int64_t offset = gogo->backend()->type_field_offset(btype, + ret->field_count() - 1); + int64_t size; + if (!ret->backend_type_size(gogo, &size)) + { + go_assert(saw_errors()); + return NULL; + } - ++p; - go_assert(p->is_field_name("__val_offset")); - vals->push_back(Expression::make_struct_field_offset(map_entry_type, &*pf)); + int64_t ptr_size; + if (!Type::make_pointer_type(uint8_type)->backend_type_size(gogo, &ptr_size)) + { + go_assert(saw_errors()); + return NULL; + } - ++p; - go_assert(p == fields->end()); + go_assert(offset + ptr_size == size); - Expression* initializer = - Expression::make_struct_composite_literal(map_descriptor_type, vals, bloc); + ret->set_is_struct_incomparable(); - std::string mangled_name = "__go_map_" + this->mangled_name(gogo); - Btype* map_descriptor_btype = map_descriptor_type->get_backend(gogo); - Bvariable* bvar = gogo->backend()->immutable_struct(mangled_name, false, - true, - map_descriptor_btype, - bloc); + this->bucket_type_ = ret; + return ret; +} - Translate_context context(gogo, NULL, NULL, NULL); - context.set_is_const(); - Bexpression* binitializer = initializer->get_backend(&context); +// Return the hashmap type for a map type. - gogo->backend()->immutable_struct_set_init(bvar, mangled_name, false, true, - map_descriptor_btype, bloc, - binitializer); +Type* +Map_type::hmap_type(Type* bucket_type) +{ + if (this->hmap_type_ != NULL) + return this->hmap_type_; - ins.first->second = bvar; - return bvar; + Type* int_type = Type::lookup_integer_type("int"); + Type* uint8_type = Type::lookup_integer_type("uint8"); + Type* uint32_type = Type::lookup_integer_type("uint32"); + Type* uintptr_type = Type::lookup_integer_type("uintptr"); + Type* void_ptr_type = Type::make_pointer_type(Type::make_void_type()); + + Type* ptr_bucket_type = Type::make_pointer_type(bucket_type); + + Struct_type* ret = make_builtin_struct_type(8, + "count", int_type, + "flags", uint8_type, + "B", uint8_type, + "hash0", uint32_type, + "buckets", ptr_bucket_type, + "oldbuckets", ptr_bucket_type, + "nevacuate", uintptr_type, + "overflow", void_ptr_type); + ret->set_is_struct_incomparable(); + this->hmap_type_ = ret; + return ret; } -// Build the type of a map descriptor. This must match the struct -// __go_map_descriptor in libgo/runtime/map.h. +// Return the iterator type for a map type. This is the type of the +// value used when doing a range over a map. Type* -Map_type::make_map_descriptor_type() +Map_type::hiter_type(Gogo* gogo) { - static Type* ret; - if (ret == NULL) + if (this->hiter_type_ != NULL) + return this->hiter_type_; + + int64_t keysize, valsize; + if (!this->key_type_->backend_type_size(gogo, &keysize) + || !this->val_type_->backend_type_size(gogo, &valsize)) { - Type* ptdt = Type::make_type_descriptor_ptr_type(); - Type* uintptr_type = Type::lookup_integer_type("uintptr"); - Struct_type* sf = - Type::make_builtin_struct_type(4, - "__map_descriptor", ptdt, - "__entry_size", uintptr_type, - "__key_offset", uintptr_type, - "__val_offset", uintptr_type); - ret = Type::make_builtin_named_type("__go_map_descriptor", sf); + go_assert(saw_errors()); + return NULL; } + + Type* key_ptr_type = Type::make_pointer_type(this->key_type_); + Type* val_ptr_type = Type::make_pointer_type(this->val_type_); + Type* uint8_type = Type::lookup_integer_type("uint8"); + Type* uint8_ptr_type = Type::make_pointer_type(uint8_type); + Type* uintptr_type = Type::lookup_integer_type("uintptr"); + Type* bucket_type = this->bucket_type(gogo, keysize, valsize); + Type* bucket_ptr_type = Type::make_pointer_type(bucket_type); + Type* hmap_type = this->hmap_type(bucket_type); + Type* hmap_ptr_type = Type::make_pointer_type(hmap_type); + Type* void_ptr_type = Type::make_pointer_type(Type::make_void_type()); + + Struct_type* ret = make_builtin_struct_type(12, + "key", key_ptr_type, + "val", val_ptr_type, + "t", uint8_ptr_type, + "h", hmap_ptr_type, + "buckets", bucket_ptr_type, + "bptr", bucket_ptr_type, + "overflow0", void_ptr_type, + "overflow1", void_ptr_type, + "startBucket", uintptr_type, + "stuff", uintptr_type, + "bucket", uintptr_type, + "checkBucket", uintptr_type); + ret->set_is_struct_incomparable(); + this->hiter_type_ = ret; return ret; } @@ -6998,7 +7978,7 @@ Channel_type::do_type_descriptor(Gogo* gogo, Named_type* name) vals->reserve(3); Struct_field_list::const_iterator p = fields->begin(); - go_assert(p->is_field_name("commonType")); + go_assert(p->is_field_name("_type")); vals->push_back(this->type_descriptor_constructor(gogo, RUNTIME_TYPE_KIND_CHAN, name, NULL, true)); @@ -7118,6 +8098,53 @@ Channel_type::do_import(Import* imp) return Type::make_channel_type(may_send, may_receive, element_type); } +// Return the type to manage a select statement with ncases case +// statements. A value of this type is allocated on the stack. This +// must match the type hselect in libgo/go/runtime/select.go. + +Type* +Channel_type::select_type(int ncases) +{ + Type* unsafe_pointer_type = Type::make_pointer_type(Type::make_void_type()); + Type* uint16_type = Type::lookup_integer_type("uint16"); + + static Struct_type* scase_type; + if (scase_type == NULL) + { + Type* uintptr_type = Type::lookup_integer_type("uintptr"); + Type* uint64_type = Type::lookup_integer_type("uint64"); + scase_type = + Type::make_builtin_struct_type(7, + "elem", unsafe_pointer_type, + "chan", unsafe_pointer_type, + "pc", uintptr_type, + "kind", uint16_type, + "index", uint16_type, + "receivedp", unsafe_pointer_type, + "releasetime", uint64_type); + scase_type->set_is_struct_incomparable(); + } + + Expression* ncases_expr = + Expression::make_integer_ul(ncases, NULL, Linemap::predeclared_location()); + Array_type* scases = Type::make_array_type(scase_type, ncases_expr); + scases->set_is_array_incomparable(); + Array_type* order = Type::make_array_type(uint16_type, ncases_expr); + order->set_is_array_incomparable(); + + Struct_type* ret = + Type::make_builtin_struct_type(7, + "tcase", uint16_type, + "ncase", uint16_type, + "pollorder", unsafe_pointer_type, + "lockorder", unsafe_pointer_type, + "scase", scases, + "lockorderarr", order, + "pollorderarr", order); + ret->set_is_struct_incomparable(); + return ret; +} + // Make a new channel type. Channel_type* @@ -7184,7 +8211,7 @@ Interface_type::finalize_methods() else if (this->find_method(p->name()) == NULL) this->all_methods_->push_back(*p); else - error_at(p->location(), "duplicate method %qs", + go_error_at(p->location(), "duplicate method %qs", Gogo::message_name(p->name()).c_str()); } @@ -7201,14 +8228,14 @@ Interface_type::finalize_methods() if (it == NULL) { if (!t->is_error()) - error_at(tl, "interface contains embedded non-interface"); + go_error_at(tl, "interface contains embedded non-interface"); continue; } if (it == this) { if (!issued_recursive_error) { - error_at(tl, "invalid recursive interface"); + go_error_at(tl, "invalid recursive interface"); issued_recursive_error = true; } continue; @@ -7222,7 +8249,7 @@ Interface_type::finalize_methods() { if (*q == nt) { - error_at(tl, "inherited interface loop"); + go_error_at(tl, "inherited interface loop"); break; } } @@ -7244,7 +8271,7 @@ Interface_type::finalize_methods() this->all_methods_->push_back(Typed_identifier(q->name(), q->type(), tl)); else - error_at(tl, "inherited method %qs is ambiguous", + go_error_at(tl, "inherited method %qs is ambiguous", Gogo::message_name(q->name()).c_str()); } } @@ -7394,8 +8421,8 @@ Interface_type::is_compatible_for_assign(const Interface_type* t, char buf[200]; snprintf(buf, sizeof buf, _("need explicit conversion; missing method %s%s%s"), - open_quote, Gogo::message_name(p->name()).c_str(), - close_quote); + go_open_quote(), Gogo::message_name(p->name()).c_str(), + go_close_quote()); reason->assign(buf); } return false; @@ -7411,11 +8438,11 @@ Interface_type::is_compatible_for_assign(const Interface_type* t, char* buf = new char[len]; if (subreason.empty()) snprintf(buf, len, _("incompatible type for method %s%s%s"), - open_quote, n.c_str(), close_quote); + go_open_quote(), n.c_str(), go_close_quote()); else snprintf(buf, len, _("incompatible type for method %s%s%s (%s)"), - open_quote, n.c_str(), close_quote, + go_open_quote(), n.c_str(), go_close_quote(), subreason.c_str()); reason->assign(buf); delete[] buf; @@ -7530,10 +8557,10 @@ Interface_type::implements_interface(const Type* t, std::string* reason) const char* buf = new char[len]; if (is_ambiguous) snprintf(buf, len, _("ambiguous method %s%s%s"), - open_quote, n.c_str(), close_quote); + go_open_quote(), n.c_str(), go_close_quote()); else snprintf(buf, len, _("missing method %s%s%s"), - open_quote, n.c_str(), close_quote); + go_open_quote(), n.c_str(), go_close_quote()); reason->assign(buf); delete[] buf; } @@ -7553,11 +8580,11 @@ Interface_type::implements_interface(const Type* t, std::string* reason) const char* buf = new char[len]; if (subreason.empty()) snprintf(buf, len, _("incompatible type for method %s%s%s"), - open_quote, n.c_str(), close_quote); + go_open_quote(), n.c_str(), go_close_quote()); else snprintf(buf, len, _("incompatible type for method %s%s%s (%s)"), - open_quote, n.c_str(), close_quote, + go_open_quote(), n.c_str(), go_close_quote(), subreason.c_str()); reason->assign(buf); delete[] buf; @@ -7574,7 +8601,7 @@ Interface_type::implements_interface(const Type* t, std::string* reason) const char* buf = new char[len]; snprintf(buf, len, _("method %s%s%s requires a pointer receiver"), - open_quote, n.c_str(), close_quote); + go_open_quote(), n.c_str(), go_close_quote()); reason->assign(buf); delete[] buf; } @@ -7592,7 +8619,7 @@ Interface_type::implements_interface(const Type* t, std::string* reason) const char* buf = new char[len]; snprintf(buf, len, _("method %s%s%s is marked go:nointerface"), - open_quote, n.c_str(), close_quote); + go_open_quote(), n.c_str(), go_close_quote()); reason->assign(buf); delete[] buf; } @@ -7840,7 +8867,7 @@ Interface_type::do_type_descriptor(Gogo* gogo, Named_type* name) ivals->reserve(2); Struct_field_list::const_iterator pif = ifields->begin(); - go_assert(pif->is_field_name("commonType")); + go_assert(pif->is_field_name("_type")); const int rt = RUNTIME_TYPE_KIND_INTERFACE; ivals->push_back(this->type_descriptor_constructor(gogo, rt, name, NULL, true)); @@ -7982,17 +9009,7 @@ Interface_type::do_mangled_name(Gogo* gogo, std::string* ret) const { if (!p->name().empty()) { - std::string n; - if (!Gogo::is_hidden_name(p->name())) - n = p->name(); - else - { - n = "."; - std::string pkgpath = Gogo::hidden_name_pkgpath(p->name()); - n.append(Gogo::pkgpath_for_symbol(pkgpath)); - n.append(1, '.'); - n.append(Gogo::unpack_hidden_name(p->name())); - } + std::string n(Gogo::mangle_possibly_hidden_name(p->name())); char buf[20]; snprintf(buf, sizeof buf, "%u_", static_cast<unsigned int>(n.length())); @@ -8375,18 +9392,6 @@ Named_type::message_name() const return this->named_object_->message_name(); } -// Whether this is an alias. There are currently only two aliases so -// we just recognize them by name. - -bool -Named_type::is_alias() const -{ - if (!this->is_builtin()) - return false; - const std::string& name(this->name()); - return name == "byte" || name == "rune"; -} - // Return the base type for this type. We have to be careful about // circular type definitions, which are invalid but may be seen here. @@ -8446,6 +9451,7 @@ Named_type::named_type_is_comparable(std::string* reason) const Named_object* Named_type::add_method(const std::string& name, Function* function) { + go_assert(!this->is_alias_); if (this->local_methods_ == NULL) this->local_methods_ = new Bindings(NULL); return this->local_methods_->add_function(name, NULL, function); @@ -8458,6 +9464,7 @@ Named_type::add_method_declaration(const std::string& name, Package* package, Function_type* type, Location location) { + go_assert(!this->is_alias_); if (this->local_methods_ == NULL) this->local_methods_ = new Bindings(NULL); return this->local_methods_->add_function_declaration(name, package, type, @@ -8469,6 +9476,7 @@ Named_type::add_method_declaration(const std::string& name, Package* package, void Named_type::add_existing_method(Named_object* no) { + go_assert(!this->is_alias_); if (this->local_methods_ == NULL) this->local_methods_ = new Bindings(NULL); this->local_methods_->add_named_object(no); @@ -8480,11 +9488,51 @@ Named_type::add_existing_method(Named_object* no) Named_object* Named_type::find_local_method(const std::string& name) const { + if (this->is_error_) + return NULL; + if (this->is_alias_) + { + Named_type* nt = this->type_->named_type(); + if (nt != NULL) + { + if (this->seen_alias_) + return NULL; + this->seen_alias_ = true; + Named_object* ret = nt->find_local_method(name); + this->seen_alias_ = false; + return ret; + } + return NULL; + } if (this->local_methods_ == NULL) return NULL; return this->local_methods_->lookup(name); } +// Return the list of local methods. + +const Bindings* +Named_type::local_methods() const +{ + if (this->is_error_) + return NULL; + if (this->is_alias_) + { + Named_type* nt = this->type_->named_type(); + if (nt != NULL) + { + if (this->seen_alias_) + return NULL; + this->seen_alias_ = true; + const Bindings* ret = nt->local_methods(); + this->seen_alias_ = false; + return ret; + } + return NULL; + } + return this->local_methods_; +} + // Return whether NAME is an unexported field or method, for better // error reporting. @@ -8492,6 +9540,22 @@ bool Named_type::is_unexported_local_method(Gogo* gogo, const std::string& name) const { + if (this->is_error_) + return false; + if (this->is_alias_) + { + Named_type* nt = this->type_->named_type(); + if (nt != NULL) + { + if (this->seen_alias_) + return false; + this->seen_alias_ = true; + bool ret = nt->is_unexported_local_method(gogo, name); + this->seen_alias_ = false; + return ret; + } + return false; + } Bindings* methods = this->local_methods_; if (methods != NULL) { @@ -8516,6 +9580,8 @@ Named_type::is_unexported_local_method(Gogo* gogo, void Named_type::finalize_methods(Gogo* gogo) { + if (this->is_alias_) + return; if (this->all_methods_ != NULL) return; @@ -8526,8 +9592,8 @@ Named_type::finalize_methods(Gogo* gogo) for (Bindings::const_declarations_iterator p = lm->begin_declarations(); p != lm->end_declarations(); ++p) - error_at(p->second->location(), - "invalid pointer or interface receiver type"); + go_error_at(p->second->location(), + "invalid pointer or interface receiver type"); delete this->local_methods_; this->local_methods_ = NULL; return; @@ -8536,6 +9602,56 @@ Named_type::finalize_methods(Gogo* gogo) Type::finalize_methods(gogo, this, this->location_, &this->all_methods_); } +// Return whether this type has any methods. + +bool +Named_type::has_any_methods() const +{ + if (this->is_error_) + return false; + if (this->is_alias_) + { + if (this->type_->named_type() != NULL) + { + if (this->seen_alias_) + return false; + this->seen_alias_ = true; + bool ret = this->type_->named_type()->has_any_methods(); + this->seen_alias_ = false; + return ret; + } + if (this->type_->struct_type() != NULL) + return this->type_->struct_type()->has_any_methods(); + return false; + } + return this->all_methods_ != NULL; +} + +// Return the methods for this type. + +const Methods* +Named_type::methods() const +{ + if (this->is_error_) + return NULL; + if (this->is_alias_) + { + if (this->type_->named_type() != NULL) + { + if (this->seen_alias_) + return NULL; + this->seen_alias_ = true; + const Methods* ret = this->type_->named_type()->methods(); + this->seen_alias_ = false; + return ret; + } + if (this->type_->struct_type() != NULL) + return this->type_->struct_type()->methods(); + return NULL; + } + return this->all_methods_; +} + // Return the method NAME, or NULL if there isn't one or if it is // ambiguous. Set *IS_AMBIGUOUS if the method exists but is // ambiguous. @@ -8543,6 +9659,26 @@ Named_type::finalize_methods(Gogo* gogo) Method* Named_type::method_function(const std::string& name, bool* is_ambiguous) const { + if (this->is_error_) + return NULL; + if (this->is_alias_) + { + if (is_ambiguous != NULL) + *is_ambiguous = false; + if (this->type_->named_type() != NULL) + { + if (this->seen_alias_) + return NULL; + this->seen_alias_ = true; + Named_type* nt = this->type_->named_type(); + Method* ret = nt->method_function(name, is_ambiguous); + this->seen_alias_ = false; + return ret; + } + if (this->type_->struct_type() != NULL) + return this->type_->struct_type()->method_function(name, is_ambiguous); + return NULL; + } return Type::method_function(this->all_methods_, name, is_ambiguous); } @@ -8553,6 +9689,25 @@ Named_type::method_function(const std::string& name, bool* is_ambiguous) const Expression* Named_type::interface_method_table(Interface_type* interface, bool is_pointer) { + if (this->is_error_) + return Expression::make_error(this->location_); + if (this->is_alias_) + { + if (this->type_->named_type() != NULL) + { + if (this->seen_alias_) + return Expression::make_error(this->location_); + this->seen_alias_ = true; + Named_type* nt = this->type_->named_type(); + Expression* ret = nt->interface_method_table(interface, is_pointer); + this->seen_alias_ = false; + return ret; + } + if (this->type_->struct_type() != NULL) + return this->type_->struct_type()->interface_method_table(interface, + is_pointer); + go_unreachable(); + } return Type::interface_method_table(this, interface, is_pointer, &this->interface_method_tables_, &this->pointer_interface_method_tables_); @@ -8671,6 +9826,55 @@ Find_type_use::type(Type* type) return TRAVERSE_CONTINUE; } +// Look for a circular reference of an alias. + +class Find_alias : public Traverse +{ + public: + Find_alias(Named_type* find_type) + : Traverse(traverse_types), + find_type_(find_type), found_(false) + { } + + // Whether we found the type. + bool + found() const + { return this->found_; } + + protected: + int + type(Type*); + + private: + // The type we are looking for. + Named_type* find_type_; + // Whether we found the type. + bool found_; +}; + +int +Find_alias::type(Type* type) +{ + Named_type* nt = type->named_type(); + if (nt != NULL) + { + if (nt == this->find_type_) + { + this->found_ = true; + return TRAVERSE_EXIT; + } + + // We started from `type T1 = T2`, where T1 is find_type_ and T2 + // is, perhaps indirectly, the parameter TYPE. If TYPE is not + // an alias itself, it's OK if whatever T2 is defined as refers + // to T1. + if (!nt->is_alias()) + return TRAVERSE_SKIP_COMPONENTS; + } + + return TRAVERSE_CONTINUE; +} + // Verify that a named type does not refer to itself. bool @@ -8680,12 +9884,28 @@ Named_type::do_verify() return true; this->is_verified_ = true; + if (this->is_error_) + return false; + + if (this->is_alias_) + { + Find_alias find(this); + Type::traverse(this->type_, &find); + if (find.found()) + { + go_error_at(this->location_, "invalid recursive alias %qs", + this->message_name().c_str()); + this->is_error_ = true; + return false; + } + } + Find_type_use find(this); Type::traverse(this->type_, &find); if (find.found()) { - error_at(this->location_, "invalid recursive type %qs", - this->message_name().c_str()); + go_error_at(this->location_, "invalid recursive type %qs", + this->message_name().c_str()); this->is_error_ = true; return false; } @@ -8707,9 +9927,9 @@ Named_type::do_verify() const std::string& name(p->first); if (st != NULL && st->find_local_field(name, NULL) != NULL) { - error_at(p->second->location(), - "method %qs redeclares struct field name", - Gogo::message_name(name).c_str()); + go_error_at(p->second->location(), + "method %qs redeclares struct field name", + Gogo::message_name(name).c_str()); } } } @@ -8747,14 +9967,44 @@ Named_type::do_compare_is_identity(Gogo* gogo) return ret; } +// Return whether this type is reflexive--whether it is always equal +// to itself. + +bool +Named_type::do_is_reflexive() +{ + if (this->seen_in_compare_is_identity_) + return false; + this->seen_in_compare_is_identity_ = true; + bool ret = this->type_->is_reflexive(); + this->seen_in_compare_is_identity_ = false; + return ret; +} + +// Return whether this type needs a key update when used as a map key. + +bool +Named_type::do_needs_key_update() +{ + if (this->seen_in_compare_is_identity_) + return true; + this->seen_in_compare_is_identity_ = true; + bool ret = this->type_->needs_key_update(); + this->seen_in_compare_is_identity_ = false; + return ret; +} + // Return a hash code. This is used for method lookup. We simply // hash on the name itself. unsigned int Named_type::do_hash_for_method(Gogo* gogo) const { - if (this->is_alias()) - return this->type_->named_type()->do_hash_for_method(gogo); + if (this->is_error_) + return 0; + + // Aliases are handled in Type::hash_for_method. + go_assert(!this->is_alias_); const std::string& name(this->named_object()->name()); unsigned int ret = Type::hash_string(name, 0); @@ -9124,8 +10374,17 @@ Named_type::do_get_backend(Gogo* gogo) Expression* Named_type::do_type_descriptor(Gogo* gogo, Named_type* name) { - if (name == NULL && this->is_alias()) - return this->type_->type_descriptor(gogo, this->type_); + if (this->is_error_) + return Expression::make_error(this->location_); + if (name == NULL && this->is_alias_) + { + if (this->seen_alias_) + return Expression::make_error(this->location_); + this->seen_alias_ = true; + Expression* ret = this->type_->type_descriptor(gogo, NULL); + this->seen_alias_ = false; + return ret; + } // If NAME is not NULL, then we don't really want the type // descriptor for this type; we want the descriptor for the @@ -9141,9 +10400,25 @@ Named_type::do_type_descriptor(Gogo* gogo, Named_type* name) void Named_type::do_reflection(Gogo* gogo, std::string* ret) const { - if (this->is_alias()) + this->append_reflection_type_name(gogo, false, ret); +} + +// Add to the reflection string. For an alias we normally use the +// real name, but if USE_ALIAS is true we use the alias name itself. + +void +Named_type::append_reflection_type_name(Gogo* gogo, bool use_alias, + std::string* ret) const +{ + if (this->is_error_) + return; + if (this->is_alias_ && !use_alias) { + if (this->seen_alias_) + return; + this->seen_alias_ = true; this->append_reflection(this->type_, gogo, ret); + this->seen_alias_ = false; return; } if (!this->is_builtin()) @@ -9208,9 +10483,25 @@ Named_type::do_gc_symbol(Gogo* gogo, Expression_list** vals, void Named_type::do_mangled_name(Gogo* gogo, std::string* ret) const { - if (this->is_alias()) + this->append_mangled_type_name(gogo, false, ret); +} + +// Get the mangled name. For an alias we normally get the real name, +// but if USE_ALIAS is true we use the alias name itself. + +void +Named_type::append_mangled_type_name(Gogo* gogo, bool use_alias, + std::string* ret) const +{ + if (this->is_error_) + return; + if (this->is_alias_ && !use_alias) { + if (this->seen_alias_) + return; + this->seen_alias_ = true; this->append_mangled_name(this->type_, gogo, ret); + this->seen_alias_ = false; return; } Named_object* no = this->named_object_; @@ -9869,9 +11160,9 @@ Type::bind_field_or_method(Gogo* gogo, const Type* type, Expression* expr, go_assert(m != NULL); if (dereferenced) { - error_at(location, - "calling method %qs requires explicit dereference", - Gogo::message_name(name).c_str()); + go_error_at(location, + "calling method %qs requires explicit dereference", + Gogo::message_name(name).c_str()); return Expression::make_error(location); } if (!m->is_value_method() && expr->type()->points_to() == NULL) @@ -9888,16 +11179,16 @@ Type::bind_field_or_method(Gogo* gogo, const Type* type, Expression* expr, // An error was already reported. } else if (!ambig1.empty()) - error_at(location, "%qs is ambiguous via %qs and %qs", - Gogo::message_name(name).c_str(), ambig1.c_str(), - ambig2.c_str()); + go_error_at(location, "%qs is ambiguous via %qs and %qs", + Gogo::message_name(name).c_str(), ambig1.c_str(), + ambig2.c_str()); else if (found_pointer_method) - error_at(location, "method requires a pointer receiver"); + go_error_at(location, "method requires a pointer receiver"); else if (nt == NULL && st == NULL && it == NULL) - error_at(location, - ("reference to field %qs in object which " - "has no fields or methods"), - Gogo::message_name(name).c_str()); + go_error_at(location, + ("reference to field %qs in object which " + "has no fields or methods"), + Gogo::message_name(name).c_str()); else { bool is_unexported; @@ -9914,11 +11205,11 @@ Type::bind_field_or_method(Gogo* gogo, const Type* type, Expression* expr, &seen); } if (is_unexported) - error_at(location, "reference to unexported field or method %qs", - Gogo::message_name(name).c_str()); + go_error_at(location, "reference to unexported field or method %qs", + Gogo::message_name(name).c_str()); else - error_at(location, "reference to undefined field or method %qs", - Gogo::message_name(name).c_str()); + go_error_at(location, "reference to undefined field or method %qs", + Gogo::message_name(name).c_str()); } return Expression::make_error(location); } @@ -10267,9 +11558,9 @@ Forward_declaration_type::warn() const // The name was not defined anywhere. if (!this->warned_) { - error_at(this->named_object_->location(), - "use of undefined type %qs", - no->message_name().c_str()); + go_error_at(this->named_object_->location(), + "use of undefined type %qs", + no->message_name().c_str()); this->warned_ = true; } } @@ -10278,9 +11569,9 @@ Forward_declaration_type::warn() const // The name was seen as a type, but the type was never defined. if (no->type_declaration_value()->using_type()) { - error_at(this->named_object_->location(), - "use of undefined type %qs", - no->message_name().c_str()); + go_error_at(this->named_object_->location(), + "use of undefined type %qs", + no->message_name().c_str()); this->warned_ = true; } } @@ -10289,7 +11580,7 @@ Forward_declaration_type::warn() const // The name was defined, but not as a type. if (!this->warned_) { - error_at(this->named_object_->location(), "expected type"); + go_error_at(this->named_object_->location(), "expected type"); this->warned_ = true; } } @@ -10427,7 +11718,7 @@ Forward_declaration_type::do_type_descriptor(Gogo* gogo, Named_type* name) if (name != NULL) return this->named_type_descriptor(gogo, t, name); else - return Expression::make_type_descriptor(t, ploc); + return Expression::make_error(this->named_object_->location()); } } diff --git a/gcc/go/gofrontend/types.h b/gcc/go/gofrontend/types.h index 8d2a982533..47a70fcd08 100644 --- a/gcc/go/gofrontend/types.h +++ b/gcc/go/gofrontend/types.h @@ -7,11 +7,14 @@ #ifndef GO_TYPES_H #define GO_TYPES_H +#include <ostream> + #include "go-linemap.h" #include "escape.h" class Gogo; class Package; +class Variable; class Traverse; class Typed_identifier; class Typed_identifier_list; @@ -627,6 +630,18 @@ class Type compare_is_identity(Gogo* gogo) { return this->do_compare_is_identity(gogo); } + // Return whether values of this type are reflexive: if a comparison + // of a value with itself always returns true. + bool + is_reflexive() + { return this->do_is_reflexive(); } + + // Return whether values of this, when used as a key in map, + // requires the key to be updated when an assignment is made. + bool + needs_key_update() + { return this->do_needs_key_update(); } + // Return a hash code for this type for the method hash table. // Types which are equivalent according to are_identical will have // the same hash code. @@ -960,6 +975,10 @@ class Type bool is_backend_type_size_known(Gogo*); + // Return whether the type needs specially built type functions. + bool + needs_specific_type_functions(Gogo*); + // Get the hash and equality functions for a type. void type_functions(Gogo*, Named_type* name, Function_type* hash_fntype, @@ -968,12 +987,15 @@ class Type // Write the hash and equality type functions. void - write_specific_type_functions(Gogo*, Named_type*, + write_specific_type_functions(Gogo*, Named_type*, int64_t size, const std::string& hash_name, Function_type* hash_fntype, const std::string& equal_name, Function_type* equal_fntype); + // Return the alignment required by the memequalN function. + static int64_t memequal_align(Gogo*, int size); + // Export the type. void export_type(Export* exp) const @@ -1004,6 +1026,14 @@ class Type virtual bool do_compare_is_identity(Gogo*) = 0; + virtual bool + do_is_reflexive() + { return true; } + + virtual bool + do_needs_key_update() + { return false; } + virtual unsigned int do_hash_for_method(Gogo*) const; @@ -1183,11 +1213,18 @@ class Type // Build the hash and equality type functions for a type which needs // specific functions. void - specific_type_functions(Gogo*, Named_type*, Function_type* hash_fntype, + specific_type_functions(Gogo*, Named_type*, int64_t size, + Function_type* hash_fntype, Function_type* equal_fntype, Named_object** hash_fn, Named_object** equal_fn); void + write_identity_hash(Gogo*, int64_t size); + + void + write_identity_equal(Gogo*, int64_t size); + + void write_named_hash(Gogo*, Named_type*, Function_type* hash_fntype, Function_type* equal_fntype); @@ -1325,7 +1362,7 @@ class Typed_identifier public: Typed_identifier(const std::string& name, Type* type, Location location) - : name_(name), type_(type), location_(location) + : name_(name), type_(type), location_(location), note_(NULL) { } // Get the name. @@ -1352,6 +1389,16 @@ class Typed_identifier this->type_ = type; } + // Get the escape note. + std::string* + note() const + { return this->note_; } + + // Set the escape note. + void + set_note(const std::string& note) + { this->note_ = new std::string(note); } + private: // Identifier name. std::string name_; @@ -1359,6 +1406,9 @@ class Typed_identifier Type* type_; // The location where the name was seen. Location location_; + // Escape note for this typed identifier. Used when importing and exporting + // functions. + std::string* note_; }; // A list of Typed_identifiers. @@ -1423,6 +1473,10 @@ class Typed_identifier_list back() const { return this->entries_.back(); } + Typed_identifier& + at(size_t i) + { return this->entries_.at(i); } + const Typed_identifier& at(size_t i) const { return this->entries_.at(i); } @@ -1620,6 +1674,15 @@ class Float_type : public Type do_compare_is_identity(Gogo*) { return false; } + bool + do_is_reflexive() + { return false; } + + // Distinction between +0 and -0 requires a key update. + bool + do_needs_key_update() + { return true; } + unsigned int do_hash_for_method(Gogo*) const; @@ -1693,6 +1756,15 @@ class Complex_type : public Type do_compare_is_identity(Gogo*) { return false; } + bool + do_is_reflexive() + { return false; } + + // Distinction between +0 and -0 requires a key update. + bool + do_needs_key_update() + { return true; } + unsigned int do_hash_for_method(Gogo*) const; @@ -1749,6 +1821,11 @@ class String_type : public Type do_compare_is_identity(Gogo*) { return false; } + // New string might have a smaller backing store. + bool + do_needs_key_update() + { return true; } + Btype* do_get_backend(Gogo*); @@ -1779,7 +1856,7 @@ class Function_type : public Type : Type(TYPE_FUNCTION), receiver_(receiver), parameters_(parameters), results_(results), location_(location), is_varargs_(false), is_builtin_(false), - has_escape_info_(false), fnbtype_(NULL), parameter_escape_states_(NULL) + fnbtype_(NULL), is_tagged_(false) { } // Get the receiver. @@ -1787,15 +1864,10 @@ class Function_type : public Type receiver() const { return this->receiver_; } - // Get the escape state of the receiver. - const Node::Escapement_lattice& - receiver_escape_state() const - { return this->receiver_escape_state_; } - - // Set the escape state of the receiver. + // Add an escape note for the receiver. void - set_receiver_escape_state(const Node::Escapement_lattice& e) - { this->receiver_escape_state_ = e; } + add_receiver_note(int encoding) + { this->receiver_->set_note(Escape_note::make_tag(encoding)); } // Get the return names and types. const Typed_identifier_list* @@ -1807,15 +1879,20 @@ class Function_type : public Type parameters() const { return this->parameters_; } - // Get the escape states associated with each parameter. - const Node::Escape_states* - parameter_escape_states() const - { return this->parameter_escape_states_; } + // Add an escape note for the ith parameter. + void + add_parameter_note(int index, int encoding) + { this->parameters_->at(index).set_note(Escape_note::make_tag(encoding)); } + + // Whether this function has been tagged during escape analysis. + bool + is_tagged() const + { return this->is_tagged_; } - // Set the escape states of the parameters. + // Mark this function as tagged after analyzing its escape. void - set_parameter_escape_states(Node::Escape_states* states) - { this->parameter_escape_states_ = states; } + set_is_tagged() + { this->is_tagged_ = true; } // Whether this is a varargs function. bool @@ -1827,11 +1904,6 @@ class Function_type : public Type is_builtin() const { return this->is_builtin_; } - // Whether this contains escape information. - bool - has_escape_info() const - { return this->has_escape_info_; } - // The location where this type was defined. Location location() const @@ -1862,11 +1934,6 @@ class Function_type : public Type set_is_builtin() { this->is_builtin_ = true; } - // Record that this has escape information. - void - set_has_escape_info() - { this->has_escape_info_ = true; } - // Import a function type. static Function_type* do_import(Import*); @@ -1978,16 +2045,12 @@ class Function_type : public Type // Whether this is a special builtin function which can not simply // be called. This is used for len, cap, etc. bool is_builtin_; - // Whether escape information for the receiver and parameters has been - // recorded. - bool has_escape_info_; // The backend representation of this type for backend function // declarations and definitions. Btype* fnbtype_; - // The escape state of the receiver. - Node::Escapement_lattice receiver_escape_state_; - // The escape states of each parameter. - Node::Escape_states* parameter_escape_states_; + // Whether this function has been analyzed by escape analysis. If this is + // TRUE, this function type's parameters contain a summary of the analysis. + bool is_tagged_; }; // The type of a function's backend representation. @@ -2213,7 +2276,8 @@ class Struct_type : public Type public: Struct_type(Struct_field_list* fields, Location location) : Type(TYPE_STRUCT), - fields_(fields), location_(location), all_methods_(NULL) + fields_(fields), location_(location), all_methods_(NULL), + is_struct_incomparable_(false) { } // Return the field NAME. This only looks at local fields, not at @@ -2318,6 +2382,16 @@ class Struct_type : public Type static Type* make_struct_type_descriptor_type(); + // Return whether this is a generated struct that is not comparable. + bool + is_struct_incomparable() const + { return this->is_struct_incomparable_; } + + // Record that this is a generated struct that is not comparable. + void + set_is_struct_incomparable() + { this->is_struct_incomparable_ = true; } + // Write the hash function for this type. void write_hash_function(Gogo*, Named_type*, Function_type*, Function_type*); @@ -2326,6 +2400,16 @@ class Struct_type : public Type void write_equal_function(Gogo*, Named_type*); + // Whether we can write this type to a C header file, to implement + // -fgo-c-header. + bool + can_write_to_c_header(std::vector<const Named_object*>*, + std::vector<const Named_object*>*) const; + + // Write this type to a C header file, to implement -fgo-c-header. + void + write_to_c_header(std::ostream&) const; + protected: int do_traverse(Traverse*); @@ -2339,6 +2423,12 @@ class Struct_type : public Type bool do_compare_is_identity(Gogo*); + bool + do_is_reflexive(); + + bool + do_needs_key_update(); + unsigned int do_hash_for_method(Gogo*) const; @@ -2361,6 +2451,14 @@ class Struct_type : public Type do_export(Export*) const; private: + bool + can_write_type_to_c_header(const Type*, + std::vector<const Named_object*>*, + std::vector<const Named_object*>*) const; + + void + write_field_to_c_header(std::ostream&, const std::string&, const Type*) const; + // Used to merge method sets of identical unnamed structs. typedef Unordered_map_hash(Struct_type*, Struct_type*, Type_hash_identical, Type_identical) Identical_structs; @@ -2395,6 +2493,9 @@ class Struct_type : public Type Location location_; // If this struct is unnamed, a list of methods. Methods* all_methods_; + // True if this is a generated struct that is not considered to be + // comparable. + bool is_struct_incomparable_; }; // The type of an array. @@ -2405,7 +2506,7 @@ class Array_type : public Type Array_type(Type* element_type, Expression* length) : Type(TYPE_ARRAY), element_type_(element_type), length_(length), blength_(NULL), - issued_length_error_(false) + issued_length_error_(false), is_array_incomparable_(false) { } // Return the element type. @@ -2456,6 +2557,16 @@ class Array_type : public Type static Type* make_slice_type_descriptor_type(); + // Return whether this is a generated array that is not comparable. + bool + is_array_incomparable() const + { return this->is_array_incomparable_; } + + // Record that this is a generated array that is not comparable. + void + set_is_array_incomparable() + { this->is_array_incomparable_ = true; } + // Write the hash function for this type. void write_hash_function(Gogo*, Named_type*, Function_type*, Function_type*); @@ -2480,6 +2591,16 @@ class Array_type : public Type bool do_compare_is_identity(Gogo*); + bool + do_is_reflexive() + { + return this->length_ != NULL && this->element_type_->is_reflexive(); + } + + bool + do_needs_key_update() + { return this->element_type_->needs_key_update(); } + unsigned int do_hash_for_method(Gogo*) const; @@ -2527,6 +2648,9 @@ class Array_type : public Type // Whether or not an invalid length error has been issued for this type, // to avoid knock-on errors. mutable bool issued_length_error_; + // True if this is a generated array that is not considered to be + // comparable. + bool is_array_incomparable_; }; // The type of a map. @@ -2536,7 +2660,8 @@ class Map_type : public Type public: Map_type(Type* key_type, Type* val_type, Location location) : Type(TYPE_MAP), - key_type_(key_type), val_type_(val_type), location_(location) + key_type_(key_type), val_type_(val_type), hmap_type_(NULL), + bucket_type_(NULL), hiter_type_(NULL), location_(location) { } // Return the key type. @@ -2549,6 +2674,24 @@ class Map_type : public Type val_type() const { return this->val_type_; } + // Return the type used for an iteration over this map. + Type* + hiter_type(Gogo*); + + // If this map requires the "fat" functions, returns the pointer to + // pass as the zero value to those functions. Otherwise, in the + // normal case, returns NULL. + Expression* + fat_zero_value(Gogo*); + + // Return whether VAR is the map zero value. + static bool + is_zero_value(Variable* var); + + // Return the backend representation of the map zero value. + static Bvariable* + backend_zero_value(Gogo*); + // Whether this type is identical with T. bool is_identical(const Map_type* t, bool errors_are_identical) const; @@ -2560,15 +2703,6 @@ class Map_type : public Type static Type* make_map_type_descriptor_type(); - static Type* - make_map_descriptor_type(); - - // Build a map descriptor for this type. Return a pointer to it. - // The location is the location which causes us to need the - // descriptor. - Bexpression* - map_descriptor_pointer(Gogo* gogo, Location); - protected: int do_traverse(Traverse*); @@ -2584,6 +2718,12 @@ class Map_type : public Type do_compare_is_identity(Gogo*) { return false; } + bool + do_is_reflexive() + { + return this->key_type_->is_reflexive() && this->val_type_->is_reflexive(); + } + unsigned int do_hash_for_method(Gogo*) const; @@ -2606,18 +2746,41 @@ class Map_type : public Type do_export(Export*) const; private: - // Mapping from map types to map descriptors. - typedef Unordered_map_hash(const Map_type*, Bvariable*, Type_hash_identical, - Type_identical) Map_descriptors; - static Map_descriptors map_descriptors; + // These must be in sync with libgo/go/runtime/hashmap.go. + static const int bucket_size = 8; + static const int max_key_size = 128; + static const int max_val_size = 128; + static const int max_zero_size = 1024; + + // Maps with value types larger than max_zero_size require passing a + // zero value pointer to the map functions. + + // The zero value variable. + static Named_object* zero_value; + + // The current size of the zero value. + static int64_t zero_value_size; - Bvariable* - map_descriptor(Gogo*); + // The current alignment of the zero value. + static int64_t zero_value_align; + + Type* + bucket_type(Gogo*, int64_t, int64_t); + + Type* + hmap_type(Type*); // The key type. Type* key_type_; // The value type. Type* val_type_; + // The hashmap type. At run time a map is represented as a pointer + // to this type. + Type* hmap_type_; + // The bucket type, the type used to hold keys and values at run time. + Type* bucket_type_; + // The iterator type. + Type* hiter_type_; // Where the type was defined. Location location_; }; @@ -2660,6 +2823,9 @@ class Channel_type : public Type static Type* make_chan_type_descriptor_type(); + static Type* + select_type(int ncases); + protected: int do_traverse(Traverse* traverse) @@ -2809,6 +2975,17 @@ class Interface_type : public Type do_compare_is_identity(Gogo*) { return false; } + // Not reflexive if it contains a float. + bool + do_is_reflexive() + { return false; } + + // Distinction between +0 and -0 requires a key update if it + // contains a float. + bool + do_needs_key_update() + { return true; } + unsigned int do_hash_for_method(Gogo*) const; @@ -2887,10 +3064,10 @@ class Named_type : public Type type_(type), local_methods_(NULL), all_methods_(NULL), interface_method_tables_(NULL), pointer_interface_method_tables_(NULL), location_(location), named_btype_(NULL), dependencies_(), - is_visible_(true), is_error_(false), is_placeholder_(false), - is_converted_(false), is_circular_(false), is_verified_(false), - seen_(false), seen_in_compare_is_identity_(false), - seen_in_get_backend_(false) + is_alias_(false), is_visible_(true), is_error_(false), + is_placeholder_(false), is_converted_(false), is_circular_(false), + is_verified_(false), seen_(false), seen_in_compare_is_identity_(false), + seen_in_get_backend_(false), seen_alias_(false) { } // Return the associated Named_object. This holds the actual name. @@ -2908,6 +3085,17 @@ class Named_type : public Type set_named_object(Named_object* no) { this->named_object_ = no; } + // Whether this is an alias (type T1 = T2) rather than an ordinary + // named type (type T1 T2). + bool + is_alias() const + { return this->is_alias_; } + + // Record that this type is an alias. + void + set_is_alias() + { this->is_alias_ = true; } + // Return the function in which this type is defined. This will // return NULL for a type defined in global scope. const Named_object* @@ -2969,11 +3157,6 @@ class Named_type : public Type is_builtin() const { return Linemap::is_predeclared_location(this->location_); } - // Whether this is an alias. There are currently two aliases: byte - // and rune. - bool - is_alias() const; - // Whether this named type is valid. A recursive named type is invalid. bool is_valid() const @@ -3021,8 +3204,7 @@ class Named_type : public Type // Return the list of local methods. const Bindings* - local_methods() const - { return this->local_methods_; } + local_methods() const; // Build the complete list of methods, including those from // anonymous fields, and build method stubs if needed. @@ -3032,14 +3214,12 @@ class Named_type : public Type // Return whether this type has any methods. This should only be // called after the finalize_methods pass. bool - has_any_methods() const - { return this->all_methods_ != NULL; } + has_any_methods() const; // Return the methods for this type. This should only be called // after the finalized_methods pass. const Methods* - methods() const - { return this->all_methods_; } + methods() const; // Return the method to use for NAME. This returns NULL if there is // no such method or if the method is ambiguous. When it returns @@ -3072,6 +3252,16 @@ class Named_type : public Type is_named_backend_type_size_known() const { return this->named_btype_ != NULL && !this->is_placeholder_; } + // Add to the reflection string as for Type::append_reflection, but + // if USE_ALIAS use the alias name rather than the alias target. + void + append_reflection_type_name(Gogo*, bool use_alias, std::string*) const; + + // Append the mangled type name as for Type::append_mangled_name, + // but if USE_ALIAS use the alias name rather than the alias target. + void + append_mangled_type_name(Gogo*, bool use_alias, std::string*) const; + // Export the type. void export_named_type(Export*, const std::string& name) const; @@ -3098,6 +3288,12 @@ class Named_type : public Type bool do_compare_is_identity(Gogo*); + bool + do_is_reflexive(); + + bool + do_needs_key_update(); + unsigned int do_hash_for_method(Gogo*) const; @@ -3160,6 +3356,8 @@ class Named_type : public Type // where we can't convert S2 to the backend representation unless we // have converted S1. std::vector<Named_type*> dependencies_; + // Whether this is an alias type. + bool is_alias_; // Whether this type is visible. This is false if this type was // created because it was referenced by an imported object, but the // type itself was not exported. This will always be true for types @@ -3187,6 +3385,8 @@ class Named_type : public Type bool seen_in_compare_is_identity_; // Like seen_, but used only by do_get_backend. bool seen_in_get_backend_; + // Like seen_, but used when resolving aliases. + mutable bool seen_alias_; }; // A forward declaration. This handles a type which has been declared @@ -3245,6 +3445,14 @@ class Forward_declaration_type : public Type do_compare_is_identity(Gogo* gogo) { return this->real_type()->compare_is_identity(gogo); } + bool + do_is_reflexive() + { return this->real_type()->is_reflexive(); } + + bool + do_needs_key_update() + { return this->real_type()->needs_key_update(); } + unsigned int do_hash_for_method(Gogo* gogo) const { return this->real_type()->hash_for_method(gogo); } diff --git a/gcc/go/gospec.c b/gcc/go/gospec.c index 13c1b9c021..8c4a14f95e 100644 --- a/gcc/go/gospec.c +++ b/gcc/go/gospec.c @@ -1,5 +1,5 @@ /* gospec.c -- Specific flags and argument handling of the gcc Go front end. - Copyright (C) 2009-2016 Free Software Foundation, Inc. + Copyright (C) 2009-2017 Free Software Foundation, Inc. This file is part of GCC. diff --git a/gcc/go/lang-specs.h b/gcc/go/lang-specs.h index 5fd8e9e36b..8e8d360283 100644 --- a/gcc/go/lang-specs.h +++ b/gcc/go/lang-specs.h @@ -1,5 +1,5 @@ /* lang-specs.h -- gcc driver specs for Go frontend. - Copyright (C) 2009-2016 Free Software Foundation, Inc. + Copyright (C) 2009-2017 Free Software Foundation, Inc. This file is part of GCC. diff --git a/gcc/go/lang.opt b/gcc/go/lang.opt index 7d44e44800..527d82fc5a 100644 --- a/gcc/go/lang.opt +++ b/gcc/go/lang.opt @@ -1,6 +1,6 @@ ; lang.opt -- Options for the gcc Go front end. -; Copyright (C) 2009-2016 Free Software Foundation, Inc. +; Copyright (C) 2009-2017 Free Software Foundation, Inc. ; ; This file is part of GCC. ; @@ -37,6 +37,10 @@ Wall Go ; Documented in c.opt +fgo-c-header= +Go Joined RejectNegative +-fgo-c-header=<file> Write Go struct definitions to file as C code. + fgo-check-divide-zero Go Var(go_check_divide_zero) Init(1) Add explicit checks for division by zero. @@ -45,6 +49,10 @@ fgo-check-divide-overflow Go Var(go_check_divide_overflow) Init(1) Add explicit checks for division overflow in INT_MIN / -1. +fgo-compiling-runtime +Go Var(go_compiling_runtime) Init(0) +Apply special rules for compiling runtime package. + fgo-dump- Go Joined RejectNegative -fgo-dump-<type> Dump Go frontend internal information. @@ -63,12 +71,16 @@ Go Joined RejectNegative fgo-relative-import-path= Go Joined RejectNegative --fgo-relative-import-path=<path> Treat a relative import as relative to path. +-fgo-relative-import-path=<path> Treat a relative import as relative to path. frequire-return-statement Go Var(go_require_return_statement) Init(1) Warning Functions which return values must end with return statements. +fgo-debug-escape +Go Joined UInteger Var(go_debug_escape_level) Init(0) +Emit debugging information related to the escape analysis pass when run with -fgo-optimize-allocs. + o Go Joined Separate ; Documented in common.opt |