aboutsummaryrefslogtreecommitdiff
path: root/gcc/go
diff options
context:
space:
mode:
authorIan Lance Taylor <iant@golang.org>2021-09-13 10:37:49 -0700
committerIan Lance Taylor <iant@golang.org>2021-09-13 10:37:49 -0700
commite252b51ccde010cbd2a146485d8045103cd99533 (patch)
treee060f101cdc32bf5e520de8e5275db9d4236b74c /gcc/go
parentf10c7c4596dda99d2ee872c995ae4aeda65adbdf (diff)
parent104c05c5284b7822d770ee51a7d91946c7e56d50 (diff)
downloadgcc-e252b51ccde010cbd2a146485d8045103cd99533.zip
gcc-e252b51ccde010cbd2a146485d8045103cd99533.tar.gz
gcc-e252b51ccde010cbd2a146485d8045103cd99533.tar.bz2
Merge from trunk revision 104c05c5284b7822d770ee51a7d91946c7e56d50.
Diffstat (limited to 'gcc/go')
-rw-r--r--gcc/go/ChangeLog17
-rw-r--r--gcc/go/gccgo.texi2
-rw-r--r--gcc/go/go-gcc.cc158
-rw-r--r--gcc/go/gofrontend/MERGE2
-rw-r--r--gcc/go/gofrontend/backend.h145
-rw-r--r--gcc/go/gofrontend/escape.cc164
-rw-r--r--gcc/go/gofrontend/export.cc44
-rw-r--r--gcc/go/gofrontend/export.h10
-rw-r--r--gcc/go/gofrontend/expressions.cc587
-rw-r--r--gcc/go/gofrontend/expressions.h106
-rw-r--r--gcc/go/gofrontend/gogo.cc94
-rw-r--r--gcc/go/gofrontend/gogo.h12
-rw-r--r--gcc/go/gofrontend/import.cc6
-rw-r--r--gcc/go/gofrontend/runtime.def19
-rw-r--r--gcc/go/gofrontend/statements.cc65
-rw-r--r--gcc/go/gofrontend/types.cc173
-rw-r--r--gcc/go/gofrontend/types.h20
-rw-r--r--gcc/go/gofrontend/unsafe.cc68
18 files changed, 1256 insertions, 436 deletions
diff --git a/gcc/go/ChangeLog b/gcc/go/ChangeLog
index 85bd343..60ebb89 100644
--- a/gcc/go/ChangeLog
+++ b/gcc/go/ChangeLog
@@ -1,3 +1,20 @@
+2021-06-29 Ian Lance Taylor <iant@golang.org>
+
+ * go-gcc.cc (Gcc_backend::static_chain_variable): Set
+ DECL_NAMELESS on the new decl.
+ (Gcc_backend::temporary_variable): Likewise.
+ (Gcc_backend::function): Set DECL_NAMELESS on the result decl.
+
+2021-05-27 Ian Lance Taylor <iant@golang.org>
+
+ * gccgo.texi (Function Names): Don't HTML quote ampersand.
+
+2021-05-24 Ian Lance Taylor <iant@golang.org>
+
+ PR go/100537
+ * go-gcc.cc (class Gcc_backend): Update methods that create
+ variables to take a flags parameter.
+
2021-01-28 Ian Lance Taylor <iant@golang.org>
* gospec.c (lang_specific_driver): Add -g if no debugging options
diff --git a/gcc/go/gccgo.texi b/gcc/go/gccgo.texi
index ce6b518..fa0e488 100644
--- a/gcc/go/gccgo.texi
+++ b/gcc/go/gccgo.texi
@@ -495,7 +495,7 @@ like (after importing the @code{os} package):
@smallexample
var name = [4]byte@{'f', 'o', 'o', 0@};
-i := c_open(&amp;name[0], os.O_RDONLY, 0);
+i := c_open(&name[0], os.O_RDONLY, 0);
@end smallexample
Note that this serves as an example only. To open a file in Go please
diff --git a/gcc/go/go-gcc.cc b/gcc/go/go-gcc.cc
index 5d9dbb5..f812796 100644
--- a/gcc/go/go-gcc.cc
+++ b/gcc/go/go-gcc.cc
@@ -415,47 +415,46 @@ class Gcc_backend : public Backend
global_variable(const std::string& var_name,
const std::string& asm_name,
Btype* btype,
- bool is_external,
- bool is_hidden,
- bool in_unique_section,
+ unsigned int flags,
Location location);
void
global_variable_set_init(Bvariable*, Bexpression*);
Bvariable*
- local_variable(Bfunction*, const std::string&, Btype*, Bvariable*, bool,
- Location);
+ local_variable(Bfunction*, const std::string&, Btype*, Bvariable*,
+ unsigned int, Location);
Bvariable*
- parameter_variable(Bfunction*, const std::string&, Btype*, bool,
+ parameter_variable(Bfunction*, const std::string&, Btype*, unsigned int,
Location);
Bvariable*
- static_chain_variable(Bfunction*, const std::string&, Btype*, Location);
+ static_chain_variable(Bfunction*, const std::string&, Btype*, unsigned int,
+ Location);
Bvariable*
- temporary_variable(Bfunction*, Bblock*, Btype*, Bexpression*, bool,
+ temporary_variable(Bfunction*, Bblock*, Btype*, Bexpression*, unsigned int,
Location, Bstatement**);
Bvariable*
implicit_variable(const std::string&, const std::string&, Btype*,
- bool, bool, bool, int64_t);
+ unsigned int, int64_t);
void
implicit_variable_set_init(Bvariable*, const std::string&, Btype*,
- bool, bool, bool, Bexpression*);
+ unsigned int, Bexpression*);
Bvariable*
implicit_variable_reference(const std::string&, const std::string&, Btype*);
Bvariable*
immutable_struct(const std::string&, const std::string&,
- bool, bool, Btype*, Location);
+ unsigned int, Btype*, Location);
void
- immutable_struct_set_init(Bvariable*, const std::string&, bool, bool, Btype*,
- Location, Bexpression*);
+ immutable_struct_set_init(Bvariable*, const std::string&, unsigned int,
+ Btype*, Location, Bexpression*);
Bvariable*
immutable_struct_reference(const std::string&, const std::string&,
@@ -2696,9 +2695,7 @@ Bvariable*
Gcc_backend::global_variable(const std::string& var_name,
const std::string& asm_name,
Btype* btype,
- bool is_external,
- bool is_hidden,
- bool in_unique_section,
+ unsigned int flags,
Location location)
{
tree type_tree = btype->get_tree();
@@ -2707,30 +2704,49 @@ Gcc_backend::global_variable(const std::string& var_name,
// The GNU linker does not like dynamic variables with zero size.
tree orig_type_tree = type_tree;
+ bool is_external = (flags & variable_is_external) != 0;
+ bool is_hidden = (flags & variable_is_hidden) != 0;
if ((is_external || !is_hidden) && int_size_in_bytes(type_tree) == 0)
type_tree = this->non_zero_size_type(type_tree);
tree decl = build_decl(location.gcc_location(), VAR_DECL,
get_identifier_from_string(var_name),
type_tree);
- if (is_external)
- DECL_EXTERNAL(decl) = 1;
- else
- TREE_STATIC(decl) = 1;
- if (!is_hidden)
+ if ((flags & variable_is_external) != 0)
{
- TREE_PUBLIC(decl) = 1;
- SET_DECL_ASSEMBLER_NAME(decl, get_identifier_from_string(asm_name));
+ DECL_EXTERNAL(decl) = 1;
+ flags &=~ variable_is_external;
}
else
+ TREE_STATIC(decl) = 1;
+
+ if ((flags & variable_is_hidden) == 0)
+ TREE_PUBLIC(decl) = 1;
+ else
+ flags &=~ variable_is_hidden;
+
+ if ((flags & variable_address_is_taken) != 0)
{
- SET_DECL_ASSEMBLER_NAME(decl, get_identifier_from_string(asm_name));
+ TREE_ADDRESSABLE(decl) = 1;
+ flags &=~ variable_address_is_taken;
}
+ // We take the address in Bvariable::get_tree if orig_type_tree is
+ // different from type_tree.
+ if (orig_type_tree != type_tree)
+ TREE_ADDRESSABLE(decl) = 1;
+
+ SET_DECL_ASSEMBLER_NAME(decl, get_identifier_from_string(asm_name));
+
TREE_USED(decl) = 1;
- if (in_unique_section)
- resolve_unique_section (decl, 0, 1);
+ if ((flags & variable_in_unique_section) != 0)
+ {
+ resolve_unique_section (decl, 0, 1);
+ flags &=~ variable_in_unique_section;
+ }
+
+ gcc_assert(flags == 0);
go_preserve_from_gc(decl);
@@ -2768,7 +2784,7 @@ Gcc_backend::global_variable_set_init(Bvariable* var, Bexpression* expr)
Bvariable*
Gcc_backend::local_variable(Bfunction* function, const std::string& name,
Btype* btype, Bvariable* decl_var,
- bool is_address_taken, Location location)
+ unsigned int flags, Location location)
{
tree type_tree = btype->get_tree();
if (type_tree == error_mark_node)
@@ -2778,13 +2794,17 @@ Gcc_backend::local_variable(Bfunction* function, const std::string& name,
type_tree);
DECL_CONTEXT(decl) = function->get_tree();
TREE_USED(decl) = 1;
- if (is_address_taken)
- TREE_ADDRESSABLE(decl) = 1;
+ if ((flags & variable_address_is_taken) != 0)
+ {
+ TREE_ADDRESSABLE(decl) = 1;
+ flags &=~ variable_address_is_taken;
+ }
if (decl_var != NULL)
{
DECL_HAS_VALUE_EXPR_P(decl) = 1;
SET_DECL_VALUE_EXPR(decl, decl_var->get_decl());
}
+ go_assert(flags == 0);
go_preserve_from_gc(decl);
return new Bvariable(decl);
}
@@ -2793,7 +2813,7 @@ Gcc_backend::local_variable(Bfunction* function, const std::string& name,
Bvariable*
Gcc_backend::parameter_variable(Bfunction* function, const std::string& name,
- Btype* btype, bool is_address_taken,
+ Btype* btype, unsigned int flags,
Location location)
{
tree type_tree = btype->get_tree();
@@ -2805,8 +2825,12 @@ Gcc_backend::parameter_variable(Bfunction* function, const std::string& name,
DECL_CONTEXT(decl) = function->get_tree();
DECL_ARG_TYPE(decl) = type_tree;
TREE_USED(decl) = 1;
- if (is_address_taken)
- TREE_ADDRESSABLE(decl) = 1;
+ if ((flags & variable_address_is_taken) != 0)
+ {
+ TREE_ADDRESSABLE(decl) = 1;
+ flags &=~ variable_address_is_taken;
+ }
+ go_assert(flags == 0);
go_preserve_from_gc(decl);
return new Bvariable(decl);
}
@@ -2815,7 +2839,8 @@ Gcc_backend::parameter_variable(Bfunction* function, const std::string& name,
Bvariable*
Gcc_backend::static_chain_variable(Bfunction* function, const std::string& name,
- Btype* btype, Location location)
+ Btype* btype, unsigned int flags,
+ Location location)
{
tree type_tree = btype->get_tree();
if (type_tree == error_mark_node)
@@ -2828,6 +2853,7 @@ Gcc_backend::static_chain_variable(Bfunction* function, const std::string& name,
TREE_USED(decl) = 1;
DECL_ARTIFICIAL(decl) = 1;
DECL_IGNORED_P(decl) = 1;
+ DECL_NAMELESS(decl) = 1;
TREE_READONLY(decl) = 1;
struct function *f = DECL_STRUCT_FUNCTION(fndecl);
@@ -2840,6 +2866,7 @@ Gcc_backend::static_chain_variable(Bfunction* function, const std::string& name,
gcc_assert(f->static_chain_decl == NULL);
f->static_chain_decl = decl;
DECL_STATIC_CHAIN(fndecl) = 1;
+ go_assert(flags == 0);
go_preserve_from_gc(decl);
return new Bvariable(decl);
@@ -2850,7 +2877,7 @@ Gcc_backend::static_chain_variable(Bfunction* function, const std::string& name,
Bvariable*
Gcc_backend::temporary_variable(Bfunction* function, Bblock* bblock,
Btype* btype, Bexpression* binit,
- bool is_address_taken,
+ unsigned int flags,
Location location,
Bstatement** pstatement)
{
@@ -2886,6 +2913,7 @@ Gcc_backend::temporary_variable(Bfunction* function, Bblock* bblock,
type_tree);
DECL_ARTIFICIAL(var) = 1;
DECL_IGNORED_P(var) = 1;
+ DECL_NAMELESS(var) = 1;
TREE_USED(var) = 1;
DECL_CONTEXT(var) = decl;
@@ -2904,8 +2932,13 @@ Gcc_backend::temporary_variable(Bfunction* function, Bblock* bblock,
&& TREE_TYPE(init_tree) != void_type_node)
DECL_INITIAL(var) = this->convert_tree(type_tree, init_tree, location);
- if (is_address_taken)
- TREE_ADDRESSABLE(var) = 1;
+ if ((flags & variable_address_is_taken) != 0)
+ {
+ TREE_ADDRESSABLE(var) = 1;
+ flags &=~ variable_address_is_taken;
+ }
+
+ gcc_assert(flags == 0);
*pstatement = this->make_statement(build1_loc(location.gcc_location(),
DECL_EXPR,
@@ -2929,8 +2962,8 @@ Gcc_backend::temporary_variable(Bfunction* function, Bblock* bblock,
Bvariable*
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)
+ Btype* type, unsigned int flags,
+ int64_t alignment)
{
tree type_tree = type->get_tree();
if (type_tree == error_mark_node)
@@ -2939,11 +2972,14 @@ Gcc_backend::implicit_variable(const std::string& name,
tree decl = build_decl(BUILTINS_LOCATION, VAR_DECL,
get_identifier_from_string(name), type_tree);
DECL_EXTERNAL(decl) = 0;
- TREE_PUBLIC(decl) = !is_hidden;
+ if ((flags & variable_is_hidden) != 0)
+ flags &=~ variable_is_hidden;
+ else
+ TREE_PUBLIC(decl) = 1;
TREE_STATIC(decl) = 1;
TREE_USED(decl) = 1;
DECL_ARTIFICIAL(decl) = 1;
- if (is_common)
+ if ((flags & variable_is_common) != 0)
{
DECL_COMMON(decl) = 1;
@@ -2960,11 +2996,19 @@ Gcc_backend::implicit_variable(const std::string& name,
// mark this symbol as weak here. We undo that below in
// immutable_struct_set_init before calling mark_decl_one_only.
DECL_WEAK(decl) = 1;
+
+ flags &=~ variable_is_common;
}
- if (is_constant)
+ if ((flags & variable_is_constant) != 0)
{
TREE_READONLY(decl) = 1;
TREE_CONSTANT(decl) = 1;
+ flags &=~ variable_is_constant;
+ }
+ if ((flags & variable_address_is_taken) != 0)
+ {
+ TREE_ADDRESSABLE(decl) = 1;
+ flags &=~ variable_address_is_taken;
}
if (alignment != 0)
{
@@ -2973,6 +3017,7 @@ Gcc_backend::implicit_variable(const std::string& name,
}
if (! asm_name.empty())
SET_DECL_ASSEMBLER_NAME(decl, get_identifier_from_string(asm_name));
+ gcc_assert(flags == 0);
go_preserve_from_gc(decl);
return new Bvariable(decl);
@@ -2983,7 +3028,7 @@ Gcc_backend::implicit_variable(const std::string& name,
void
Gcc_backend::implicit_variable_set_init(Bvariable* var, const std::string&,
- Btype*, bool, bool, bool is_common,
+ Btype*, unsigned int flags,
Bexpression* init)
{
tree decl = var->get_decl();
@@ -2999,7 +3044,7 @@ Gcc_backend::implicit_variable_set_init(Bvariable* var, const std::string&,
// Now that DECL_INITIAL is set, we can't call make_decl_one_only.
// See the comment where DECL_WEAK is set in implicit_variable.
- if (is_common)
+ if ((flags & variable_is_common) != 0)
{
DECL_WEAK(decl) = 0;
make_decl_one_only(decl, DECL_ASSEMBLER_NAME(decl));
@@ -3038,8 +3083,8 @@ Gcc_backend::implicit_variable_reference(const std::string& name,
Bvariable*
Gcc_backend::immutable_struct(const std::string& name,
const std::string& asm_name,
- bool is_hidden,
- bool is_common, Btype* btype, Location location)
+ unsigned int flags, Btype* btype,
+ Location location)
{
tree type_tree = btype->get_tree();
if (type_tree == error_mark_node)
@@ -3053,10 +3098,17 @@ Gcc_backend::immutable_struct(const std::string& name,
TREE_READONLY(decl) = 1;
TREE_CONSTANT(decl) = 1;
DECL_ARTIFICIAL(decl) = 1;
- if (!is_hidden)
+ if ((flags & variable_is_hidden) != 0)
+ flags &=~ variable_is_hidden;
+ else
TREE_PUBLIC(decl) = 1;
if (! asm_name.empty())
SET_DECL_ASSEMBLER_NAME(decl, get_identifier_from_string(asm_name));
+ if ((flags & variable_address_is_taken) != 0)
+ {
+ TREE_ADDRESSABLE(decl) = 1;
+ flags &=~ variable_address_is_taken;
+ }
// When the initializer for one immutable_struct refers to another,
// it needs to know the visibility of the referenced struct so that
@@ -3070,8 +3122,13 @@ Gcc_backend::immutable_struct(const std::string& name,
// the right value if some other initializer refers to this one, we
// mark this symbol as weak here. We undo that below in
// immutable_struct_set_init before calling mark_decl_one_only.
- if (is_common)
- DECL_WEAK(decl) = 1;
+ if ((flags & variable_is_common) != 0)
+ {
+ DECL_WEAK(decl) = 1;
+ flags &=~ variable_is_common;
+ }
+
+ gcc_assert(flags == 0);
// We don't call rest_of_decl_compilation until we have the
// initializer.
@@ -3085,7 +3142,7 @@ Gcc_backend::immutable_struct(const std::string& name,
void
Gcc_backend::immutable_struct_set_init(Bvariable* var, const std::string&,
- bool, bool is_common, Btype*, Location,
+ unsigned int flags, Btype*, Location,
Bexpression* initializer)
{
tree decl = var->get_decl();
@@ -3097,7 +3154,7 @@ Gcc_backend::immutable_struct_set_init(Bvariable* var, const std::string&,
// Now that DECL_INITIAL is set, we can't call make_decl_one_only.
// See the comment where DECL_WEAK is set in immutable_struct.
- if (is_common)
+ if ((flags & variable_is_common) != 0)
{
DECL_WEAK(decl) = 0;
make_decl_one_only(decl, DECL_ASSEMBLER_NAME(decl));
@@ -3235,6 +3292,7 @@ Gcc_backend::function(Btype* fntype, const std::string& name,
build_decl(location.gcc_location(), RESULT_DECL, NULL_TREE, restype);
DECL_ARTIFICIAL(resdecl) = 1;
DECL_IGNORED_P(resdecl) = 1;
+ DECL_NAMELESS(resdecl) = 1;
DECL_CONTEXT(resdecl) = decl;
DECL_RESULT(decl) = resdecl;
}
diff --git a/gcc/go/gofrontend/MERGE b/gcc/go/gofrontend/MERGE
index a3eef23..f481681 100644
--- a/gcc/go/gofrontend/MERGE
+++ b/gcc/go/gofrontend/MERGE
@@ -1,4 +1,4 @@
-4bdff733a0c2a9ddc3eff104b1be03df058a79c4
+c11d9f8275f2bbe9b05cdd815c79ac331f78e15c
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/backend.h b/gcc/go/gofrontend/backend.h
index c5b5d8f..5b6ffea 100644
--- a/gcc/go/gofrontend/backend.h
+++ b/gcc/go/gofrontend/backend.h
@@ -481,24 +481,51 @@ class Backend
virtual Bvariable*
error_variable() = 0;
+ // Bit flags to pass to the various methods that return Bvariable*.
+ // Not all flags are meaningful for all methods.
+
+ // Set if the variable's address is taken. For a local variable
+ // this implies that the address does not escape the function, as
+ // otherwise the variable would be on the heap.
+ static const unsigned int variable_address_is_taken = 1 << 0;
+
+ // Set if the variable is defined in some other package. Only
+ // meaningful for the global_variable method. At most one of
+ // is_external, is_hidden, and is_common may be set.
+ static const unsigned int variable_is_external = 1 << 1;
+
+ // Set if the variable is not exported, and as such is only defined
+ // in the current package. Only meaningful for global_variable,
+ // implicit_variable, and immutable_struct. At most one of
+ // is_external, is_hidden, and is_common may be set.
+ static const unsigned variable_is_hidden = 1 << 2;
+
+ // Set if the variable should be treated as a common variable:
+ // multiple definitions with different sizes permitted in different
+ // object files, all merged into the largest definition at link
+ // time. Only meaningful for implicit_variable and immutable_struct.
+ // At most one of is_external, is_hidden, and is_common may be set.
+ static const unsigned int variable_is_common = 1 << 3;
+
+ // Set if the variable should be put into a unique section if
+ // possible; this is intended to permit the linker to garbage
+ // collect the value if it is not referenced. Only meaningful for
+ // global_variable.
+ static const unsigned int variable_in_unique_section = 1 << 4;
+
+ // Set if the variable should be treated as immutable. Only
+ // meaningful for implicit_variable. For example, this is set for
+ // slice initializers if the values must be copied to the heap.
+ static const unsigned int variable_is_constant = 1 << 5;
+
// 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.
+ // assembler. BTYPE is the type of the variable. FLAGS is the bit
+ // flags defined above. LOCATION is where the variable was defined.
virtual Bvariable*
- global_variable(const std::string& name,
- const std::string& asm_name,
- Btype* btype,
- bool is_external,
- bool is_hidden,
- bool in_unique_section,
- Location location) = 0;
+ global_variable(const std::string& name, const std::string& asm_name,
+ Btype* btype, unsigned int flags, Location location) = 0;
// A global variable will 1) be initialized to zero, or 2) be
// initialized to a constant value, or 3) be initialized in the init
@@ -516,42 +543,40 @@ class Backend
// null, gives the location at which the value of this variable may
// be found, typically used to create an inner-scope reference to an
// outer-scope variable, to extend the lifetime of the variable beyond
- // the inner scope. IS_ADDRESS_TAKEN is true if the address of this
- // variable is taken (this implies that the address does not escape
- // the function, as otherwise the variable would be on the heap).
+ // the inner scope. FLAGS is the bit flags defined above.
// LOCATION is where the variable is defined. For each local variable
// the frontend will call init_statement to set the initial value.
virtual Bvariable*
local_variable(Bfunction* function, const std::string& name, Btype* type,
- Bvariable* decl_var, bool is_address_taken, Location location) = 0;
+ Bvariable* decl_var, unsigned int flags,
+ Location location) = 0;
// Create a function parameter. This is an incoming parameter, not
// a result parameter (result parameters are treated as local
// variables). The arguments are as for local_variable.
virtual Bvariable*
parameter_variable(Bfunction* function, const std::string& name,
- Btype* type, bool is_address_taken,
- Location location) = 0;
+ Btype* type, unsigned int flags, Location location) = 0;
// Create a static chain parameter. This is the closure parameter.
virtual Bvariable*
static_chain_variable(Bfunction* function, const std::string& name,
- Btype* type, Location location) = 0;
+ Btype* type, unsigned int flags,
+ Location location) = 0;
// Create a temporary variable. A temporary variable has no name,
// just a type. We pass in FUNCTION and BLOCK in case they are
// needed. If INIT is not NULL, the variable should be initialized
// to that value. Otherwise the initial value is irrelevant--the
// backend does not have to explicitly initialize it to zero.
- // ADDRESS_IS_TAKEN is true if the programs needs to take the
- // address of this temporary variable. LOCATION is the location of
+ // FLAGS is the bit flags defined above. LOCATION is the location of
// the statement or expression which requires creating the temporary
// variable, and may not be very useful. This function should
// return a variable which can be referenced later and should set
// *PSTATEMENT to a statement which initializes the variable.
virtual Bvariable*
temporary_variable(Bfunction*, Bblock*, Btype*, Bexpression* init,
- bool address_is_taken, Location location,
+ unsigned int flags, Location location,
Bstatement** pstatement) = 0;
// Create an implicit variable that is compiler-defined. This is
@@ -566,46 +591,33 @@ class Backend
//
// TYPE is the type of the implicit variable.
//
- // IS_HIDDEN will be true if the descriptor should only be visible
- // within the current object.
- //
- // IS_CONSTANT is true if the implicit variable should be treated like it is
- // immutable. For slice initializers, if the values must be copied to the
- // heap, the variable IS_CONSTANT.
- //
- // IS_COMMON is true if the implicit variable should
- // be treated as a common variable (multiple definitions with
- // different sizes permitted in different object files, all merged
- // into the largest definition at link time); this will be true for
- // the zero value. IS_HIDDEN and IS_COMMON will never both be true.
+ // FLAGS is the bit flags defined above.
//
// If ALIGNMENT is not zero, it is the desired alignment of the variable.
virtual Bvariable*
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;
+ Btype* type, unsigned int flags, int64_t alignment) = 0;
// Set the initial value of a variable created by implicit_variable.
// This must be called even if there is no initializer, i.e., INIT is NULL.
- // The NAME, TYPE, IS_HIDDEN, IS_CONSTANT, and IS_COMMON parameters are
- // the same ones passed to implicit_variable. INIT will be a composite
- // literal of type TYPE. It will not contain any function calls or anything
- // else that can not be put into a read-only data section.
- // It may contain the address of variables created by implicit_variable.
+ // The NAME, TYPE, and FLAGS parameters are the same ones passed to
+ // implicit_variable. INIT will be a composite literal of type
+ // TYPE. It will not contain any function calls or anything else
+ // that can not be put into a read-only data section. It may
+ // contain the address of variables created by implicit_variable.
//
- // If IS_COMMON is true, INIT will be NULL, and the
+ // If variable_is_common is set in FLAGS, INIT will be NULL, and the
// variable should be initialized to all zeros.
virtual void
implicit_variable_set_init(Bvariable*, const std::string& name, Btype* type,
- bool is_hidden, bool is_constant, bool is_common,
- Bexpression* init) = 0;
+ unsigned int flags, 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, ASM_NAME and TYPE and with
- // IS_COMMON passed as false. This corresponds to an extern global
- // variable in C.
+ // variable_is_common not set in FLAGS. This corresponds to an
+ // extern global variable in C.
virtual Bvariable*
implicit_variable_reference(const std::string& name,
const std::string& asm_name,
@@ -622,15 +634,12 @@ class Backend
// 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.
- //
- // IS_COMMON is true if NAME may be defined by several packages, and
- // the linker should merge all such definitions. If IS_COMMON is
- // false, NAME should be defined in only one file. In general
- // IS_COMMON will be true for the type descriptor of an unnamed type
- // or a builtin type. IS_HIDDEN and IS_COMMON will never both be
- // true.
+ // FLAGS is the bit flags defined above. The variable_is_common
+ // flag will be set if NAME may be defined by several packages, and
+ // the linker should merge all such definitions. If the
+ // variable_is_common flag is not set, NAME should be defined in
+ // only one file. In general variable_is_common will be set for the
+ // type descriptor of an unnamed type or a builtin type.
//
// TYPE will be a struct type; the type of the returned expression
// must be a pointer to this struct type.
@@ -640,28 +649,26 @@ class Backend
// address. After calling this the frontend will call
// immutable_struct_set_init.
virtual Bvariable*
- immutable_struct(const std::string& name,
- const std::string& asm_name,
- bool is_hidden, bool is_common,
- Btype* type, Location) = 0;
+ immutable_struct(const std::string& name, const std::string& asm_name,
+ unsigned int flags, Btype* type, Location) = 0;
// Set the initial value of a variable created by immutable_struct.
- // The NAME, IS_HIDDEN, IS_COMMON, TYPE, and location parameters are
- // the same ones passed to immutable_struct. INITIALIZER will be a
- // composite literal of type TYPE. It will not contain any function
- // calls or anything else that can not be put into a read-only data
- // section. It may contain the address of variables created by
+ // The NAME, FLAGS, TYPE, and location parameters are the same ones
+ // passed to immutable_struct. INITIALIZER will be a composite
+ // literal of type TYPE. It will not contain any function calls or
+ // anything else that can not be put into a read-only data section.
+ // It may contain the address of variables created by
// immutable_struct.
virtual void
immutable_struct_set_init(Bvariable*, const std::string& name,
- bool is_hidden, bool is_common, Btype* type,
+ unsigned int flags, Btype* type,
Location, Bexpression* initializer) = 0;
// 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, ASM_NAME and TYPE and with IS_COMMON passed as false. This
- // corresponds to an extern const global variable in C.
+ // NAME, ASM_NAME and TYPE and with variable_is_common not set in
+ // flags. This corresponds to an extern const global variable in C.
virtual Bvariable*
immutable_struct_reference(const std::string& name,
const std::string& asm_name,
diff --git a/gcc/go/gofrontend/escape.cc b/gcc/go/gofrontend/escape.cc
index cf68874..6da29ed 100644
--- a/gcc/go/gofrontend/escape.cc
+++ b/gcc/go/gofrontend/escape.cc
@@ -1608,8 +1608,33 @@ Escape_analysis_assign::expression(Expression** pexpr)
}
break;
- default:
+ case Builtin_call_expression::BUILTIN_CLOSE:
+ case Builtin_call_expression::BUILTIN_DELETE:
+ case Builtin_call_expression::BUILTIN_PRINT:
+ case Builtin_call_expression::BUILTIN_PRINTLN:
+ case Builtin_call_expression::BUILTIN_LEN:
+ case Builtin_call_expression::BUILTIN_CAP:
+ case Builtin_call_expression::BUILTIN_COMPLEX:
+ case Builtin_call_expression::BUILTIN_REAL:
+ case Builtin_call_expression::BUILTIN_IMAG:
+ case Builtin_call_expression::BUILTIN_RECOVER:
+ case Builtin_call_expression::BUILTIN_ALIGNOF:
+ case Builtin_call_expression::BUILTIN_OFFSETOF:
+ case Builtin_call_expression::BUILTIN_SIZEOF:
+ // these do not escape.
+ break;
+
+ case Builtin_call_expression::BUILTIN_ADD:
+ case Builtin_call_expression::BUILTIN_SLICE:
+ // handled in ::assign.
break;
+
+ case Builtin_call_expression::BUILTIN_MAKE:
+ case Builtin_call_expression::BUILTIN_NEW:
+ // should have been lowered to runtime calls at this point.
+ // fallthrough
+ default:
+ go_unreachable();
}
break;
}
@@ -1621,6 +1646,7 @@ Escape_analysis_assign::expression(Expression** pexpr)
case Runtime::MAKECHAN:
case Runtime::MAKECHAN64:
case Runtime::MAKEMAP:
+ case Runtime::MAKEMAP64:
case Runtime::MAKESLICE:
case Runtime::MAKESLICE64:
this->context_->track(n);
@@ -1680,8 +1706,52 @@ Escape_analysis_assign::expression(Expression** pexpr)
}
break;
+ case Runtime::MEMCMP:
+ case Runtime::DECODERUNE:
+ case Runtime::INTSTRING:
+ case Runtime::MAKEMAP_SMALL:
+ case Runtime::MAPACCESS1:
+ case Runtime::MAPACCESS1_FAST32:
+ case Runtime::MAPACCESS1_FAST64:
+ case Runtime::MAPACCESS1_FASTSTR:
+ case Runtime::MAPACCESS1_FAT:
+ case Runtime::MAPACCESS2:
+ case Runtime::MAPACCESS2_FAST32:
+ case Runtime::MAPACCESS2_FAST64:
+ case Runtime::MAPACCESS2_FASTSTR:
+ case Runtime::MAPACCESS2_FAT:
+ case Runtime::MAPASSIGN_FAST32:
+ case Runtime::MAPASSIGN_FAST64:
+ case Runtime::MAPITERINIT:
+ case Runtime::MAPITERNEXT:
+ case Runtime::MAPCLEAR:
+ case Runtime::CHANRECV2:
+ case Runtime::SELECTGO:
+ case Runtime::SELECTNBSEND:
+ case Runtime::SELECTNBRECV:
+ case Runtime::BLOCK:
+ case Runtime::IFACET2IP:
+ case Runtime::EQTYPE:
+ case Runtime::MEMCLRHASPTR:
+ case Runtime::FIELDTRACK:
+ case Runtime::BUILTIN_MEMSET:
+ case Runtime::PANIC_SLICE_CONVERT:
+ // these do not escape.
+ break;
+
+ case Runtime::IFACEE2E2:
+ case Runtime::IFACEI2E2:
+ case Runtime::IFACEE2I2:
+ case Runtime::IFACEI2I2:
+ case Runtime::IFACEE2T2P:
+ case Runtime::IFACEI2T2P:
+ // handled in ::assign.
+ break;
+
default:
- break;
+ // should not see other runtime calls. they are not yet
+ // lowered to runtime calls at this point.
+ go_unreachable();
}
}
else
@@ -2325,19 +2395,82 @@ Escape_analysis_assign::assign(Node* dst, Node* src)
}
break;
+ case Expression::EXPRESSION_SLICE_INFO:
+ {
+ Slice_info_expression* sie = e->slice_info_expression();
+ if (sie->info() == Expression::SLICE_INFO_VALUE_POINTER)
+ {
+ Node* slice = Node::make_node(sie->slice());
+ this->assign(dst, slice);
+ }
+ }
+ break;
+
case Expression::EXPRESSION_CALL:
{
Call_expression* call = e->call_expression();
if (call->is_builtin())
{
Builtin_call_expression* bce = call->builtin_call_expression();
- if (bce->code() == Builtin_call_expression::BUILTIN_APPEND)
+ switch (bce->code())
{
- // 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);
+ case Builtin_call_expression::BUILTIN_APPEND:
+ {
+ // 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 Builtin_call_expression::BUILTIN_ADD:
+ {
+ // unsafe.Add(p, off).
+ // Flow p to result.
+ Node* arg = Node::make_node(call->args()->front());
+ this->assign(dst, arg);
+ }
+ break;
+
+ case Builtin_call_expression::BUILTIN_SLICE:
+ {
+ // unsafe.Slice(p, len).
+ // The resulting slice has the same backing store as p. Flow p to result.
+ Node* arg = Node::make_node(call->args()->front());
+ this->assign(dst, arg);
+ }
+ break;
+
+ case Builtin_call_expression::BUILTIN_LEN:
+ case Builtin_call_expression::BUILTIN_CAP:
+ case Builtin_call_expression::BUILTIN_COMPLEX:
+ case Builtin_call_expression::BUILTIN_REAL:
+ case Builtin_call_expression::BUILTIN_IMAG:
+ case Builtin_call_expression::BUILTIN_RECOVER:
+ case Builtin_call_expression::BUILTIN_ALIGNOF:
+ case Builtin_call_expression::BUILTIN_OFFSETOF:
+ case Builtin_call_expression::BUILTIN_SIZEOF:
+ // these do not escape.
+ break;
+
+ case Builtin_call_expression::BUILTIN_COPY:
+ // handled in ::expression.
+ break;
+
+ case Builtin_call_expression::BUILTIN_CLOSE:
+ case Builtin_call_expression::BUILTIN_DELETE:
+ case Builtin_call_expression::BUILTIN_PRINT:
+ case Builtin_call_expression::BUILTIN_PRINTLN:
+ case Builtin_call_expression::BUILTIN_PANIC:
+ // these do not have result.
+ // fallthrough
+ case Builtin_call_expression::BUILTIN_MAKE:
+ case Builtin_call_expression::BUILTIN_NEW:
+ // should have been lowered to runtime calls at this point.
+ // fallthrough
+ default:
+ go_unreachable();
}
break;
}
@@ -2592,6 +2725,21 @@ Escape_analysis_assign::assign(Node* dst, Node* src)
}
break;
+ case Expression::EXPRESSION_CONDITIONAL:
+ {
+ Conditional_expression* ce = e->conditional_expression();
+ this->assign(dst, Node::make_node(ce->then_expr()));
+ this->assign(dst, Node::make_node(ce->else_expr()));
+ }
+ break;
+
+ case Expression::EXPRESSION_COMPOUND:
+ {
+ Compound_expression* ce = e->compound_expression();
+ this->assign(dst, Node::make_node(ce->expr()));
+ }
+ 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.
diff --git a/gcc/go/gofrontend/export.cc b/gcc/go/gofrontend/export.cc
index e99c680..3d11334 100644
--- a/gcc/go/gofrontend/export.cc
+++ b/gcc/go/gofrontend/export.cc
@@ -106,11 +106,12 @@ class Collect_export_references : public Traverse
{
public:
Collect_export_references(Export* exp,
+ const std::map<std::string, Package*>& packages,
Unordered_set(Named_object*)* exports,
Unordered_set(const Package*)* imports)
: Traverse(traverse_expressions
| traverse_types),
- exp_(exp), exports_(exports), imports_(imports),
+ exp_(exp), packages_(packages), exports_(exports), imports_(imports),
inline_fcn_worklist_(NULL), exports_finalized_(false)
{ }
@@ -150,6 +151,8 @@ class Collect_export_references : public Traverse
// The exporter.
Export* exp_;
+ // The list of packages known to this compilation.
+ const std::map<std::string, Package*>& packages_;
// The set of named objects to export.
Unordered_set(Named_object*)* exports_;
// Set containing all directly and indirectly imported packages.
@@ -257,6 +260,24 @@ Collect_export_references::expression(Expression** pexpr)
return TRAVERSE_CONTINUE;
}
+ const Call_expression* call = expr->call_expression();
+ if (call != NULL)
+ {
+ const Builtin_call_expression* bce = call->builtin_call_expression();
+ if (bce != NULL
+ && (bce->code() == Builtin_call_expression::BUILTIN_ADD
+ || bce->code() == Builtin_call_expression::BUILTIN_SLICE))
+ {
+ // This is a reference to unsafe.Add or unsafe.Slice. Make
+ // sure we list the "unsafe" package in the imports and give
+ // it a package index.
+ const std::map<std::string, Package*>::const_iterator p =
+ this->packages_.find("unsafe");
+ go_assert(p != this->packages_.end());
+ this->imports_->insert(p->second);
+ }
+ }
+
return TRAVERSE_CONTINUE;
}
@@ -589,7 +610,7 @@ Export::export_globals(const std::string& package_name,
// Track all imported packages mentioned in export data.
Unordered_set(const Package*) all_imports;
- Collect_export_references collect(this, &exports, &all_imports);
+ Collect_export_references collect(this, packages, &exports, &all_imports);
// Walk the set of inlinable routine bodies collected above. This
// can potentially expand the exports set.
@@ -1274,6 +1295,25 @@ Export::package_index(const Package* pkg) const
return index;
}
+// Return the index of the "unsafe" package.
+
+int
+Export::unsafe_package_index() const
+{
+ for (Unordered_map(const Package*, int)::const_iterator p =
+ this->packages_.begin();
+ p != this->packages_.end();
+ ++p)
+ {
+ if (p->first->pkgpath() == "unsafe")
+ {
+ go_assert(p->second != 0);
+ return p->second;
+ }
+ }
+ go_unreachable();
+}
+
// Return the index of a type.
int
diff --git a/gcc/go/gofrontend/export.h b/gcc/go/gofrontend/export.h
index c93bced..1f61343 100644
--- a/gcc/go/gofrontend/export.h
+++ b/gcc/go/gofrontend/export.h
@@ -216,6 +216,11 @@ class Export : public String_dump
int
package_index(const Package* p) const;
+ // Return the index of the "unsafe" package, which must be one of
+ // the exported packages.
+ int
+ unsafe_package_index() const;
+
private:
Export(const Export&);
Export& operator=(const Export&);
@@ -377,6 +382,11 @@ class Export_function_body : public String_dump
package_index(const Package* p) const
{ return this->exp_->package_index(p); }
+ // Return the index of the "unsafe" package.
+ int
+ unsafe_package_index() const
+ { return this->exp_->unsafe_package_index(); }
+
// Record a temporary statement and return its index.
unsigned int
record_temporary(const Temporary_statement*);
diff --git a/gcc/go/gofrontend/expressions.cc b/gcc/go/gofrontend/expressions.cc
index 5409d26..ddb1d91 100644
--- a/gcc/go/gofrontend/expressions.cc
+++ b/gcc/go/gofrontend/expressions.cc
@@ -408,7 +408,14 @@ Expression::convert_type_to_interface(Type* lhs_type, Expression* rhs,
{
// We are assigning a non-pointer value to the interface; the
// interface gets a copy of the value in the heap if it escapes.
- if (rhs->is_constant())
+
+ // An exception is &global if global is notinheap, which is a
+ // pointer value but not a direct-iface type and we can't simply
+ // take its address.
+ bool is_address = (rhs->unary_expression() != NULL
+ && rhs->unary_expression()->op() == OPERATOR_AND);
+
+ if (rhs->is_constant() && !is_address)
obj = Expression::make_unary(OPERATOR_AND, rhs, location);
else
{
@@ -1427,7 +1434,7 @@ Sink_expression::do_get_backend(Translate_context* context)
Bstatement* decl;
this->bvar_ =
gogo->backend()->temporary_variable(fn_ctx, context->bblock(), bt, NULL,
- false, loc, &decl);
+ 0, loc, &decl);
Bexpression* var_ref =
gogo->backend()->var_expression(this->bvar_, loc);
var_ref = gogo->backend()->compound_expression(decl, var_ref, loc);
@@ -1724,10 +1731,12 @@ Func_descriptor_expression::do_get_backend(Translate_context* context)
if (no->is_function() && no->func_value()->is_referenced_by_inline())
is_hidden = false;
+ unsigned int flags = 0;
+ if (is_hidden)
+ flags |= Backend::variable_is_hidden;
bvar = context->backend()->immutable_struct(bname.name(),
bname.optional_asm_name(),
- is_hidden, false,
- btype, bloc);
+ flags, btype, bloc);
Expression_list* vals = new Expression_list();
vals->push_back(Expression::make_func_code_reference(this->fn_, bloc));
Expression* init =
@@ -1736,8 +1745,7 @@ Func_descriptor_expression::do_get_backend(Translate_context* context)
bcontext.set_is_const();
Bexpression* binit = init->get_backend(&bcontext);
context->backend()->immutable_struct_set_init(bvar, bname.name(),
- is_hidden, false, btype,
- bloc, binit);
+ flags, btype, bloc, binit);
}
this->dvar_ = bvar;
@@ -3865,11 +3873,12 @@ Type_conversion_expression::do_traverse(Traverse* traverse)
return TRAVERSE_CONTINUE;
}
-// Convert to a constant at lowering time.
+// Convert to a constant at lowering time. Also lower conversions
+// from slice to pointer-to-array, as they can panic.
Expression*
Type_conversion_expression::do_lower(Gogo*, Named_object*,
- Statement_inserter*, int)
+ Statement_inserter* inserter, int)
{
Type* type = this->type_;
Expression* val = this->expr_;
@@ -3957,6 +3966,57 @@ Type_conversion_expression::do_lower(Gogo*, Named_object*,
}
}
+ if (type->points_to() != NULL
+ && type->points_to()->array_type() != NULL
+ && !type->points_to()->is_slice_type()
+ && val->type()->is_slice_type()
+ && Type::are_identical(type->points_to()->array_type()->element_type(),
+ val->type()->array_type()->element_type(),
+ 0, NULL))
+ {
+ Temporary_statement* val_temp = NULL;
+ if (!val->is_multi_eval_safe())
+ {
+ val_temp = Statement::make_temporary(val->type(), NULL, location);
+ inserter->insert(val_temp);
+ val = Expression::make_set_and_use_temporary(val_temp, val,
+ location);
+ }
+
+ Type* int_type = Type::lookup_integer_type("int");
+ Temporary_statement* vallen_temp =
+ Statement::make_temporary(int_type, NULL, location);
+ inserter->insert(vallen_temp);
+
+ Expression* arrlen = type->points_to()->array_type()->length();
+ Expression* vallen =
+ Expression::make_slice_info(val, Expression::SLICE_INFO_LENGTH,
+ location);
+ vallen = Expression::make_set_and_use_temporary(vallen_temp, vallen,
+ location);
+ Expression* cond = Expression::make_binary(OPERATOR_GT, arrlen, vallen,
+ location);
+
+ vallen = Expression::make_temporary_reference(vallen_temp, location);
+ Expression* panic = Runtime::make_call(Runtime::PANIC_SLICE_CONVERT,
+ location, 2, arrlen, vallen);
+
+ Expression* nil = Expression::make_nil(location);
+ Expression* check = Expression::make_conditional(cond, panic, nil,
+ location);
+
+ if (val_temp == NULL)
+ val = val->copy();
+ else
+ val = Expression::make_temporary_reference(val_temp, location);
+ Expression* ptr =
+ Expression::make_slice_info(val, Expression::SLICE_INFO_VALUE_POINTER,
+ location);
+ ptr = Expression::make_unsafe_cast(type, ptr, location);
+
+ return Expression::make_compound(check, ptr, location);
+ }
+
return this;
}
@@ -5280,12 +5340,14 @@ Unary_expression::do_get_backend(Translate_context* context)
copy_to_heap = (context->function() != NULL
|| context->is_const());
}
+ unsigned int flags = (Backend::variable_is_hidden
+ | Backend::variable_address_is_taken);
+ if (copy_to_heap)
+ flags |= Backend::variable_is_constant;
Bvariable* implicit =
- gogo->backend()->implicit_variable(var_name, "", btype, true,
- copy_to_heap, false, 0);
+ gogo->backend()->implicit_variable(var_name, "", btype, flags, 0);
gogo->backend()->implicit_variable_set_init(implicit, var_name, btype,
- true, copy_to_heap, false,
- bexpr);
+ flags, bexpr);
bexpr = gogo->backend()->var_expression(implicit, loc);
// If we are not copying a slice initializer to the heap,
@@ -5307,22 +5369,24 @@ Unary_expression::do_get_backend(Translate_context* context)
&& this->expr_->is_static_initializer())
{
std::string var_name(gogo->initializer_name());
+ unsigned int flags = (Backend::variable_is_hidden
+ | Backend::variable_address_is_taken);
Bvariable* decl =
- gogo->backend()->immutable_struct(var_name, "", true, false,
- btype, loc);
- gogo->backend()->immutable_struct_set_init(decl, var_name, true,
- false, btype, loc, bexpr);
+ gogo->backend()->immutable_struct(var_name, "", flags, btype, loc);
+ gogo->backend()->immutable_struct_set_init(decl, var_name, flags,
+ btype, loc, bexpr);
bexpr = gogo->backend()->var_expression(decl, loc);
}
else if (this->expr_->is_constant())
{
std::string var_name(gogo->initializer_name());
+ unsigned int flags = (Backend::variable_is_hidden
+ | Backend::variable_is_constant
+ | Backend::variable_address_is_taken);
Bvariable* decl =
- gogo->backend()->implicit_variable(var_name, "", btype,
- true, true, false, 0);
+ gogo->backend()->implicit_variable(var_name, "", btype, flags, 0);
gogo->backend()->implicit_variable_set_init(decl, var_name, btype,
- true, true, false,
- bexpr);
+ flags, bexpr);
bexpr = gogo->backend()->var_expression(decl, loc);
}
@@ -8247,12 +8311,16 @@ Builtin_call_expression::Builtin_call_expression(Gogo* gogo,
this->code_ = BUILTIN_REAL;
else if (name == "recover")
this->code_ = BUILTIN_RECOVER;
+ else if (name == "Add")
+ this->code_ = BUILTIN_ADD;
else if (name == "Alignof")
this->code_ = BUILTIN_ALIGNOF;
else if (name == "Offsetof")
this->code_ = BUILTIN_OFFSETOF;
else if (name == "Sizeof")
this->code_ = BUILTIN_SIZEOF;
+ else if (name == "Slice")
+ this->code_ = BUILTIN_SLICE;
else
go_unreachable();
}
@@ -8689,6 +8757,119 @@ Builtin_call_expression::do_flatten(Gogo* gogo, Named_object* function,
return Runtime::make_call(code, loc, 3, e1, e2, e3);
}
+
+ case BUILTIN_ADD:
+ {
+ Expression* ptr = this->args()->front();
+ Type* uintptr_type = Type::lookup_integer_type("uintptr");
+ ptr = Expression::make_cast(uintptr_type, ptr, loc);
+ Expression* len = this->args()->back();
+ len = Expression::make_cast(uintptr_type, len, loc);
+ Expression* add = Expression::make_binary(OPERATOR_PLUS, ptr, len,
+ loc);
+ return Expression::make_cast(this->args()->front()->type(), add, loc);
+ }
+
+ case BUILTIN_SLICE:
+ {
+ Expression* ptr = this->args()->front();
+ Temporary_statement* ptr_temp = NULL;
+ if (!ptr->is_multi_eval_safe())
+ {
+ ptr_temp = Statement::make_temporary(NULL, ptr, loc);
+ inserter->insert(ptr_temp);
+ ptr = Expression::make_temporary_reference(ptr_temp, loc);
+ }
+
+ Expression* len = this->args()->back();
+ Temporary_statement* len_temp = NULL;
+ if (!len->is_multi_eval_safe())
+ {
+ len_temp = Statement::make_temporary(NULL, len, loc);
+ inserter->insert(len_temp);
+ len = Expression::make_temporary_reference(len_temp, loc);
+ }
+
+ bool fits_in_int;
+ Numeric_constant nc;
+ if (this->args()->back()->numeric_constant_value(&nc))
+ {
+ // We gave an error for constants that don't fit in int in
+ // check_types.
+ fits_in_int = true;
+ }
+ else
+ {
+ Integer_type* itype = this->args()->back()->type()->integer_type();
+ go_assert(itype != NULL);
+ int ebits = itype->bits();
+ int intbits =
+ Type::lookup_integer_type("int")->integer_type()->bits();
+
+ // 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.
+
+ fits_in_int = ebits <= intbits;
+ }
+
+ Runtime::Function code = (fits_in_int
+ ? Runtime::UNSAFESLICE
+ : Runtime::UNSAFESLICE64);
+ Expression* td =
+ Expression::make_type_descriptor(ptr->type()->points_to(), loc);
+ Expression* check = Runtime::make_call(code, loc, 3,
+ td, ptr, len);
+
+ if (ptr_temp == NULL)
+ ptr = ptr->copy();
+ else
+ ptr = Expression::make_temporary_reference(ptr_temp, loc);
+ Expression* nil = Expression::make_nil(loc);
+ nil = Expression::make_cast(ptr->type(), nil, loc);
+ Expression* is_nil = Expression::make_binary(OPERATOR_EQEQ, ptr, nil,
+ loc);
+
+ if (len_temp == NULL)
+ len = len->copy();
+ else
+ len = Expression::make_temporary_reference(len_temp, loc);
+ Expression* zero = Expression::make_integer_ul(0, len->type(), loc);
+ Expression* is_zero = Expression::make_binary(OPERATOR_EQEQ, len, zero,
+ loc);
+
+ Expression* cond = Expression::make_binary(OPERATOR_ANDAND, is_nil,
+ is_zero, loc);
+
+ Type* slice_type = Type::make_array_type(ptr->type()->points_to(),
+ NULL);
+ nil = Expression::make_nil(loc);
+ Expression* nil_slice = Expression::make_cast(slice_type, nil, loc);
+
+ if (ptr_temp == NULL)
+ ptr = ptr->copy();
+ else
+ ptr = Expression::make_temporary_reference(ptr_temp, loc);
+
+ if (len_temp == NULL)
+ len = len->copy();
+ else
+ len = Expression::make_temporary_reference(len_temp, loc);
+
+ Expression* cap;
+ if (len_temp == NULL)
+ cap = len->copy();
+ else
+ cap = Expression::make_temporary_reference(len_temp, loc);
+
+ Expression* slice = Expression::make_slice_value(slice_type, ptr,
+ len, cap, loc);
+
+ slice = Expression::make_conditional(cond, nil_slice, slice, loc);
+
+ return Expression::make_compound(check, slice, loc);
+ }
}
return this;
@@ -9169,7 +9350,7 @@ Builtin_call_expression::flatten_append(Gogo* gogo, Named_object* function,
ref2 = Expression::make_cast(uint_type, ref2, loc);
cond = Expression::make_binary(OPERATOR_GT, ref, ref2, loc);
zero = Expression::make_integer_ul(0, int_type, loc);
- call = Expression::make_conditional(cond, call, zero, loc);
+ call = Expression::make_conditional(cond, zero, call, loc);
}
}
else
@@ -9776,9 +9957,11 @@ Builtin_call_expression::do_discarding_value()
case BUILTIN_MAKE:
case BUILTIN_NEW:
case BUILTIN_REAL:
+ case BUILTIN_ADD:
case BUILTIN_ALIGNOF:
case BUILTIN_OFFSETOF:
case BUILTIN_SIZEOF:
+ case BUILTIN_SLICE:
this->unused_value_error();
return false;
@@ -9885,6 +10068,18 @@ Builtin_call_expression::do_type()
t = Type::make_error_type();
return t;
}
+
+ case BUILTIN_ADD:
+ return Type::make_pointer_type(Type::make_void_type());
+
+ case BUILTIN_SLICE:
+ const Expression_list* args = this->args();
+ if (args == NULL || args->size() != 2)
+ return Type::make_error_type();
+ Type* pt = args->front()->type()->points_to();
+ if (pt == NULL)
+ return Type::make_error_type();
+ return Type::make_array_type(pt, NULL);
}
}
@@ -9949,6 +10144,28 @@ Builtin_call_expression::do_determine_type(const Type_context* context)
is_print = false;
break;
+ case BUILTIN_ADD:
+ case BUILTIN_SLICE:
+ // Both unsafe.Add and unsafe.Slice take two arguments, and the
+ // second arguments defaults to "int".
+ if (args != NULL && args->size() == 2)
+ {
+ if (this->code_ == BUILTIN_SLICE)
+ args->front()->determine_type_no_context();
+ else
+ {
+ Type* pointer = Type::make_pointer_type(Type::make_void_type());
+ Type_context subcontext(pointer, false);
+ args->front()->determine_type(&subcontext);
+ }
+ Type* int_type = Type::lookup_integer_type("int");
+ Type_context subcontext(int_type, false);
+ args->back()->determine_type(&subcontext);
+ return;
+ }
+ is_print = false;
+ break;
+
default:
is_print = false;
break;
@@ -10348,6 +10565,78 @@ Builtin_call_expression::do_check_types(Gogo*)
}
break;
+ case BUILTIN_ADD:
+ case BUILTIN_SLICE:
+ {
+ Numeric_constant nc;
+ unsigned long v;
+ const Expression_list* args = this->args();
+ if (args == NULL || args->size() < 2)
+ this->report_error(_("not enough arguments"));
+ else if (args->size() > 2)
+ this->report_error(_("too many arguments"));
+ else if (args->front()->is_error_expression()
+ || args->front()->type()->is_error()
+ || args->back()->is_error_expression()
+ || args->back()->type()->is_error())
+ this->set_is_error();
+ else if (args->back()->type()->integer_type() == NULL
+ && (!args->back()->type()->is_abstract()
+ || !args->back()->numeric_constant_value(&nc)
+ || (nc.to_unsigned_long(&v)
+ == Numeric_constant::NC_UL_NOTINT)))
+ {
+ if (this->code_ == BUILTIN_ADD)
+ go_error_at(args->back()->location(), "non-integer offset");
+ else
+ go_error_at(args->back()->location(), "non-integer size");
+ }
+ else if (this->code_ == BUILTIN_ADD)
+ {
+ Type* pointer_type =
+ Type::make_pointer_type(Type::make_void_type());
+ std::string reason;
+ if (!Type::are_assignable(pointer_type, args->front()->type(),
+ &reason))
+ {
+ if (reason.empty())
+ go_error_at(args->front()->location(),
+ "argument 1 has incompatible type");
+ else
+ go_error_at(args->front()->location(),
+ "argument 1 has incompatible type (%s)",
+ reason.c_str());
+ this->set_is_error();
+ }
+ }
+ else
+ {
+ if (args->front()->type()->points_to() == NULL)
+ {
+ go_error_at(args->front()->location(),
+ "argument 1 must be a pointer");
+ this->set_is_error();
+ }
+
+ unsigned int int_bits =
+ Type::lookup_integer_type("int")->integer_type()->bits();
+
+ mpz_t ival;
+ if (args->back()->numeric_constant_value(&nc) && nc.to_int(&ival))
+ {
+ if (mpz_sgn(ival) < 0
+ || mpz_sizeinbase(ival, 2) >= int_bits)
+ {
+ go_error_at(args->back()->location(),
+ "slice length out of range");
+ this->set_is_error();
+ }
+ mpz_clear(ival);
+ }
+ }
+ }
+ break;
+
default:
go_unreachable();
}
@@ -10392,6 +10681,8 @@ Builtin_call_expression::do_get_backend(Translate_context* context)
case BUILTIN_INVALID:
case BUILTIN_NEW:
case BUILTIN_MAKE:
+ case BUILTIN_ADD:
+ case BUILTIN_SLICE:
go_unreachable();
case BUILTIN_LEN:
@@ -10755,6 +11046,14 @@ Builtin_call_expression::do_export(Export_function_body* efb) const
// A trailing space lets us reliably identify the end of the number.
efb->write_c_string(" ");
}
+ else if (this->code_ == BUILTIN_ADD || this->code_ == BUILTIN_SLICE)
+ {
+ char buf[50];
+ snprintf(buf, sizeof buf, "<p%d>%s", efb->unsafe_package_index(),
+ (this->code_ == BUILTIN_ADD ? "Add" : "Slice"));
+ efb->write_c_string(buf);
+ this->export_arguments(efb);
+ }
else
{
const char *s = NULL;
@@ -10910,12 +11209,23 @@ Call_expression::do_lower(Gogo* gogo, Named_object* function,
{
Location loc = this->location();
+ if (this->is_error_expression())
+ return Expression::make_error(loc);
+
// A type cast can look like a function call.
if (this->fn_->is_type_expression()
&& this->args_ != NULL
&& this->args_->size() == 1)
- return Expression::make_cast(this->fn_->type(), this->args_->front(),
- loc);
+ {
+ if (this->expected_result_count_ != 0
+ && this->expected_result_count_ != 1)
+ {
+ this->report_error(_("type conversion result count mismatch"));
+ return Expression::make_error(loc);
+ }
+ return Expression::make_cast(this->fn_->type(), this->args_->front(),
+ loc);
+ }
// Because do_type will return an error type and thus prevent future
// errors, check for that case now to ensure that the error gets
@@ -11028,6 +11338,7 @@ Call_expression::do_lower(Gogo* gogo, Named_object* function,
// We always pass a pointer when calling a method, except for
// direct interface types when calling a value method.
if (!first_arg->type()->is_error()
+ && first_arg->type()->points_to() == NULL
&& !first_arg->type()->is_direct_iface_type())
{
first_arg = Expression::make_unary(OPERATOR_AND, first_arg, loc);
@@ -11306,12 +11617,10 @@ Call_expression::intrinsify(Gogo* gogo,
// sync/atomic functions and runtime/internal/atomic functions
// are very similar. In order not to duplicate code, we just
// redirect to the latter and let the code below to handle them.
- // In case there is no equivalent functions (slight variance
- // in types), we just make an artificial name (begin with '$').
// Note: no StorePointer, SwapPointer, and CompareAndSwapPointer,
// as they need write barriers.
if (name == "LoadInt32")
- name = "$Loadint32";
+ name = "Loadint32";
else if (name == "LoadInt64")
name = "Loadint64";
else if (name == "LoadUint32")
@@ -11323,9 +11632,9 @@ Call_expression::intrinsify(Gogo* gogo,
else if (name == "LoadPointer")
name = "Loadp";
else if (name == "StoreInt32")
- name = "$Storeint32";
+ name = "Storeint32";
else if (name == "StoreInt64")
- name = "$Storeint64";
+ name = "Storeint64";
else if (name == "StoreUint32")
name = "Store";
else if (name == "StoreUint64")
@@ -11333,7 +11642,7 @@ Call_expression::intrinsify(Gogo* gogo,
else if (name == "StoreUintptr")
name = "Storeuintptr";
else if (name == "AddInt32")
- name = "$Xaddint32";
+ name = "Xaddint32";
else if (name == "AddInt64")
name = "Xaddint64";
else if (name == "AddUint32")
@@ -11343,9 +11652,9 @@ Call_expression::intrinsify(Gogo* gogo,
else if (name == "AddUintptr")
name = "Xadduintptr";
else if (name == "SwapInt32")
- name = "$Xchgint32";
+ name = "Xchgint32";
else if (name == "SwapInt64")
- name = "$Xchgint64";
+ name = "Xchgint64";
else if (name == "SwapUint32")
name = "Xchg";
else if (name == "SwapUint64")
@@ -11353,9 +11662,9 @@ Call_expression::intrinsify(Gogo* gogo,
else if (name == "SwapUintptr")
name = "Xchguintptr";
else if (name == "CompareAndSwapInt32")
- name = "$Casint32";
+ name = "Casint32";
else if (name == "CompareAndSwapInt64")
- name = "$Casint64";
+ name = "Casint64";
else if (name == "CompareAndSwapUint32")
name = "Cas";
else if (name == "CompareAndSwapUint64")
@@ -11591,7 +11900,7 @@ Call_expression::intrinsify(Gogo* gogo,
if ((name == "Load" || name == "Load64" || name == "Loadint64" || name == "Loadp"
|| name == "Loaduint" || name == "Loaduintptr" || name == "LoadAcq"
- || name == "$Loadint32")
+ || name == "Loadint32")
&& this->args_ != NULL && this->args_->size() == 1)
{
if (int_size < 8 && (name == "Load64" || name == "Loadint64"))
@@ -11611,7 +11920,7 @@ Call_expression::intrinsify(Gogo* gogo,
code = Runtime::ATOMIC_LOAD_8;
res_type = uint64_type;
}
- else if (name == "$Loadint32")
+ else if (name == "Loadint32")
{
code = Runtime::ATOMIC_LOAD_4;
res_type = int32_type;
@@ -11658,10 +11967,10 @@ Call_expression::intrinsify(Gogo* gogo,
if ((name == "Store" || name == "Store64" || name == "StorepNoWB"
|| name == "Storeuintptr" || name == "StoreRel"
- || name == "$Storeint32" || name == "$Storeint64")
+ || name == "Storeint32" || name == "Storeint64")
&& this->args_ != NULL && this->args_->size() == 2)
{
- if (int_size < 8 && (name == "Store64" || name == "$Storeint64"))
+ if (int_size < 8 && (name == "Store64" || name == "Storeint64"))
return NULL;
Runtime::Function code;
@@ -11671,9 +11980,9 @@ Call_expression::intrinsify(Gogo* gogo,
code = Runtime::ATOMIC_STORE_4;
else if (name == "Store64")
code = Runtime::ATOMIC_STORE_8;
- else if (name == "$Storeint32")
+ else if (name == "Storeint32")
code = Runtime::ATOMIC_STORE_4;
- else if (name == "$Storeint64")
+ else if (name == "Storeint64")
code = Runtime::ATOMIC_STORE_8;
else if (name == "Storeuintptr")
code = (ptr_size == 8 ? Runtime::ATOMIC_STORE_8 : Runtime::ATOMIC_STORE_4);
@@ -11695,7 +12004,7 @@ Call_expression::intrinsify(Gogo* gogo,
}
if ((name == "Xchg" || name == "Xchg64" || name == "Xchguintptr"
- || name == "$Xchgint32" || name == "$Xchgint64")
+ || name == "Xchgint32" || name == "Xchgint64")
&& this->args_ != NULL && this->args_->size() == 2)
{
if (int_size < 8 && (name == "Xchg64" || name == "Xchgint64"))
@@ -11713,12 +12022,12 @@ Call_expression::intrinsify(Gogo* gogo,
code = Runtime::ATOMIC_EXCHANGE_8;
res_type = uint64_type;
}
- else if (name == "$Xchgint32")
+ else if (name == "Xchgint32")
{
code = Runtime::ATOMIC_EXCHANGE_4;
res_type = int32_type;
}
- else if (name == "$Xchgint64")
+ else if (name == "Xchgint64")
{
code = Runtime::ATOMIC_EXCHANGE_8;
res_type = int64_type;
@@ -11741,10 +12050,10 @@ Call_expression::intrinsify(Gogo* gogo,
if ((name == "Cas" || name == "Cas64" || name == "Casuintptr"
|| name == "Casp1" || name == "CasRel"
- || name == "$Casint32" || name == "$Casint64")
+ || name == "Casint32" || name == "Casint64")
&& this->args_ != NULL && this->args_->size() == 3)
{
- if (int_size < 8 && (name == "Cas64" || name == "$Casint64"))
+ if (int_size < 8 && (name == "Cas64" || name == "Casint64"))
return NULL;
Runtime::Function code;
@@ -11763,9 +12072,9 @@ Call_expression::intrinsify(Gogo* gogo,
code = Runtime::ATOMIC_COMPARE_EXCHANGE_4;
else if (name == "Cas64")
code = Runtime::ATOMIC_COMPARE_EXCHANGE_8;
- else if (name == "$Casint32")
+ else if (name == "Casint32")
code = Runtime::ATOMIC_COMPARE_EXCHANGE_4;
- else if (name == "$Casint64")
+ else if (name == "Casint64")
code = Runtime::ATOMIC_COMPARE_EXCHANGE_8;
else if (name == "Casuintptr")
code = (ptr_size == 8
@@ -11793,7 +12102,7 @@ Call_expression::intrinsify(Gogo* gogo,
}
if ((name == "Xadd" || name == "Xadd64" || name == "Xaddint64"
- || name == "Xadduintptr" || name == "$Xaddint32")
+ || name == "Xadduintptr" || name == "Xaddint32")
&& this->args_ != NULL && this->args_->size() == 2)
{
if (int_size < 8 && (name == "Xadd64" || name == "Xaddint64"))
@@ -11811,7 +12120,7 @@ Call_expression::intrinsify(Gogo* gogo,
code = Runtime::ATOMIC_ADD_FETCH_8;
res_type = uint64_type;
}
- else if (name == "$Xaddint32")
+ else if (name == "Xaddint32")
{
code = Runtime::ATOMIC_ADD_FETCH_4;
res_type = int32_type;
@@ -14888,7 +15197,9 @@ Allocation_expression::do_get_backend(Translate_context* context)
: gogo->backend()->zero_expression(btype));
Bvariable* temp =
gogo->backend()->temporary_variable(fndecl, context->bblock(), btype,
- init, true, loc, &decl);
+ init,
+ Backend::variable_address_is_taken,
+ loc, &decl);
Bexpression* ret = gogo->backend()->var_expression(temp, loc);
ret = gogo->backend()->address_expression(ret, loc);
ret = gogo->backend()->compound_expression(decl, ret, loc);
@@ -15140,44 +15451,6 @@ Struct_construction_expression::do_copy()
return ret;
}
-// Flatten a struct construction expression. Store the values into
-// temporaries in case they need interface conversion.
-
-Expression*
-Struct_construction_expression::do_flatten(Gogo*, Named_object*,
- Statement_inserter* inserter)
-{
- if (this->vals() == NULL)
- return this;
-
- // If this is a constant struct, we don't need temporaries.
- 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();
- ++pv)
- {
- if (*pv != NULL)
- {
- if ((*pv)->is_error_expression() || (*pv)->type()->is_error_type())
- {
- go_assert(saw_errors());
- return Expression::make_error(loc);
- }
- if (!(*pv)->is_multi_eval_safe())
- {
- Temporary_statement* temp =
- Statement::make_temporary(NULL, *pv, loc);
- inserter->insert(temp);
- *pv = Expression::make_temporary_reference(temp, loc);
- }
- }
- }
- return this;
-}
-
// Make implicit type conversions explicit.
void
@@ -15440,50 +15713,6 @@ Array_construction_expression::do_check_types(Gogo*)
}
}
-// Flatten an array construction expression. Store the values into
-// temporaries in case they need interface conversion.
-
-Expression*
-Array_construction_expression::do_flatten(Gogo*, Named_object*,
- Statement_inserter* inserter)
-{
- if (this->is_error_expression())
- {
- go_assert(saw_errors());
- return this;
- }
-
- if (this->vals() == NULL)
- return this;
-
- // If this is a constant array, we don't need temporaries.
- 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();
- ++pv)
- {
- if (*pv != NULL)
- {
- if ((*pv)->is_error_expression() || (*pv)->type()->is_error_type())
- {
- go_assert(saw_errors());
- return Expression::make_error(loc);
- }
- if (!(*pv)->is_multi_eval_safe())
- {
- Temporary_statement* temp =
- Statement::make_temporary(NULL, *pv, loc);
- inserter->insert(temp);
- *pv = Expression::make_temporary_reference(temp, loc);
- }
- }
- }
- return this;
-}
-
// Make implicit type conversions explicit.
void
@@ -15752,14 +15981,14 @@ Slice_construction_expression::create_array_val()
// the new temp statement.
Expression*
-Slice_construction_expression::do_flatten(Gogo* gogo, Named_object* no,
+Slice_construction_expression::do_flatten(Gogo*, Named_object*,
Statement_inserter* inserter)
{
if (this->type()->array_type() == NULL)
- return NULL;
-
- // Base class flattening first
- this->Array_construction_expression::do_flatten(gogo, no, inserter);
+ {
+ go_assert(saw_errors());
+ return Expression::make_error(this->location());
+ }
// Create a stack-allocated storage temp if storage won't escape
if (!this->storage_escapes_
@@ -15768,7 +15997,7 @@ Slice_construction_expression::do_flatten(Gogo* gogo, Named_object* no,
{
Location loc = this->location();
this->array_val_ = this->create_array_val();
- go_assert(this->array_val_);
+ go_assert(this->array_val_ != NULL);
Temporary_statement* temp =
Statement::make_temporary(this->valtype_, this->array_val_, loc);
inserter->insert(temp);
@@ -17238,7 +17467,9 @@ Heap_expression::do_get_backend(Translate_context* context)
Bfunction* fndecl = fn->func_value()->get_or_make_decl(gogo, fn);
Bvariable* space_temp =
gogo->backend()->temporary_variable(fndecl, context->bblock(), btype,
- space, true, loc, &decl);
+ space,
+ Backend::variable_address_is_taken,
+ loc, &decl);
Btype* expr_btype = etype->get_backend(gogo);
Bexpression* bexpr = this->expr_->get_backend(context);
@@ -17259,8 +17490,9 @@ Heap_expression::do_get_backend(Translate_context* context)
Bstatement* edecl;
Bvariable* btemp =
gogo->backend()->temporary_variable(fndecl, context->bblock(),
- expr_btype, bexpr, true, loc,
- &edecl);
+ expr_btype, bexpr,
+ Backend::variable_address_is_taken,
+ loc, &edecl);
Bexpression* btempref = gogo->backend()->var_expression(btemp,
loc);
space = gogo->backend()->var_expression(space_temp, loc);
@@ -17802,49 +18034,7 @@ Expression::make_type_info(Type* type, Type_info type_info)
return new Type_info_expression(type, type_info);
}
-// An expression that evaluates to some characteristic of a slice.
-// This is used when indexing, bound-checking, or nil checking a slice.
-
-class Slice_info_expression : public Expression
-{
- public:
- Slice_info_expression(Expression* slice, Slice_info slice_info,
- Location location)
- : Expression(EXPRESSION_SLICE_INFO, location),
- slice_(slice), slice_info_(slice_info)
- { }
-
- protected:
- Type*
- do_type();
-
- void
- do_determine_type(const Type_context*)
- { }
-
- Expression*
- do_copy()
- {
- return new Slice_info_expression(this->slice_->copy(), this->slice_info_,
- this->location());
- }
-
- Bexpression*
- do_get_backend(Translate_context* context);
-
- void
- do_dump_expression(Ast_dump_context*) const;
-
- void
- do_issue_nil_check()
- { this->slice_->issue_nil_check(); }
-
- private:
- // The slice for which we are getting information.
- Expression* slice_;
- // What information we want.
- Slice_info slice_info_;
-};
+// Slice_info_expression.
// Return the type of the slice info.
@@ -18448,12 +18638,20 @@ Interface_mtable_expression::do_get_backend(Translate_context* context)
else
m = st->method_function(p->name(), &is_ambiguous);
go_assert(m != NULL);
- Named_object* no =
- (this->is_pointer_
- && this->type_->is_direct_iface_type()
- && m->is_value_method()
- ? m->iface_stub_object()
- : m->named_object());
+
+ // See the comment in Type::method_constructor.
+ bool use_direct_iface_stub = false;
+ if (m->is_value_method()
+ && this->is_pointer_
+ && this->type_->is_direct_iface_type())
+ use_direct_iface_stub = true;
+ if (!m->is_value_method()
+ && this->is_pointer_
+ && !this->type_->in_heap())
+ use_direct_iface_stub = true;
+ Named_object* no = (use_direct_iface_stub
+ ? m->iface_stub_object()
+ : m->named_object());
go_assert(no->is_function() || no->is_function_declaration());
@@ -18478,10 +18676,13 @@ Interface_mtable_expression::do_get_backend(Translate_context* context)
Bexpression* ctor =
gogo->backend()->constructor_expression(btype, ctor_bexprs, loc);
- this->bvar_ = gogo->backend()->immutable_struct(mangled_name, "", false,
- !is_public, btype, loc);
- gogo->backend()->immutable_struct_set_init(this->bvar_, mangled_name, false,
- !is_public, btype, loc, ctor);
+ unsigned int flags = 0;
+ if (!is_public)
+ flags |= Backend::variable_is_hidden;
+ this->bvar_ = gogo->backend()->immutable_struct(mangled_name, "", flags,
+ btype, loc);
+ gogo->backend()->immutable_struct_set_init(this->bvar_, mangled_name, flags,
+ btype, loc, ctor);
return gogo->backend()->var_expression(this->bvar_, loc);
}
diff --git a/gcc/go/gofrontend/expressions.h b/gcc/go/gofrontend/expressions.h
index e3747cc..9f8f4e9 100644
--- a/gcc/go/gofrontend/expressions.h
+++ b/gcc/go/gofrontend/expressions.h
@@ -62,6 +62,7 @@ class Type_guard_expression;
class Heap_expression;
class Receive_expression;
class Slice_value_expression;
+class Slice_info_expression;
class Conditional_expression;
class Compound_expression;
class Numeric_constant;
@@ -731,6 +732,10 @@ class Expression
call_expression()
{ return this->convert<Call_expression, EXPRESSION_CALL>(); }
+ const Call_expression*
+ call_expression() const
+ { return this->convert<const Call_expression, EXPRESSION_CALL>(); }
+
// If this is a call_result expression, return the Call_result_expression
// structure. Otherwise, return NULL. This is a controlled dynamic
// cast.
@@ -900,6 +905,14 @@ class Expression
compound_expression()
{ return this->convert<Compound_expression, EXPRESSION_COMPOUND>(); }
+ // If this is a slice info expression, return the
+ // Slice_info_expression structure. Otherwise, return NULL.
+ Slice_info_expression*
+ slice_info_expression()
+ {
+ return this->convert<Slice_info_expression, EXPRESSION_SLICE_INFO>();
+ }
+
// Return true if this is a composite literal.
bool
is_composite_literal() const;
@@ -2451,13 +2464,16 @@ class Call_expression : public Expression
// Whether this is a call to builtin function.
virtual bool
- is_builtin()
+ is_builtin() const
{ return false; }
// Convert to a Builtin_call_expression, or return NULL.
inline Builtin_call_expression*
builtin_call_expression();
+ inline const Builtin_call_expression*
+ builtin_call_expression() const;
+
protected:
int
do_traverse(Traverse*);
@@ -2608,18 +2624,20 @@ class Builtin_call_expression : public Call_expression
BUILTIN_RECOVER,
// Builtin functions from the unsafe package.
+ BUILTIN_ADD,
BUILTIN_ALIGNOF,
BUILTIN_OFFSETOF,
- BUILTIN_SIZEOF
+ BUILTIN_SIZEOF,
+ BUILTIN_SLICE
};
Builtin_function_code
- code()
+ code() const
{ return this->code_; }
// This overrides Call_expression::is_builtin.
bool
- is_builtin()
+ is_builtin() const
{ return true; }
// Return whether EXPR, of array type, is a constant if passed to
@@ -2715,6 +2733,14 @@ Call_expression::builtin_call_expression()
: NULL);
}
+inline const Builtin_call_expression*
+Call_expression::builtin_call_expression() const
+{
+ return (this->is_builtin()
+ ? static_cast<const Builtin_call_expression*>(this)
+ : NULL);
+}
+
// A single result from a call which returns multiple results.
class Call_result_expression : public Expression
@@ -3806,9 +3832,6 @@ class Struct_construction_expression : public Expression,
Expression*
do_copy();
- Expression*
- do_flatten(Gogo*, Named_object*, Statement_inserter*);
-
Bexpression*
do_get_backend(Translate_context*);
@@ -3881,9 +3904,6 @@ protected:
indexes()
{ return this->indexes_; }
- Expression*
- do_flatten(Gogo*, Named_object*, Statement_inserter*);
-
// Get the backend constructor for the array values.
Bexpression*
get_constructor(Translate_context* context, Btype* btype);
@@ -4266,6 +4286,60 @@ class Slice_value_expression : public Expression
Expression* cap_;
};
+// An expression that evaluates to some characteristic of a slice.
+// This is used when indexing, bound-checking, or nil checking a slice.
+
+class Slice_info_expression : public Expression
+{
+ public:
+ Slice_info_expression(Expression* slice, Slice_info slice_info,
+ Location location)
+ : Expression(EXPRESSION_SLICE_INFO, location),
+ slice_(slice), slice_info_(slice_info)
+ { }
+
+ // The slice operand of this slice info expression.
+ Expression*
+ slice() const
+ { return this->slice_; }
+
+ // The info this expression is about.
+ Slice_info
+ info() const
+ { return this->slice_info_; }
+
+ protected:
+ Type*
+ do_type();
+
+ void
+ do_determine_type(const Type_context*)
+ { }
+
+ Expression*
+ do_copy()
+ {
+ return new Slice_info_expression(this->slice_->copy(), this->slice_info_,
+ this->location());
+ }
+
+ Bexpression*
+ do_get_backend(Translate_context* context);
+
+ void
+ do_dump_expression(Ast_dump_context*) const;
+
+ void
+ do_issue_nil_check()
+ { this->slice_->issue_nil_check(); }
+
+ private:
+ // The slice for which we are getting information.
+ Expression* slice_;
+ // What information we want.
+ Slice_info slice_info_;
+};
+
// Conditional expressions.
class Conditional_expression : public Expression
@@ -4281,6 +4355,14 @@ class Conditional_expression : public Expression
condition() const
{ return this->cond_; }
+ Expression*
+ then_expr() const
+ { return this->then_; }
+
+ Expression*
+ else_expr() const
+ { return this->else_; }
+
protected:
int
do_traverse(Traverse*);
@@ -4326,6 +4408,10 @@ class Compound_expression : public Expression
init() const
{ return this->init_; }
+ Expression*
+ expr() const
+ { return this->expr_; }
+
protected:
int
do_traverse(Traverse*);
diff --git a/gcc/go/gofrontend/gogo.cc b/gcc/go/gofrontend/gogo.cc
index 93b54fd..95b76bd 100644
--- a/gcc/go/gofrontend/gogo.cc
+++ b/gcc/go/gofrontend/gogo.cc
@@ -918,9 +918,8 @@ Gogo::build_type_descriptor_list()
// Create the variable
std::string name = this->type_descriptor_list_symbol(this->pkgpath_symbol());
- Bvariable* bv = this->backend()->implicit_variable(name, name, bt,
- false, true, false,
- 0);
+ unsigned int flags = Backend::variable_is_constant;
+ Bvariable* bv = this->backend()->implicit_variable(name, name, bt, flags, 0);
// Build the initializer
std::vector<unsigned long> indexes;
@@ -946,8 +945,7 @@ Gogo::build_type_descriptor_list()
Bexpression* binit =
this->backend()->constructor_expression(bt, fields, builtin_loc);
- this->backend()->implicit_variable_set_init(bv, name, bt, false,
- true, false, binit);
+ this->backend()->implicit_variable_set_init(bv, name, bt, flags, binit);
}
// Register the type descriptors with the runtime. This is to help
@@ -1002,11 +1000,11 @@ Gogo::register_type_descriptors(std::vector<Bstatement*>& init_stmts,
// Create a variable holding the list.
std::string name = this->typelists_symbol();
- Bvariable* bv = this->backend()->implicit_variable(name, name, bat,
- true, true, false,
+ unsigned int flags = (Backend::variable_is_hidden
+ | Backend::variable_is_constant);
+ Bvariable* bv = this->backend()->implicit_variable(name, name, bat, flags,
0);
- this->backend()->implicit_variable_set_init(bv, name, bat, true, true,
- false, barray);
+ this->backend()->implicit_variable_set_init(bv, name, bat, flags, barray);
// Build the call in main package's init function.
Translate_context context(this, NULL, NULL, NULL);
@@ -5765,6 +5763,26 @@ Function::check_labels() const
}
}
+// Set the receiver type. This is used to remove aliases.
+
+void
+Function::set_receiver_type(Type* rtype)
+{
+ Function_type* oft = this->type_;
+ Typed_identifier* rec = new Typed_identifier(oft->receiver()->name(),
+ rtype,
+ oft->receiver()->location());
+ Typed_identifier_list* parameters = NULL;
+ if (oft->parameters() != NULL)
+ parameters = oft->parameters()->copy();
+ Typed_identifier_list* results = NULL;
+ if (oft->results() != NULL)
+ results = oft->results()->copy();
+ Function_type* nft = Type::make_function_type(rec, parameters, results,
+ oft->location());
+ this->type_ = nft;
+}
+
// Swap one function with another. This is used when building the
// thunk we use to call a function which calls recover. It may not
// work for any other case.
@@ -7287,6 +7305,26 @@ Function_declaration::set_nointerface()
this->pragmas_ |= GOPRAGMA_NOINTERFACE;
}
+// Set the receiver type. This is used to remove aliases.
+
+void
+Function_declaration::set_receiver_type(Type* rtype)
+{
+ Function_type* oft = this->fntype_;
+ Typed_identifier* rec = new Typed_identifier(oft->receiver()->name(),
+ rtype,
+ oft->receiver()->location());
+ Typed_identifier_list* parameters = NULL;
+ if (oft->parameters() != NULL)
+ parameters = oft->parameters()->copy();
+ Typed_identifier_list* results = NULL;
+ if (oft->results() != NULL)
+ results = oft->results()->copy();
+ Function_type* nft = Type::make_function_type(rec, parameters, results,
+ oft->location());
+ this->fntype_ = nft;
+}
+
// Import an inlinable function. This is used for an inlinable
// function whose body is recorded in the export data. Parse the
// export data into a Block and create a regular function using that
@@ -8062,16 +8100,24 @@ Variable::get_backend_variable(Gogo* gogo, Named_object* function,
if (package != NULL)
is_hidden = false;
+ unsigned int flags = 0;
+ if (this->is_address_taken_
+ || this->is_non_escaping_address_taken_)
+ flags |= Backend::variable_address_is_taken;
+ if (package != NULL)
+ flags |= Backend::variable_is_external;
+ if (is_hidden)
+ flags |= Backend::variable_is_hidden;
+ if (this->in_unique_section_)
+ flags |= Backend::variable_in_unique_section;
+
// For some reason asm_name can't be the empty string
// for global_variable, so we call asm_name rather than
// optional_asm_name here. FIXME.
bvar = backend->global_variable(bname.name(),
bname.asm_name(),
- btype,
- package != NULL,
- is_hidden,
- this->in_unique_section_,
+ btype, flags,
this->location_);
}
else if (function == NULL)
@@ -8083,15 +8129,16 @@ Variable::get_backend_variable(Gogo* gogo, Named_object* function,
{
const std::string n = Gogo::unpack_hidden_name(name);
Bfunction* bfunction = function->func_value()->get_decl();
- bool is_address_taken = (this->is_non_escaping_address_taken_
- && !this->is_in_heap());
+ unsigned int flags = 0;
+ if (this->is_non_escaping_address_taken_
+ && !this->is_in_heap())
+ flags |= Backend::variable_address_is_taken;
if (this->is_closure())
bvar = backend->static_chain_variable(bfunction, n, btype,
- this->location_);
+ flags, this->location_);
else if (is_parameter)
bvar = backend->parameter_variable(bfunction, n, btype,
- is_address_taken,
- this->location_);
+ flags, this->location_);
else
{
Bvariable* bvar_decl = NULL;
@@ -8102,8 +8149,7 @@ Variable::get_backend_variable(Gogo* gogo, Named_object* function,
->get_backend_variable(&context);
}
bvar = backend->local_variable(bfunction, n, btype,
- bvar_decl,
- is_address_taken,
+ bvar_decl, flags,
this->location_);
}
}
@@ -8134,10 +8180,12 @@ Result_variable::get_backend_variable(Gogo* gogo, Named_object* function,
Btype* btype = type->get_backend(gogo);
Bfunction* bfunction = function->func_value()->get_decl();
std::string n = Gogo::unpack_hidden_name(name);
- bool is_address_taken = (this->is_non_escaping_address_taken_
- && !this->is_in_heap());
+ unsigned int flags = 0;
+ if (this->is_non_escaping_address_taken_
+ && !this->is_in_heap())
+ flags |= Backend::variable_address_is_taken;
this->backend_ = backend->local_variable(bfunction, n, btype,
- NULL, is_address_taken,
+ NULL, flags,
this->location_);
}
}
diff --git a/gcc/go/gofrontend/gogo.h b/gcc/go/gofrontend/gogo.h
index f4155a2..9ffd120 100644
--- a/gcc/go/gofrontend/gogo.h
+++ b/gcc/go/gofrontend/gogo.h
@@ -533,6 +533,10 @@ class Gogo
register_package(const std::string& pkgpath,
const std::string& pkgpath_symbol, Location);
+ // Add the unsafe bindings to the unsafe package.
+ void
+ add_unsafe_bindings(Package*);
+
// Look up a package by pkgpath, and return its pkgpath_symbol.
std::string
pkgpath_symbol_for_package(const std::string&);
@@ -1724,6 +1728,10 @@ class Function
set_is_referenced_by_inline()
{ this->is_referenced_by_inline_ = true; }
+ // Set the receiver type. This is used to remove aliases.
+ void
+ set_receiver_type(Type* rtype);
+
// Swap with another function. Used only for the thunk which calls
// recover.
void
@@ -1990,6 +1998,10 @@ class Function_declaration
set_is_on_inlinable_list()
{ this->is_on_inlinable_list_ = true; }
+ // Set the receiver type. This is used to remove aliases.
+ void
+ set_receiver_type(Type* rtype);
+
// Import the function body, creating a function.
void
import_function_body(Gogo*, Named_object*);
diff --git a/gcc/go/gofrontend/import.cc b/gcc/go/gofrontend/import.cc
index f671416..6a5491b 100644
--- a/gcc/go/gofrontend/import.cc
+++ b/gcc/go/gofrontend/import.cc
@@ -497,6 +497,9 @@ Import::read_one_import()
p->set_package_name(package_name, this->location());
this->packages_.push_back(p);
+
+ if (pkgpath == "unsafe")
+ this->gogo_->add_unsafe_bindings(p);
}
// Read an indirectimport line.
@@ -515,6 +518,9 @@ Import::read_one_indirect_import()
p->set_package_name(package_name, this->location());
this->packages_.push_back(p);
+
+ if (pkgpath == "unsafe")
+ this->gogo_->add_unsafe_bindings(p);
}
// Read the list of import control functions and/or init graph.
diff --git a/gcc/go/gofrontend/runtime.def b/gcc/go/gofrontend/runtime.def
index ec01be0..87a2708 100644
--- a/gcc/go/gofrontend/runtime.def
+++ b/gcc/go/gofrontend/runtime.def
@@ -204,12 +204,8 @@ DEF_GO_RUNTIME(SELECTNBSEND, "runtime.selectnbsend", P2(CHAN, POINTER), R1(BOOL)
// Non-blocking receive a value from a channel, used for two-case select
// statement with a default case.
-DEF_GO_RUNTIME(SELECTNBRECV, "runtime.selectnbrecv", P2(POINTER, CHAN), R1(BOOL))
-
-// Non-blocking tuple receive from a channel, used for two-case select
-// statement with a default case.
-DEF_GO_RUNTIME(SELECTNBRECV2, "runtime.selectnbrecv2", P3(POINTER, POINTER, CHAN),
- R1(BOOL))
+DEF_GO_RUNTIME(SELECTNBRECV, "runtime.selectnbrecv", P2(POINTER, CHAN),
+ R2(BOOL, BOOL))
// Block execution. Used for zero-case select.
DEF_GO_RUNTIME(BLOCK, "runtime.block", P0(), R0())
@@ -489,6 +485,12 @@ DEF_GO_RUNTIME(ATOMIC_OR_FETCH_1, "__atomic_or_fetch_1",
P3(POINTER, UINT8, INT32),
R1(UINT8))
+// Check the length of an unsafe slice.
+DEF_GO_RUNTIME(UNSAFESLICE, "runtime.unsafeslice",
+ P3(TYPE, POINTER, INT), R0())
+DEF_GO_RUNTIME(UNSAFESLICE64, "runtime.unsafeslice64",
+ P3(TYPE, POINTER, INT64), R0())
+
// Panic reporting a division by zero.
DEF_GO_RUNTIME(PANIC_DIVIDE, "runtime.panicdivide", P0(), R0())
@@ -576,6 +578,11 @@ DEF_GO_RUNTIME(PANIC_EXTEND_SLICE3_C, "runtime.goPanicExtendSlice3C",
DEF_GO_RUNTIME(PANIC_EXTEND_SLICE3_C_U, "runtime.goPanicExtendSlice3CU",
P2(UINT64, INT), R0())
+// Panic for conversion of slice to pointer-to-array if the slice is
+// too short.
+DEF_GO_RUNTIME(PANIC_SLICE_CONVERT, "runtime.goPanicSliceConvert",
+ P2(INT, INT), R0())
+
// Remove helper macros.
#undef ABFT6
#undef ABFT2
diff --git a/gcc/go/gofrontend/statements.cc b/gcc/go/gofrontend/statements.cc
index b066011..95fa3c4 100644
--- a/gcc/go/gofrontend/statements.cc
+++ b/gcc/go/gofrontend/statements.cc
@@ -643,11 +643,13 @@ Temporary_statement::do_get_backend(Translate_context* context)
binit = context->backend()->convert_expression(btype, binit,
this->location());
+ unsigned int flags = 0;
+ if (this->is_address_taken_)
+ flags |= Backend::variable_address_is_taken;
Bstatement* statement;
this->bvariable_ =
context->backend()->temporary_variable(bfunction, context->bblock(),
- btype, binit,
- this->is_address_taken_,
+ btype, binit, flags,
this->location(), &statement);
return statement;
}
@@ -6049,7 +6051,7 @@ Select_statement::lower_two_case(Block* b)
Expression* chanref = Expression::make_temporary_reference(chantmp, loc);
Block* bchan;
- Expression* call;
+ Expression* cond;
if (chancase.is_send())
{
// if selectnbsend(chan, &val) { body } else { default body }
@@ -6063,7 +6065,7 @@ Select_statement::lower_two_case(Block* b)
Expression* ref = Expression::make_temporary_reference(ts, loc);
Expression* addr = Expression::make_unary(OPERATOR_AND, ref, loc);
- call = Runtime::make_call(Runtime::SELECTNBSEND, loc, 2, chanref, addr);
+ cond = Runtime::make_call(Runtime::SELECTNBSEND, loc, 2, chanref, addr);
bchan = chancase.statements();
}
else
@@ -6073,34 +6075,31 @@ Select_statement::lower_two_case(Block* b)
Expression* ref = Expression::make_temporary_reference(ts, loc);
Expression* addr = Expression::make_unary(OPERATOR_AND, ref, loc);
- Expression* okref = NULL;
- if (chancase.closed() == NULL && chancase.closedvar() == NULL)
- {
- // Simple receive.
- // if selectnbrecv(&lhs, chan) { body } else { default body }
- call = Runtime::make_call(Runtime::SELECTNBRECV, loc, 2, addr, chanref);
- }
- else
- {
- // Tuple receive.
- // if selectnbrecv2(&lhs, &ok, chan) { body } else { default body }
-
- Type* booltype = Type::make_boolean_type();
- Temporary_statement* okts = Statement::make_temporary(booltype, NULL,
- loc);
- b->add_statement(okts);
-
- okref = Expression::make_temporary_reference(okts, loc);
- Expression* okaddr = Expression::make_unary(OPERATOR_AND, okref, loc);
- call = Runtime::make_call(Runtime::SELECTNBRECV2, loc, 3, addr, okaddr,
- chanref);
- }
+
+ // selected, ok = selectnbrecv(&lhs, chan)
+ Call_expression* call = Runtime::make_call(Runtime::SELECTNBRECV, loc, 2,
+ addr, chanref);
+
+ Temporary_statement* selected_temp =
+ Statement::make_temporary(Type::make_boolean_type(),
+ Expression::make_call_result(call, 0),
+ loc);
+ b->add_statement(selected_temp);
+
+ Temporary_statement* ok_temp =
+ Statement::make_temporary(Type::make_boolean_type(),
+ Expression::make_call_result(call, 1),
+ loc);
+ b->add_statement(ok_temp);
+
+ cond = Expression::make_temporary_reference(selected_temp, loc);
Location cloc = chancase.location();
bchan = new Block(b, loc);
if (chancase.val() != NULL && !chancase.val()->is_sink_expression())
{
- Statement* as = Statement::make_assignment(chancase.val(), ref->copy(),
+ Statement* as = Statement::make_assignment(chancase.val(),
+ ref->copy(),
cloc);
bchan->add_statement(as);
}
@@ -6112,12 +6111,18 @@ Select_statement::lower_two_case(Block* b)
if (chancase.closed() != NULL && !chancase.closed()->is_sink_expression())
{
+ Expression* okref = Expression::make_temporary_reference(ok_temp,
+ cloc);
Statement* as = Statement::make_assignment(chancase.closed(),
- okref->copy(), cloc);
+ okref, cloc);
bchan->add_statement(as);
}
else if (chancase.closedvar() != NULL)
- chancase.closedvar()->var_value()->set_init(okref->copy());
+ {
+ Expression* okref = Expression::make_temporary_reference(ok_temp,
+ cloc);
+ chancase.closedvar()->var_value()->set_init(okref);
+ }
Statement* bs = Statement::make_block_statement(chancase.statements(),
cloc);
@@ -6125,7 +6130,7 @@ Select_statement::lower_two_case(Block* b)
}
Statement* ifs =
- Statement::make_if_statement(call, bchan, defcase.statements(), loc);
+ Statement::make_if_statement(cond, bchan, defcase.statements(), loc);
b->add_statement(ifs);
Statement* label =
diff --git a/gcc/go/gofrontend/types.cc b/gcc/go/gofrontend/types.cc
index 7d4c47f..cd69250 100644
--- a/gcc/go/gofrontend/types.cc
+++ b/gcc/go/gofrontend/types.cc
@@ -842,6 +842,15 @@ Type::are_convertible(const Type* lhs, const Type* rhs, std::string* reason)
return true;
}
+ // A slice may be converted to a pointer-to-array.
+ if (rhs->is_slice_type()
+ && lhs->points_to() != NULL
+ && lhs->points_to()->array_type() != NULL
+ && !lhs->points_to()->is_slice_type()
+ && Type::are_identical(lhs->points_to()->array_type()->element_type(),
+ rhs->array_type()->element_type(), 0, reason))
+ return true;
+
// An unsafe.Pointer type may be converted to any pointer type or to
// a type whose underlying type is uintptr, and vice-versa.
if (lhs->is_unsafe_pointer_type()
@@ -1410,10 +1419,12 @@ Type::make_type_descriptor_var(Gogo* gogo)
// ensure that type_descriptor_pointer will work if called while
// converting INITIALIZER.
+ unsigned int flags = 0;
+ if (is_common)
+ flags |= Backend::variable_is_common;
this->type_descriptor_var_ =
gogo->backend()->immutable_struct(bname.name(), bname.optional_asm_name(),
- false, is_common, initializer_btype,
- loc);
+ flags, initializer_btype, loc);
if (phash != NULL)
*phash = this->type_descriptor_var_;
@@ -1422,7 +1433,7 @@ Type::make_type_descriptor_var(Gogo* gogo)
Bexpression* binitializer = initializer->get_backend(&context);
gogo->backend()->immutable_struct_set_init(this->type_descriptor_var_,
- bname.name(), false, is_common,
+ bname.name(), flags,
initializer_btype, loc,
binitializer);
@@ -2453,8 +2464,16 @@ Type::is_direct_iface_type() const
bool
Type::is_direct_iface_type_helper(Unordered_set(const Type*)* visited) const
{
- if (this->points_to() != NULL
- || this->channel_type() != NULL
+ if (this->points_to() != NULL)
+ {
+ // Pointers to notinheap types must be stored indirectly. See
+ // https://golang.org/issue/42076.
+ if (!this->points_to()->in_heap())
+ return false;
+ return true;
+ }
+
+ if (this->channel_type() != NULL
|| this->function_type() != NULL
|| this->map_type() != NULL)
return true;
@@ -2714,9 +2733,13 @@ Type::make_gc_symbol_var(Gogo* gogo)
// 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.
+ unsigned int flags = Backend::variable_is_constant;
+ if (is_common)
+ flags |= Backend::variable_is_common;
+ else
+ flags |= Backend::variable_is_hidden;
this->gc_symbol_var_ =
- gogo->backend()->implicit_variable(sym_name, "", sym_btype, false, true,
- is_common, 0);
+ gogo->backend()->implicit_variable(sym_name, "", sym_btype, flags, 0);
if (phash != NULL)
*phash = this->gc_symbol_var_;
@@ -2724,8 +2747,7 @@ Type::make_gc_symbol_var(Gogo* gogo)
context.set_is_const();
Bexpression* sym_binit = sym_init->get_backend(&context);
gogo->backend()->implicit_variable_set_init(this->gc_symbol_var_, sym_name,
- sym_btype, false, true, is_common,
- sym_binit);
+ sym_btype, flags, sym_binit);
}
// Return whether this type needs a GC program, and set *PTRDATA to
@@ -3013,11 +3035,12 @@ Type::gc_ptrmask_var(Gogo* gogo, int64_t ptrsize, int64_t ptrdata)
Bexpression* bval = val->get_backend(&context);
Btype *btype = val->type()->get_backend(gogo);
+ unsigned int flags = (Backend::variable_is_constant
+ | Backend::variable_is_common);
Bvariable* ret = gogo->backend()->implicit_variable(sym_name, "",
- btype, false, true,
- true, 0);
- gogo->backend()->implicit_variable_set_init(ret, sym_name, btype, false,
- true, true, bval);
+ btype, flags, 0);
+ gogo->backend()->implicit_variable_set_init(ret, sym_name, btype, flags,
+ bval);
ins.first->second = ret;
return ret;
}
@@ -3582,10 +3605,36 @@ Type::method_constructor(Gogo*, Type* method_type,
vals->push_back(Expression::make_unary(OPERATOR_AND, s, bloc));
}
- bool use_direct_iface_stub =
- this->points_to() != NULL
- && this->points_to()->is_direct_iface_type()
- && m->is_value_method();
+ // The direct_iface_stub dereferences the value stored in the
+ // interface when calling the method.
+ //
+ // We need this for a value method if this type is a pointer to a
+ // direct-iface type. For example, if we have "type C chan int" and M
+ // is a value method on C, then since a channel is a direct-iface type
+ // M expects a value of type C. We are generating the method table
+ // for *C, so the value stored in the interface is *C. We have to
+ // call the direct-iface stub to dereference *C to get C to pass to M.
+ //
+ // We also need this for a pointer method if the pointer itself is not
+ // a direct-iface type, as arises for notinheap types. In this case
+ // we have "type NIH ..." where NIH is go:notinheap. Since NIH is
+ // notinheap, *NIH is a pointer type that is not a direct-iface type,
+ // so the value stored in the interface is actually **NIH. The method
+ // expects *NIH, so we have to call the direct-iface stub to
+ // dereference **NIH to get *NIH to pass to M. (This case doesn't
+ // arise for value methods because pointer types can't have methods,
+ // so there is no such thing as a value method for type *NIH.)
+
+ bool use_direct_iface_stub = false;
+ if (m->is_value_method()
+ && this->points_to() != NULL
+ && this->points_to()->is_direct_iface_type())
+ use_direct_iface_stub = true;
+ if (!m->is_value_method()
+ && this->points_to() != NULL
+ && !this->is_direct_iface_type())
+ use_direct_iface_stub = true;
+
Named_object* no = (use_direct_iface_stub
? m->iface_stub_object()
: (m->needs_stub_method()
@@ -5001,6 +5050,7 @@ Function_type::get_backend_fntype(Gogo* gogo)
Struct_type* st = Type::make_struct_type(sfl,
this->location());
st->set_is_struct_incomparable();
+ st->set_is_results_struct();
ins.first->second = st->get_backend(gogo);
}
bresult_struct = ins.first->second;
@@ -6409,7 +6459,7 @@ get_backend_struct_fields(Gogo* gogo, Struct_type* type, bool use_placeholder,
saw_nonzero = true;
}
go_assert(i == fields->size());
- if (saw_nonzero && lastsize == 0)
+ if (saw_nonzero && lastsize == 0 && !type->is_results_struct())
{
// For nonzero-sized structs which end in a zero-sized thing, we add
// an extra byte of padding to the type. This padding ensures that
@@ -8145,11 +8195,12 @@ Map_type::backend_zero_value(Gogo* gogo)
Btype* barray_type = gogo->backend()->array_type(buint8_type, blength);
std::string zname = Map_type::zero_value->name();
+ unsigned int flags = Backend::variable_is_common;
Bvariable* zvar =
- gogo->backend()->implicit_variable(zname, "", barray_type, false, false,
- true, Map_type::zero_value_align);
+ gogo->backend()->implicit_variable(zname, "", barray_type, flags,
+ Map_type::zero_value_align);
gogo->backend()->implicit_variable_set_init(zvar, zname, barray_type,
- false, false, true, NULL);
+ flags, NULL);
return zvar;
}
@@ -10409,6 +10460,57 @@ Named_type::finalize_methods(Gogo* gogo)
return;
}
+ // Remove any aliases in the local method receiver types.
+ Bindings* methods = this->local_methods_;
+ if (methods != NULL)
+ {
+ for (Bindings::const_declarations_iterator p =
+ methods->begin_declarations();
+ p != methods->end_declarations();
+ ++p)
+ {
+ Named_object* no = p->second;
+ Function_type* fntype;
+ if (no->is_function())
+ fntype = no->func_value()->type();
+ else if (no->is_function_declaration())
+ fntype = no->func_declaration_value()->type();
+ else
+ {
+ go_assert(saw_errors());
+ continue;
+ }
+
+ Type* rtype = fntype->receiver()->type();
+ bool is_pointer = false;
+ Type* pt = rtype->points_to();
+ if (pt != NULL)
+ {
+ rtype = pt;
+ is_pointer = true;
+ }
+ if (rtype->named_type() != this)
+ {
+ if (rtype->unalias() != this)
+ {
+ go_assert(saw_errors());
+ continue;
+ }
+
+ rtype = this;
+ if (is_pointer)
+ rtype = Type::make_pointer_type(rtype);
+
+ if (no->is_function())
+ no->func_value()->set_receiver_type(rtype);
+ else if (no->is_function_declaration())
+ no->func_declaration_value()->set_receiver_type(rtype);
+ else
+ go_unreachable();
+ }
+ }
+ }
+
Type::finalize_methods(gogo, this, this->location_, &this->all_methods_);
}
@@ -10835,6 +10937,20 @@ Named_type::do_needs_key_update()
return ret;
}
+// Return whether this type is permitted in the heap.
+bool
+Named_type::do_in_heap() const
+{
+ if (!this->in_heap_)
+ return false;
+ if (this->seen_)
+ return true;
+ this->seen_ = true;
+ bool ret = this->type_->in_heap();
+ this->seen_ = false;
+ return ret;
+}
+
// Return a hash code. This is used for method lookup. We simply
// hash on the name itself.
@@ -11367,7 +11483,7 @@ Type::finalize_methods(Gogo* gogo, const Type* type, Location location,
*all_methods = NULL;
}
Type::build_stub_methods(gogo, type, *all_methods, location);
- if (type->is_direct_iface_type())
+ if (type->is_direct_iface_type() || !type->in_heap())
Type::build_direct_iface_stub_methods(gogo, type, *all_methods, location);
}
@@ -11747,12 +11863,23 @@ Type::build_direct_iface_stub_methods(Gogo* gogo, const Type* type,
if (methods == NULL)
return;
+ bool is_direct_iface = type->is_direct_iface_type();
+ bool in_heap = type->in_heap();
for (Methods::const_iterator p = methods->begin();
p != methods->end();
++p)
{
Method* m = p->second;
- if (!m->is_value_method())
+
+ // We need a direct-iface stub for a value method for a
+ // direct-iface type, and for a pointer method for a not-in-heap
+ // type.
+ bool need_stub = false;
+ if (is_direct_iface && m->is_value_method())
+ need_stub = true;
+ if (!in_heap && !m->is_value_method())
+ need_stub = true;
+ if (!need_stub)
continue;
Type* receiver_type = const_cast<Type*>(type);
diff --git a/gcc/go/gofrontend/types.h b/gcc/go/gofrontend/types.h
index f2880f9..0c51806 100644
--- a/gcc/go/gofrontend/types.h
+++ b/gcc/go/gofrontend/types.h
@@ -2501,7 +2501,8 @@ class Struct_type : public Type
Struct_type(Struct_field_list* fields, Location location)
: Type(TYPE_STRUCT),
fields_(fields), location_(location), all_methods_(NULL),
- is_struct_incomparable_(false), has_padding_(false)
+ is_struct_incomparable_(false), has_padding_(false),
+ is_results_struct_(false)
{ }
// Return the field NAME. This only looks at local fields, not at
@@ -2632,6 +2633,17 @@ class Struct_type : public Type
set_has_padding()
{ this->has_padding_ = true; }
+ // Return whether this is a results struct created to hold the
+ // results of a function that returns multiple results.
+ bool
+ is_results_struct() const
+ { return this->is_results_struct_; }
+
+ // Record that this is a results struct.
+ void
+ set_is_results_struct()
+ { this->is_results_struct_ = true; }
+
// Write the hash function for this type.
void
write_hash_function(Gogo*, Function_type*);
@@ -2742,6 +2754,9 @@ class Struct_type : public Type
// True if this struct's backend type has padding, due to trailing
// zero-sized field.
bool has_padding_;
+ // True if this is a results struct created to hold the results of a
+ // function that returns multiple results.
+ bool is_results_struct_;
};
// The type of an array.
@@ -3605,8 +3620,7 @@ class Named_type : public Type
do_needs_key_update();
bool
- do_in_heap() const
- { return this->in_heap_ && this->type_->in_heap(); }
+ do_in_heap() const;
unsigned int
do_hash_for_method(Gogo*, int) const;
diff --git a/gcc/go/gofrontend/unsafe.cc b/gcc/go/gofrontend/unsafe.cc
index 9ed5b9d..c4a9346 100644
--- a/gcc/go/gofrontend/unsafe.cc
+++ b/gcc/go/gofrontend/unsafe.cc
@@ -10,15 +10,12 @@
#include "types.h"
#include "gogo.h"
-// Set up the builtin unsafe package. This should probably be driven
-// by a table.
+// Set up the builtin unsafe package.
void
Gogo::import_unsafe(const std::string& local_name, bool is_local_name_exported,
Location location)
{
- Location bloc = Linemap::predeclared_location();
-
bool add_to_globals;
Package* package = this->add_imported_package("unsafe", local_name,
is_local_name_exported,
@@ -34,10 +31,40 @@ Gogo::import_unsafe(const std::string& local_name, bool is_local_name_exported,
package->set_location(location);
this->imports_.insert(std::make_pair("unsafe", package));
+ this->add_unsafe_bindings(package);
+
+ Named_object* pointer_no = package->bindings()->lookup_local("Pointer");
+ pointer_no->type_value()->set_is_visible();
+
+ if (add_to_globals)
+ {
+ Bindings* bindings = package->bindings();
+ for (Bindings::const_declarations_iterator p =
+ bindings->begin_declarations();
+ p != bindings->end_declarations();
+ ++p)
+ this->add_dot_import_object(p->second);
+ }
+}
+
+// Add the unsafe bindings to the Package object. This should
+// probably be driven by a table.
+
+void
+Gogo::add_unsafe_bindings(Package* package)
+{
Bindings* bindings = package->bindings();
+ if (bindings->lookup_local("Sizeof") != NULL)
+ {
+ // Already done by an earlier import.
+ return;
+ }
+
+ Location bloc = Linemap::predeclared_location();
+
// The type may have already been created by an import.
- Named_object* no = package->bindings()->lookup("Pointer");
+ Named_object* no = bindings->lookup("Pointer");
if (no == NULL)
{
Type* type = Type::make_pointer_type(Type::make_void_type());
@@ -49,11 +76,12 @@ Gogo::import_unsafe(const std::string& local_name, bool is_local_name_exported,
go_assert(no->package() == package);
go_assert(no->is_type());
go_assert(no->type_value()->is_unsafe_pointer_type());
- no->type_value()->set_is_visible();
}
Named_type* pointer_type = no->type_value();
- if (add_to_globals)
- this->add_named_type(pointer_type);
+
+ // This may be called during an import, so the type may not be
+ // visible yet.
+ pointer_type->clear_is_visible();
Type* uintptr_type = Type::lookup_integer_type("uintptr");
@@ -62,9 +90,7 @@ Gogo::import_unsafe(const std::string& local_name, bool is_local_name_exported,
results->push_back(Typed_identifier("", uintptr_type, bloc));
Function_type* fntype = Type::make_function_type(NULL, NULL, results, bloc);
fntype->set_is_builtin();
- no = bindings->add_function_declaration("Sizeof", package, fntype, bloc);
- if (add_to_globals)
- this->add_dot_import_object(no);
+ bindings->add_function_declaration("Sizeof", package, fntype, bloc);
// Offsetof.
results = new Typed_identifier_list;
@@ -72,9 +98,7 @@ Gogo::import_unsafe(const std::string& local_name, bool is_local_name_exported,
fntype = Type::make_function_type(NULL, NULL, results, bloc);
fntype->set_is_varargs();
fntype->set_is_builtin();
- no = bindings->add_function_declaration("Offsetof", package, fntype, bloc);
- if (add_to_globals)
- this->add_dot_import_object(no);
+ bindings->add_function_declaration("Offsetof", package, fntype, bloc);
// Alignof.
results = new Typed_identifier_list;
@@ -82,9 +106,19 @@ Gogo::import_unsafe(const std::string& local_name, bool is_local_name_exported,
fntype = Type::make_function_type(NULL, NULL, results, bloc);
fntype->set_is_varargs();
fntype->set_is_builtin();
- no = bindings->add_function_declaration("Alignof", package, fntype, bloc);
- if (add_to_globals)
- this->add_dot_import_object(no);
+ bindings->add_function_declaration("Alignof", package, fntype, bloc);
+
+ // Add.
+ results = new Typed_identifier_list;
+ results->push_back(Typed_identifier("", pointer_type, bloc));
+ fntype = Type::make_function_type(NULL, NULL, results, bloc);
+ fntype->set_is_builtin();
+ bindings->add_function_declaration("Add", package, fntype, bloc);
+
+ // Slice.
+ fntype = Type::make_function_type(NULL, NULL, NULL, bloc);
+ fntype->set_is_builtin();
+ bindings->add_function_declaration("Slice", package, fntype, bloc);
if (!this->imported_unsafe_)
{