summaryrefslogtreecommitdiff
path: root/gcc/go
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/go')
-rw-r--r--gcc/go/ChangeLog196
-rw-r--r--gcc/go/Make-lang.in10
-rw-r--r--gcc/go/config-lang.in6
-rw-r--r--gcc/go/gccgo.texi105
-rw-r--r--gcc/go/go-backend.c18
-rw-r--r--gcc/go/go-c.h27
-rw-r--r--gcc/go/go-gcc-diagnostics.cc61
-rw-r--r--gcc/go/go-gcc.cc191
-rw-r--r--gcc/go/go-gcc.h33
-rw-r--r--gcc/go/go-lang.c30
-rw-r--r--gcc/go/go-linemap.cc40
-rw-r--r--gcc/go/go-location.h10
-rw-r--r--gcc/go/go-sha1.cc71
-rw-r--r--gcc/go/go-system.h3
-rw-r--r--gcc/go/gofrontend/MERGE2
-rw-r--r--gcc/go/gofrontend/ast-dump.cc14
-rw-r--r--gcc/go/gofrontend/backend.h102
-rw-r--r--gcc/go/gofrontend/dataflow.cc299
-rw-r--r--gcc/go/gofrontend/dataflow.h91
-rw-r--r--gcc/go/gofrontend/escape.cc3780
-rw-r--r--gcc/go/gofrontend/escape.h594
-rw-r--r--gcc/go/gofrontend/export.cc317
-rw-r--r--gcc/go/gofrontend/export.h65
-rw-r--r--gcc/go/gofrontend/expressions.cc2725
-rw-r--r--gcc/go/gofrontend/expressions.h623
-rw-r--r--gcc/go/gofrontend/go-diagnostics.cc177
-rw-r--r--gcc/go/gofrontend/go-diagnostics.h64
-rw-r--r--gcc/go/gofrontend/go-encode-id.cc113
-rw-r--r--gcc/go/gofrontend/go-encode-id.h30
-rw-r--r--gcc/go/gofrontend/go-linemap.h31
-rw-r--r--gcc/go/gofrontend/go-sha1.h33
-rw-r--r--gcc/go/gofrontend/go.cc67
-rw-r--r--gcc/go/gofrontend/gogo.cc1110
-rw-r--r--gcc/go/gofrontend/gogo.h365
-rw-r--r--gcc/go/gofrontend/import-archive.cc57
-rw-r--r--gcc/go/gofrontend/import.cc259
-rw-r--r--gcc/go/gofrontend/import.h26
-rw-r--r--gcc/go/gofrontend/lex.cc359
-rw-r--r--gcc/go/gofrontend/lex.h65
-rw-r--r--gcc/go/gofrontend/operator.h6
-rw-r--r--gcc/go/gofrontend/parse.cc673
-rw-r--r--gcc/go/gofrontend/parse.h5
-rw-r--r--gcc/go/gofrontend/runtime.cc111
-rw-r--r--gcc/go/gofrontend/runtime.def241
-rw-r--r--gcc/go/gofrontend/runtime.h6
-rw-r--r--gcc/go/gofrontend/statements.cc1157
-rw-r--r--gcc/go/gofrontend/statements.h201
-rw-r--r--gcc/go/gofrontend/types.cc1989
-rw-r--r--gcc/go/gofrontend/types.h346
-rw-r--r--gcc/go/gospec.c2
-rw-r--r--gcc/go/lang-specs.h2
-rw-r--r--gcc/go/lang.opt16
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, &parameters,
- &param_escapes, &results, &is_varargs,
- &has_escape_info);
+ Function::import_func(this, &name, &receiver,
+ &parameters, &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