diff options
Diffstat (limited to 'gcc/go')
-rw-r--r-- | gcc/go/ChangeLog | 17 | ||||
-rw-r--r-- | gcc/go/gccgo.texi | 2 | ||||
-rw-r--r-- | gcc/go/go-gcc.cc | 158 | ||||
-rw-r--r-- | gcc/go/gofrontend/MERGE | 2 | ||||
-rw-r--r-- | gcc/go/gofrontend/backend.h | 145 | ||||
-rw-r--r-- | gcc/go/gofrontend/escape.cc | 164 | ||||
-rw-r--r-- | gcc/go/gofrontend/export.cc | 44 | ||||
-rw-r--r-- | gcc/go/gofrontend/export.h | 10 | ||||
-rw-r--r-- | gcc/go/gofrontend/expressions.cc | 587 | ||||
-rw-r--r-- | gcc/go/gofrontend/expressions.h | 106 | ||||
-rw-r--r-- | gcc/go/gofrontend/gogo.cc | 94 | ||||
-rw-r--r-- | gcc/go/gofrontend/gogo.h | 12 | ||||
-rw-r--r-- | gcc/go/gofrontend/import.cc | 6 | ||||
-rw-r--r-- | gcc/go/gofrontend/runtime.def | 19 | ||||
-rw-r--r-- | gcc/go/gofrontend/statements.cc | 65 | ||||
-rw-r--r-- | gcc/go/gofrontend/types.cc | 173 | ||||
-rw-r--r-- | gcc/go/gofrontend/types.h | 20 | ||||
-rw-r--r-- | gcc/go/gofrontend/unsafe.cc | 68 |
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(&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_) { |