In the backend interface for the Go frontend, I foolishly used size_t for the size of a type. That usually works, but fails when compiling on a 32-bit host for a 64-bit target and compiling code that uses very large types. The maximum type size for any Go target is a signed 64-bit number, so for simplicity I changed the type size and alignment routines to all return int64_t. The rest of this patch adjusts the Go frontend to deal with that correctly, and to fix the overflow checks when dealing with types whose size does not fit into a host unsigned long. This is PRs 64836 and 64838. Bootstrapped and ran Go testsuite on x86_64-unknown-linux-gnu. Committed to mainline.
Ian 2015-02-02 Ian Lance Taylor <i...@google.com> PR go/64836 PR go/64838 * go-gcc.cc (Gcc_backend::type_size): Change return type to int64_t. (Gcc_backend::type_alignment): Likewise. (Gcc_backend::type_field_alignment): Likewise. (Gcc_backend::type_field_offset): Likewise. (Gcc_backend::implicit_variable): Change alignment parameter type to int64_t.
Index: gcc/go/go-gcc.cc =================================================================== --- gcc/go/go-gcc.cc (revision 219876) +++ gcc/go/go-gcc.cc (working copy) @@ -223,16 +223,16 @@ class Gcc_backend : public Backend bool is_circular_pointer_type(Btype*); - size_t + int64_t type_size(Btype*); - size_t + int64_t type_alignment(Btype*); - size_t + int64_t type_field_alignment(Btype*); - size_t + int64_t type_field_offset(Btype*, size_t index); // Expressions. @@ -411,7 +411,7 @@ class Gcc_backend : public Backend Bvariable* implicit_variable(const std::string&, Btype*, bool, bool, bool, - size_t); + int64_t); void implicit_variable_set_init(Bvariable*, const std::string&, Btype*, @@ -1097,7 +1097,7 @@ Gcc_backend::is_circular_pointer_type(Bt // Return the size of a type. -size_t +int64_t Gcc_backend::type_size(Btype* btype) { tree t = btype->get_tree(); @@ -1106,14 +1106,14 @@ Gcc_backend::type_size(Btype* btype) t = TYPE_SIZE_UNIT(t); gcc_assert(tree_fits_uhwi_p (t)); unsigned HOST_WIDE_INT val_wide = TREE_INT_CST_LOW(t); - size_t ret = static_cast<size_t>(val_wide); - gcc_assert(ret == val_wide); + int64_t ret = static_cast<int64_t>(val_wide); + gcc_assert(ret >= 0 && static_cast<unsigned HOST_WIDE_INT>(ret) == val_wide); return ret; } // Return the alignment of a type. -size_t +int64_t Gcc_backend::type_alignment(Btype* btype) { tree t = btype->get_tree(); @@ -1124,7 +1124,7 @@ Gcc_backend::type_alignment(Btype* btype // Return the alignment of a struct field of type BTYPE. -size_t +int64_t Gcc_backend::type_field_alignment(Btype* btype) { tree t = btype->get_tree(); @@ -1135,7 +1135,7 @@ Gcc_backend::type_field_alignment(Btype* // Return the offset of a field in a struct. -size_t +int64_t Gcc_backend::type_field_offset(Btype* btype, size_t index) { tree struct_tree = btype->get_tree(); @@ -1149,9 +1149,8 @@ Gcc_backend::type_field_offset(Btype* bt gcc_assert(field != NULL_TREE); } HOST_WIDE_INT offset_wide = int_byte_position(field); - gcc_assert(offset_wide >= 0); - size_t ret = static_cast<size_t>(offset_wide); - gcc_assert(ret == static_cast<unsigned HOST_WIDE_INT>(offset_wide)); + int64_t ret = static_cast<int64_t>(offset_wide); + gcc_assert(ret == offset_wide); return ret; } @@ -2609,7 +2608,7 @@ Gcc_backend::temporary_variable(Bfunctio Bvariable* Gcc_backend::implicit_variable(const std::string& name, Btype* type, bool is_hidden, bool is_constant, - bool is_common, size_t alignment) + bool is_common, int64_t alignment) { tree type_tree = type->get_tree(); if (type_tree == error_mark_node) Index: gcc/go/gofrontend/backend.h =================================================================== --- gcc/go/gofrontend/backend.h (revision 219876) +++ gcc/go/gofrontend/backend.h (working copy) @@ -216,22 +216,22 @@ class Backend is_circular_pointer_type(Btype*) = 0; // Return the size of a type. - virtual size_t + virtual int64_t type_size(Btype*) = 0; // Return the alignment of a type. - virtual size_t + virtual int64_t type_alignment(Btype*) = 0; // Return the alignment of a struct field of this type. This is // normally the same as type_alignment, but not always. - virtual size_t + virtual int64_t type_field_alignment(Btype*) = 0; // Return the offset of field INDEX in a struct type. INDEX is the // entry in the FIELDS std::vector parameter of struct_type or // set_placeholder_struct_type. - virtual size_t + virtual int64_t type_field_offset(Btype*, size_t index) = 0; // Expressions. @@ -575,7 +575,7 @@ class Backend // If ALIGNMENT is not zero, it is the desired alignment of the variable. virtual Bvariable* implicit_variable(const std::string& name, Btype* type, bool is_hidden, - bool is_constant, bool is_common, size_t alignment) = 0; + bool is_constant, bool is_common, int64_t alignment) = 0; // Set the initial value of a variable created by implicit_variable. Index: gcc/go/gofrontend/expressions.cc =================================================================== --- gcc/go/gofrontend/expressions.cc (revision 219984) +++ gcc/go/gofrontend/expressions.cc (working copy) @@ -181,8 +181,10 @@ Expression::convert_for_assignment(Gogo* // represented as non-zero-sized. // TODO(cmang): This check is for a GCC-specific issue, and should be // removed from the frontend. FIXME. - size_t lhs_size = gogo->backend()->type_size(lhs_type->get_backend(gogo)); - size_t rhs_size = gogo->backend()->type_size(rhs_type->get_backend(gogo)); + int64_t lhs_size = + gogo->backend()->type_size(lhs_type->get_backend(gogo)); + int64_t rhs_size = + gogo->backend()->type_size(rhs_type->get_backend(gogo)); if (rhs_size == 0 || lhs_size == 0) return rhs; @@ -2112,6 +2114,48 @@ Expression::make_integer_sl(long val, Ty return ret; } +// Store an int64_t in an uninitialized mpz_t. + +static void +set_mpz_from_int64(mpz_t* zval, int64_t val) +{ + if (val >= 0) + { + unsigned long ul = static_cast<unsigned long>(val); + if (static_cast<int64_t>(ul) == val) + { + mpz_init_set_ui(*zval, ul); + return; + } + } + uint64_t uv; + if (val >= 0) + uv = static_cast<uint64_t>(val); + else + uv = static_cast<uint64_t>(- val); + unsigned long ul = uv & 0xffffffffUL; + mpz_init_set_ui(*zval, ul); + mpz_t hval; + mpz_init_set_ui(hval, static_cast<unsigned long>(uv >> 32)); + mpz_mul_2exp(hval, hval, 32); + mpz_add(*zval, *zval, hval); + mpz_clear(hval); + if (val < 0) + mpz_neg(*zval, *zval); +} + +// Build a new integer value from an int64_t. + +Expression* +Expression::make_integer_int64(int64_t val, Type* type, Location location) +{ + mpz_t zval; + set_mpz_from_int64(&zval, val); + Expression* ret = Expression::make_integer_z(&zval, type, location); + mpz_clear(zval); + return ret; +} + // Build a new character constant value. Expression* @@ -3694,7 +3738,7 @@ Unary_expression::do_flatten(Gogo* gogo, if (!ptype->is_void_type()) { Btype* pbtype = ptype->get_backend(gogo); - size_t s = gogo->backend()->type_size(pbtype); + int64_t s = gogo->backend()->type_size(pbtype); if (s >= 4096 || this->issue_nil_check_) { Temporary_statement* temp = @@ -4182,7 +4226,7 @@ Unary_expression::do_get_backend(Transla Btype* pbtype = ptype->get_backend(gogo); if (!ptype->is_void_type()) { - size_t s = gogo->backend()->type_size(pbtype); + int64_t s = gogo->backend()->type_size(pbtype); if (s >= 4096 || this->issue_nil_check_) { go_assert(this->expr_->is_variable()); @@ -7361,7 +7405,7 @@ Builtin_call_expression::do_numeric_cons if (this->seen_) return false; - unsigned long ret; + int64_t ret; if (this->code_ == BUILTIN_SIZEOF) { this->seen_ = true; @@ -7389,7 +7433,10 @@ Builtin_call_expression::do_numeric_cons else go_unreachable(); - nc->set_unsigned_long(Type::lookup_integer_type("uintptr"), ret); + mpz_t zval; + set_mpz_from_int64(&zval, ret); + nc->set_int(Type::lookup_integer_type("uintptr"), zval); + mpz_clear(zval); return true; } else if (this->code_ == BUILTIN_OFFSETOF) @@ -7403,7 +7450,7 @@ Builtin_call_expression::do_numeric_cons if (this->seen_) return false; - unsigned int total_offset = 0; + int64_t total_offset = 0; while (true) { Expression* struct_expr = farg->expr(); @@ -7412,7 +7459,7 @@ Builtin_call_expression::do_numeric_cons return false; if (st->named_type() != NULL) st->named_type()->convert(this->gogo_); - unsigned int offset; + int64_t offset; this->seen_ = true; bool ok = st->struct_type()->backend_field_offset(this->gogo_, farg->field_index(), @@ -7429,8 +7476,10 @@ Builtin_call_expression::do_numeric_cons } break; } - nc->set_unsigned_long(Type::lookup_integer_type("uintptr"), - static_cast<unsigned long>(total_offset)); + mpz_t zval; + set_mpz_from_int64(&zval, total_offset); + nc->set_int(Type::lookup_integer_type("uintptr"), zval); + mpz_clear(zval); return true; } else if (this->code_ == BUILTIN_REAL || this->code_ == BUILTIN_IMAG) @@ -8329,10 +8378,10 @@ Builtin_call_expression::do_get_backend( Type* element_type = at->element_type(); Btype* element_btype = element_type->get_backend(gogo); - size_t element_size = gogo->backend()->type_size(element_btype); - Expression* size_expr = Expression::make_integer_ul(element_size, - length->type(), - location); + int64_t element_size = gogo->backend()->type_size(element_btype); + Expression* size_expr = Expression::make_integer_int64(element_size, + length->type(), + location); Expression* bytecount = Expression::make_binary(OPERATOR_MULT, size_expr, length, location); Expression* copy = Runtime::make_call(Runtime::COPY, location, 3, @@ -8355,7 +8404,7 @@ Builtin_call_expression::do_get_backend( go_assert(arg2->is_variable()); Expression* arg2_val; Expression* arg2_len; - unsigned long size; + int64_t size; if (arg2->type()->is_string_type() && element_type->integer_type() != NULL && element_type->integer_type()->is_byte()) @@ -8374,7 +8423,7 @@ Builtin_call_expression::do_get_backend( size = gogo->backend()->type_size(element_btype); } Expression* element_size = - Expression::make_integer_ul(size, NULL, location); + Expression::make_integer_int64(size, NULL, location); Expression* append = Runtime::make_call(Runtime::APPEND, location, 4, arg1, arg2_val, arg2_len, @@ -14028,7 +14077,7 @@ Type_info_expression::do_get_backend(Tra { Btype* btype = this->type_->get_backend(context->gogo()); Gogo* gogo = context->gogo(); - size_t val; + int64_t val; switch (this->type_info_) { case TYPE_INFO_SIZE: @@ -14043,13 +14092,9 @@ Type_info_expression::do_get_backend(Tra default: go_unreachable(); } - mpz_t cst; - mpz_init_set_ui(cst, val); - Btype* int_btype = this->type()->get_backend(gogo); - Bexpression* ret = - gogo->backend()->integer_constant_expression(int_btype, cst); - mpz_clear(cst); - return ret; + Expression* e = Expression::make_integer_int64(val, this->type(), + this->location()); + return e->get_backend(context); } // Dump ast representation for a type info expression. @@ -14780,11 +14825,11 @@ Struct_field_offset_expression::do_get_b Gogo* gogo = context->gogo(); Btype* btype = this->type_->get_backend(gogo); - size_t offset = gogo->backend()->type_field_offset(btype, i); + int64_t offset = gogo->backend()->type_field_offset(btype, i); Type* uptr_type = Type::lookup_integer_type("uintptr"); Expression* ret = - Expression::make_integer_ul(offset, uptr_type, - Linemap::predeclared_location()); + Expression::make_integer_int64(offset, uptr_type, + Linemap::predeclared_location()); return ret->get_backend(context); } Index: gcc/go/gofrontend/expressions.h =================================================================== --- gcc/go/gofrontend/expressions.h (revision 219876) +++ gcc/go/gofrontend/expressions.h (working copy) @@ -233,6 +233,11 @@ class Expression static Expression* make_integer_sl(long, Type*, Location); + // Make a constant integer expression from an int64_t. TYPE should + // be NULL for an abstract type. + static Expression* + make_integer_int64(int64_t, Type*, Location); + // Make a constant float expression. TYPE should be NULL for an // abstract type. static Expression* Index: gcc/go/gofrontend/gogo.cc =================================================================== --- gcc/go/gofrontend/gogo.cc (revision 220291) +++ gcc/go/gofrontend/gogo.cc (working copy) @@ -602,7 +602,7 @@ Gogo::zero_value(Type *type) } // The zero value will be the maximum required size. - unsigned long size; + int64_t size; bool ok = type->backend_type_size(this, &size); if (!ok) { go_assert(saw_errors()); @@ -611,7 +611,7 @@ Gogo::zero_value(Type *type) if (size > this->zero_value_size_) this->zero_value_size_ = size; - unsigned long align; + int64_t align; ok = type->backend_type_align(this, &align); if (!ok) { go_assert(saw_errors()); @@ -644,13 +644,12 @@ Gogo::backend_zero_value() Btype* bbtype_type = byte_type->get_backend(this); Type* int_type = this->lookup_global("int")->type_value(); - Btype* bint_type = int_type->get_backend(this); - mpz_t val; - mpz_init_set_ui(val, this->zero_value_size_); - Bexpression* blength = - this->backend()->integer_constant_expression(bint_type, val); - mpz_clear(val); + Expression* e = Expression::make_integer_int64(this->zero_value_size_, + int_type, + Linemap::unknown_location()); + Translate_context context(this, NULL, NULL, NULL); + Bexpression* blength = e->get_backend(&context); Btype* barray_type = this->backend()->array_type(bbtype_type, blength); Index: gcc/go/gofrontend/gogo.h =================================================================== --- gcc/go/gofrontend/gogo.h (revision 220268) +++ gcc/go/gofrontend/gogo.h (working copy) @@ -746,9 +746,9 @@ class Gogo // The special zero value variable. Named_object* zero_value_; // The size of the zero value variable. - unsigned long zero_value_size_; + int64_t zero_value_size_; // The alignment of the zero value variable, in bytes. - unsigned long zero_value_align_; + int64_t zero_value_align_; // Whether pkgpath_ has been set. bool pkgpath_set_; // Whether an explicit package path was set by -fgo-pkgpath. Index: gcc/go/gofrontend/types.cc =================================================================== --- gcc/go/gofrontend/types.cc (revision 219876) +++ gcc/go/gofrontend/types.cc (working copy) @@ -2533,15 +2533,12 @@ Type::is_backend_type_size_known(Gogo* g // the backend. bool -Type::backend_type_size(Gogo* gogo, unsigned long *psize) +Type::backend_type_size(Gogo* gogo, int64_t *psize) { if (!this->is_backend_type_size_known(gogo)) return false; Btype* bt = this->get_backend_placeholder(gogo); - size_t size = gogo->backend()->type_size(bt); - *psize = static_cast<unsigned long>(size); - if (*psize != size) - return false; + *psize = gogo->backend()->type_size(bt); return true; } @@ -2549,15 +2546,12 @@ Type::backend_type_size(Gogo* gogo, unsi // the alignment in bytes and return true. Otherwise, return false. bool -Type::backend_type_align(Gogo* gogo, unsigned long *palign) +Type::backend_type_align(Gogo* gogo, int64_t *palign) { if (!this->is_backend_type_size_known(gogo)) return false; Btype* bt = this->get_backend_placeholder(gogo); - size_t align = gogo->backend()->type_alignment(bt); - *palign = static_cast<unsigned long>(align); - if (*palign != align) - return false; + *palign = gogo->backend()->type_alignment(bt); return true; } @@ -2565,15 +2559,12 @@ Type::backend_type_align(Gogo* gogo, uns // field. bool -Type::backend_type_field_align(Gogo* gogo, unsigned long *palign) +Type::backend_type_field_align(Gogo* gogo, int64_t *palign) { if (!this->is_backend_type_size_known(gogo)) return false; Btype* bt = this->get_backend_placeholder(gogo); - size_t a = gogo->backend()->type_field_alignment(bt); - *palign = static_cast<unsigned long>(a); - if (*palign != a) - return false; + *palign = gogo->backend()->type_field_alignment(bt); return true; } @@ -4780,7 +4771,7 @@ Struct_type::do_compare_is_identity(Gogo const Struct_field_list* fields = this->fields_; if (fields == NULL) return true; - unsigned long offset = 0; + int64_t offset = 0; for (Struct_field_list::const_iterator pf = fields->begin(); pf != fields->end(); ++pf) @@ -4791,7 +4782,7 @@ Struct_type::do_compare_is_identity(Gogo if (!pf->type()->compare_is_identity(gogo)) return false; - unsigned long field_align; + int64_t field_align; if (!pf->type()->backend_type_align(gogo, &field_align)) return false; if ((offset & (field_align - 1)) != 0) @@ -4802,13 +4793,13 @@ Struct_type::do_compare_is_identity(Gogo return false; } - unsigned long field_size; + int64_t field_size; if (!pf->type()->backend_type_size(gogo, &field_size)) return false; offset += field_size; } - unsigned long struct_size; + int64_t struct_size; if (!this->backend_type_size(gogo, &struct_size)) return false; if (offset != struct_size) @@ -5571,15 +5562,12 @@ Struct_type::do_mangled_name(Gogo* gogo, bool Struct_type::backend_field_offset(Gogo* gogo, unsigned int index, - unsigned int* poffset) + int64_t* poffset) { if (!this->is_backend_type_size_known(gogo)) return false; Btype* bt = this->get_backend_placeholder(gogo); - size_t offset = gogo->backend()->type_field_offset(bt, index); - *poffset = static_cast<unsigned int>(offset); - if (*poffset != offset) - return false; + *poffset = gogo->backend()->type_field_offset(bt, index); return true; } @@ -5764,10 +5752,17 @@ Array_type::verify_length() return false; } + Type* int_type = Type::lookup_integer_type("int"); + unsigned int tbits = int_type->integer_type()->bits(); unsigned long val; switch (nc.to_unsigned_long(&val)) { case Numeric_constant::NC_UL_VALID: + if (sizeof(val) >= tbits / 8 && val >> (tbits - 1) != 0) + { + error_at(this->length_->location(), "array bound overflows"); + return false; + } break; case Numeric_constant::NC_UL_NOTINT: error_at(this->length_->location(), "array bound truncated to integer"); @@ -5776,21 +5771,23 @@ Array_type::verify_length() error_at(this->length_->location(), "negative array bound"); return false; case Numeric_constant::NC_UL_BIG: - error_at(this->length_->location(), "array bound overflows"); - return false; + { + mpz_t val; + if (!nc.to_int(&val)) + go_unreachable(); + unsigned int bits = mpz_sizeinbase(val, 2); + mpz_clear(val); + if (bits >= tbits) + { + error_at(this->length_->location(), "array bound overflows"); + return false; + } + } + break; default: go_unreachable(); } - Type* int_type = Type::lookup_integer_type("int"); - unsigned int tbits = int_type->integer_type()->bits(); - if (sizeof(val) <= tbits * 8 - && val >> (tbits - 1) != 0) - { - error_at(this->length_->location(), "array bound overflows"); - return false; - } - return true; } @@ -5820,8 +5817,8 @@ Array_type::do_compare_is_identity(Gogo* return false; // If there is any padding, then we can't use memcmp. - unsigned long size; - unsigned long align; + int64_t size; + int64_t align; if (!this->element_type_->backend_type_size(gogo, &size) || !this->element_type_->backend_type_align(gogo, &align)) return false; @@ -6417,7 +6414,7 @@ Array_type::slice_gc_symbol(Gogo* gogo, // Differentiate between slices with zero-length and non-zero-length values. Type* element_type = this->element_type(); Btype* ebtype = element_type->get_backend(gogo); - size_t element_size = gogo->backend()->type_size(ebtype); + int64_t element_size = gogo->backend()->type_size(ebtype); Type* uintptr_type = Type::lookup_integer_type("uintptr"); unsigned long opval = element_size == 0 ? GC_APTR : GC_SLICE; @@ -6444,8 +6441,8 @@ Array_type::array_gc_symbol(Gogo* gogo, go_assert(saw_errors()); Btype* pbtype = gogo->backend()->pointer_type(gogo->backend()->void_type()); - size_t pwidth = gogo->backend()->type_size(pbtype); - size_t iwidth = gogo->backend()->type_size(this->get_backend(gogo)); + int64_t pwidth = gogo->backend()->type_size(pbtype); + int64_t iwidth = gogo->backend()->type_size(this->get_backend(gogo)); Type* element_type = this->element_type(); if (bound < 1 || !element_type->has_pointer()) Index: gcc/go/gofrontend/types.h =================================================================== --- gcc/go/gofrontend/types.h (revision 219876) +++ gcc/go/gofrontend/types.h (working copy) @@ -942,18 +942,18 @@ class Type // in bytes and return true. Otherwise, return false. This queries // the backend. bool - backend_type_size(Gogo*, unsigned long* psize); + backend_type_size(Gogo*, int64_t* psize); // If the alignment of the type can be determined, set *PALIGN to // the alignment in bytes and return true. Otherwise, return false. bool - backend_type_align(Gogo*, unsigned long* palign); + backend_type_align(Gogo*, int64_t* palign); // If the alignment of a struct field of this type can be // determined, set *PALIGN to the alignment in bytes and return // true. Otherwise, return false. bool - backend_type_field_align(Gogo*, unsigned long* palign); + backend_type_field_align(Gogo*, int64_t* palign); // Whether the backend size is known. bool @@ -2263,7 +2263,7 @@ class Struct_type : public Type // determined, set *POFFSET to the offset in bytes and return true. // Otherwise, return false. bool - backend_field_offset(Gogo*, unsigned int index, unsigned int* poffset); + backend_field_offset(Gogo*, unsigned int index, int64_t* poffset); // Finish the backend representation of all the fields. void