compiler, runtime: provide index information on bounds check failure
authorIan Lance Taylor <ian@gcc.gnu.org>
Wed, 28 Aug 2019 18:27:30 +0000 (18:27 +0000)
committerIan Lance Taylor <ian@gcc.gnu.org>
Wed, 28 Aug 2019 18:27:30 +0000 (18:27 +0000)
    This implements https://golang.org/cl/161477 in the gofrontend.

    Updates golang/go#30116

    Reviewed-on: https://go-review.googlesource.com/c/gofrontend/+/191881

From-SVN: r274998

gcc/go/gofrontend/MERGE
gcc/go/gofrontend/expressions.cc
gcc/go/gofrontend/expressions.h
gcc/go/gofrontend/gogo.cc
gcc/go/gofrontend/runtime.cc
gcc/go/gofrontend/runtime.def
libgo/go/runtime/error.go
libgo/go/runtime/panic.go
libgo/go/runtime/panic32.go [new file with mode: 0644]
libgo/mkruntimeinc.sh

index 18127de31ac3e9d2a3cbef2af64081401ad74ffe..f596a6988400e42f53a6731667230922c0186960 100644 (file)
@@ -1,4 +1,4 @@
-a6ddd0e1208a7d229c10be630c1110b3914038f5
+189ff44b2c26f29f41f0eb159e0d8f3fa508ecae
 
 The first line of this file holds the git revision number of the last
 merge done from the gofrontend repository.
index 90a39a222b0347b3fed21cd2eb313b76642bfb06..939a5f7f3748a5da54d5217108dc4a2cfadec0fd 100644 (file)
@@ -594,67 +594,110 @@ Expression::backend_numeric_constant_expression(Translate_context* context,
   return ret;
 }
 
-// Return an expression which evaluates to true if VAL, of arbitrary integer
-// type, is negative or is more than the maximum value of the Go type "int".
+// Insert bounds checks for an index expression.  Check that that VAL
+// >= 0 and that it fits in an int.  Then check that VAL OP BOUND is
+// true.  If any condition is false, call one of the CODE runtime
+// functions, which will panic.
 
-Expression*
-Expression::check_bounds(Expression* val, Location loc)
+void
+Expression::check_bounds(Expression* val, Operator op, Expression* bound,
+                        Runtime::Function code,
+                        Runtime::Function code_u,
+                        Runtime::Function code_extend,
+                        Runtime::Function code_extend_u,
+                        Statement_inserter* inserter,
+                        Location loc)
 {
-  Type* val_type = val->type();
-  Type* bound_type = Type::lookup_integer_type("int");
+  go_assert(val->is_variable() || val->is_constant());
+  go_assert(bound->is_variable() || bound->is_constant());
 
-  int val_type_size;
-  bool val_is_unsigned = false;
-  if (val_type->integer_type() != NULL)
-    {
-      val_type_size = val_type->integer_type()->bits();
-      val_is_unsigned = val_type->integer_type()->is_unsigned();
-    }
-  else
-    {
-      if (!val_type->is_numeric_type()
-          || !Type::are_convertible(bound_type, val_type, NULL))
-        {
-          go_assert(saw_errors());
-          return Expression::make_boolean(true, loc);
-        }
+  Type* int_type = Type::lookup_integer_type("int");
+  int int_type_size = int_type->integer_type()->bits();
 
-      if (val_type->complex_type() != NULL)
-        val_type_size = val_type->complex_type()->bits();
-      else
-        val_type_size = val_type->float_type()->bits();
+  Type* val_type = val->type();
+  if (val_type->integer_type() == NULL)
+    {
+      go_assert(saw_errors());
+      return;
     }
+  int val_type_size = val_type->integer_type()->bits();
+  bool val_is_unsigned = val_type->integer_type()->is_unsigned();
 
-  Expression* negative_index = Expression::make_boolean(false, loc);
-  Expression* index_overflows = Expression::make_boolean(false, loc);
+  // Check that VAL >= 0.
+  Expression* check = NULL;
   if (!val_is_unsigned)
     {
       Expression* zero = Expression::make_integer_ul(0, val_type, loc);
-      negative_index = Expression::make_binary(OPERATOR_LT, val, zero, loc);
+      check = Expression::make_binary(OPERATOR_GE, val->copy(), zero, loc);
     }
 
-  int bound_type_size = bound_type->integer_type()->bits();
-  if (val_type_size > bound_type_size
-      || (val_type_size == bound_type_size
+  // If VAL's type is larger than int, check that VAL fits in an int.
+  if (val_type_size > int_type_size
+      || (val_type_size == int_type_size
          && val_is_unsigned))
     {
       mpz_t one;
       mpz_init_set_ui(one, 1UL);
 
-      // maxval = 2^(bound_type_size - 1) - 1
+      // maxval = 2^(int_type_size - 1) - 1
       mpz_t maxval;
       mpz_init(maxval);
-      mpz_mul_2exp(maxval, one, bound_type_size - 1);
+      mpz_mul_2exp(maxval, one, int_type_size - 1);
       mpz_sub_ui(maxval, maxval, 1);
       Expression* max = Expression::make_integer_z(&maxval, val_type, loc);
       mpz_clear(one);
       mpz_clear(maxval);
 
-      index_overflows = Expression::make_binary(OPERATOR_GT, val, max, loc);
+      Expression* cmp = Expression::make_binary(OPERATOR_LE, val->copy(),
+                                               max, loc);
+      if (check == NULL)
+       check = cmp;
+      else
+       check = Expression::make_binary(OPERATOR_ANDAND, check, cmp, loc);
+    }
+
+  // For the final check we can assume that VAL fits in an int.
+  Expression* ival;
+  if (val_type == int_type)
+    ival = val->copy();
+  else
+    ival = Expression::make_cast(int_type, val->copy(), loc);
+
+  // BOUND is assumed to fit in an int.  Either it comes from len or
+  // cap, or it was checked by an earlier call.
+  Expression* ibound;
+  if (bound->type() == int_type)
+    ibound = bound->copy();
+  else
+    ibound = Expression::make_cast(int_type, bound->copy(), loc);
+
+  Expression* cmp = Expression::make_binary(op, ival, ibound, loc);
+  if (check == NULL)
+    check = cmp;
+  else
+    check = Expression::make_binary(OPERATOR_ANDAND, check, cmp, loc);
+
+  Runtime::Function c;
+  if (val_type_size > int_type_size)
+    {
+      if (val_is_unsigned)
+       c = code_extend_u;
+      else
+       c = code_extend;
+    }
+  else
+    {
+      if (val_is_unsigned)
+       c = code_u;
+      else
+       c = code;
     }
 
-  return Expression::make_binary(OPERATOR_OROR, negative_index, index_overflows,
-                                 loc);
+  Expression* ignore = Expression::make_boolean(true, loc);
+  Expression* crash = Runtime::make_call(c, loc, 2,
+                                        val->copy(), bound->copy());
+  Expression* cond = Expression::make_conditional(check, ignore, crash, loc);
+  inserter->insert(Statement::make_statement(cond, true));
 }
 
 void
@@ -12666,7 +12709,8 @@ Array_index_expression::do_check_types(Gogo*)
   unsigned long v;
   if (this->start_->type()->integer_type() == NULL
       && !this->start_->type()->is_error()
-      && (!this->start_->numeric_constant_value(&nc)
+      && (!this->start_->type()->is_abstract()
+         || !this->start_->numeric_constant_value(&nc)
          || nc.to_unsigned_long(&v) == Numeric_constant::NC_UL_NOTINT))
     this->report_error(_("index must be integer"));
   if (this->end_ != NULL
@@ -12674,7 +12718,8 @@ Array_index_expression::do_check_types(Gogo*)
       && !this->end_->type()->is_error()
       && !this->end_->is_nil_expression()
       && !this->end_->is_error_expression()
-      && (!this->end_->numeric_constant_value(&nc)
+      && (!this->end_->type()->is_abstract()
+         || !this->end_->numeric_constant_value(&nc)
          || nc.to_unsigned_long(&v) == Numeric_constant::NC_UL_NOTINT))
     this->report_error(_("slice end must be integer"));
   if (this->cap_ != NULL
@@ -12682,7 +12727,8 @@ Array_index_expression::do_check_types(Gogo*)
       && !this->cap_->type()->is_error()
       && !this->cap_->is_nil_expression()
       && !this->cap_->is_error_expression()
-      && (!this->cap_->numeric_constant_value(&nc)
+      && (!this->cap_->type()->is_abstract()
+         || !this->cap_->numeric_constant_value(&nc)
          || nc.to_unsigned_long(&v) == Numeric_constant::NC_UL_NOTINT))
     this->report_error(_("slice capacity must be integer"));
 
@@ -12799,13 +12845,21 @@ Array_index_expression::do_must_eval_subexpressions_in_order(
   return true;
 }
 
-// Flatten array indexing by using temporary variables for slices and indexes.
+// Flatten array indexing: add temporary variables and bounds checks.
 
 Expression*
-Array_index_expression::do_flatten(Gogo*, Named_object*,
+Array_index_expression::do_flatten(Gogo* gogo, Named_object*,
                                    Statement_inserter* inserter)
 {
+  if (this->is_flattened_)
+    return this;
+  this->is_flattened_ = true;
+
   Location loc = this->location();
+
+  if (this->is_error_expression())
+    return Expression::make_error(loc);
+
   Expression* array = this->array_;
   Expression* start = this->start_;
   Expression* end = this->end_;
@@ -12823,34 +12877,157 @@ Array_index_expression::do_flatten(Gogo*, Named_object*,
       return Expression::make_error(loc);
     }
 
+  Array_type* array_type = this->array_->type()->array_type();
+  if (array_type == NULL)
+    {
+      go_assert(saw_errors());
+      return Expression::make_error(loc);
+    }
+
   Temporary_statement* temp;
-  if (array->type()->is_slice_type() && !array->is_variable())
+  if (array_type->is_slice_type() && !array->is_variable())
     {
       temp = Statement::make_temporary(NULL, array, loc);
       inserter->insert(temp);
       this->array_ = Expression::make_temporary_reference(temp, loc);
+      array = this->array_;
     }
-  if (!start->is_variable())
+  if (!start->is_variable() && !start->is_constant())
     {
       temp = Statement::make_temporary(NULL, start, loc);
       inserter->insert(temp);
       this->start_ = Expression::make_temporary_reference(temp, loc);
+      start = this->start_;
     }
   if (end != NULL
       && !end->is_nil_expression()
-      && !end->is_variable())
+      && !end->is_variable()
+      && !end->is_constant())
     {
       temp = Statement::make_temporary(NULL, end, loc);
       inserter->insert(temp);
       this->end_ = Expression::make_temporary_reference(temp, loc);
+      end = this->end_;
     }
-  if (cap != NULL && !cap->is_variable())
+  if (cap != NULL && !cap->is_variable() && !cap->is_constant())
     {
       temp = Statement::make_temporary(NULL, cap, loc);
       inserter->insert(temp);
       this->cap_ = Expression::make_temporary_reference(temp, loc);
+      cap = this->cap_;
+    }
+
+  if (!this->needs_bounds_check_)
+    return this;
+
+  Expression* len;
+  if (!array_type->is_slice_type())
+    {
+      len = array_type->get_length(gogo, this->array_);
+      go_assert(len->is_constant());
+    }
+  else
+    {
+      len = array_type->get_length(gogo, this->array_->copy());
+      temp = Statement::make_temporary(NULL, len, loc);
+      inserter->insert(temp);
+      len = Expression::make_temporary_reference(temp, loc);
+    }
+
+  Expression* scap = NULL;
+  if (array_type->is_slice_type())
+    {
+      scap = array_type->get_capacity(gogo, this->array_->copy());
+      temp = Statement::make_temporary(NULL, scap, loc);
+      inserter->insert(temp);
+      scap = Expression::make_temporary_reference(temp, loc);
     }
 
+  // The order of bounds checks here matches the order used by the gc
+  // compiler, as tested by issue30116[u].go.
+
+  if (cap != NULL)
+    {
+      if (array_type->is_slice_type())
+       Expression::check_bounds(cap, OPERATOR_LE, scap,
+                                Runtime::PANIC_SLICE3_ACAP,
+                                Runtime::PANIC_SLICE3_ACAP_U,
+                                Runtime::PANIC_EXTEND_SLICE3_ACAP,
+                                Runtime::PANIC_EXTEND_SLICE3_ACAP_U,
+                                inserter, loc);
+      else
+       Expression::check_bounds(cap, OPERATOR_LE, len,
+                                Runtime::PANIC_SLICE3_ALEN,
+                                Runtime::PANIC_SLICE3_ALEN_U,
+                                Runtime::PANIC_EXTEND_SLICE3_ALEN,
+                                Runtime::PANIC_EXTEND_SLICE3_ALEN_U,
+                                inserter, loc);
+
+      Expression* start_bound = cap;
+      if (end != NULL && !end->is_nil_expression())
+       {
+         Expression::check_bounds(end, OPERATOR_LE, cap,
+                                  Runtime::PANIC_SLICE3_B,
+                                  Runtime::PANIC_SLICE3_B_U,
+                                  Runtime::PANIC_EXTEND_SLICE3_B,
+                                  Runtime::PANIC_EXTEND_SLICE3_B_U,
+                                  inserter, loc);
+         start_bound = end;
+       }
+
+      Expression::check_bounds(start, OPERATOR_LE, start_bound,
+                              Runtime::PANIC_SLICE3_C,
+                              Runtime::PANIC_SLICE3_C_U,
+                              Runtime::PANIC_EXTEND_SLICE3_C,
+                              Runtime::PANIC_EXTEND_SLICE3_C_U,
+                              inserter, loc);
+    }
+  else if (end != NULL && !end->is_nil_expression())
+    {
+      if (array_type->is_slice_type())
+       Expression::check_bounds(end, OPERATOR_LE, scap,
+                                Runtime::PANIC_SLICE_ACAP,
+                                Runtime::PANIC_SLICE_ACAP_U,
+                                Runtime::PANIC_EXTEND_SLICE_ACAP,
+                                Runtime::PANIC_EXTEND_SLICE_ACAP_U,
+                                inserter, loc);
+      else
+       Expression::check_bounds(end, OPERATOR_LE, len,
+                                Runtime::PANIC_SLICE_ALEN,
+                                Runtime::PANIC_SLICE_ALEN_U,
+                                Runtime::PANIC_EXTEND_SLICE_ALEN,
+                                Runtime::PANIC_EXTEND_SLICE_ALEN_U,
+                                inserter, loc);
+
+      Expression::check_bounds(start, OPERATOR_LE, end,
+                              Runtime::PANIC_SLICE_B,
+                              Runtime::PANIC_SLICE_B_U,
+                              Runtime::PANIC_EXTEND_SLICE_B,
+                              Runtime::PANIC_EXTEND_SLICE_B_U,
+                              inserter, loc);
+    }
+  else if (end != NULL)
+    {
+      Expression* start_bound;
+      if (array_type->is_slice_type())
+       start_bound = scap;
+      else
+       start_bound = len;
+      Expression::check_bounds(start, OPERATOR_LE, start_bound,
+                              Runtime::PANIC_SLICE_B,
+                              Runtime::PANIC_SLICE_B_U,
+                              Runtime::PANIC_EXTEND_SLICE_B,
+                              Runtime::PANIC_EXTEND_SLICE_B_U,
+                              inserter, loc);
+    }
+  else
+    Expression::check_bounds(start, OPERATOR_LT, len,
+                            Runtime::PANIC_INDEX,
+                            Runtime::PANIC_INDEX_U,
+                            Runtime::PANIC_EXTEND_INDEX,
+                            Runtime::PANIC_EXTEND_INDEX_U,
+                            inserter, loc);
+
   return this;
 }
 
@@ -12899,10 +13076,8 @@ Array_index_expression::do_get_backend(Translate_context* context)
   Type* int_type = Type::lookup_integer_type("int");
   Btype* int_btype = int_type->get_backend(gogo);
 
-  // We need to convert the length and capacity to the Go "int" type here
-  // because the length of a fixed-length array could be of type "uintptr"
-  // and gimple disallows binary operations between "uintptr" and other
-  // integer types. FIXME.
+  // Convert the length and capacity to "int".  FIXME: Do we need to
+  // do this?
   Bexpression* length = NULL;
   if (this->end_ == NULL || this->end_->is_nil_expression())
     {
@@ -12939,53 +13114,18 @@ Array_index_expression::do_get_backend(Translate_context* context)
   Bexpression* start = this->start_->get_backend(context);
   start = gogo->backend()->convert_expression(int_btype, start, loc);
 
-  Bexpression* crash = NULL;
-  Bexpression* bad_index = NULL;
-  if (this->needs_bounds_check_)
-    {
-      int code = (array_type->length() != NULL
-                  ? (this->end_ == NULL
-                     ? RUNTIME_ERROR_ARRAY_INDEX_OUT_OF_BOUNDS
-                     : RUNTIME_ERROR_ARRAY_SLICE_OUT_OF_BOUNDS)
-                  : (this->end_ == NULL
-                     ? RUNTIME_ERROR_SLICE_INDEX_OUT_OF_BOUNDS
-                     : RUNTIME_ERROR_SLICE_SLICE_OUT_OF_BOUNDS));
-      crash = gogo->runtime_error(code, loc)->get_backend(context);
-      bad_index = Expression::check_bounds(this->start_, loc)->get_backend(context);
-      Bexpression* start_too_large =
-        gogo->backend()->binary_expression((this->end_ == NULL
-                                            ? OPERATOR_GE
-                                            : OPERATOR_GT),
-                                           start,
-                                           (this->end_ == NULL
-                                            ? length
-                                            : capacity),
-                                           loc);
-      bad_index = gogo->backend()->binary_expression(OPERATOR_OROR,
-                                                     start_too_large,
-                                                     bad_index, loc);
-    }
-
-
   Bfunction* bfn = context->function()->func_value()->get_decl();
   if (this->end_ == NULL)
     {
-      // Simple array indexing.  This has to return an l-value, so
-      // wrap the index check into START.
-      if (this->needs_bounds_check_)
-        start =
-          gogo->backend()->conditional_expression(bfn, int_btype, bad_index,
-                                                  crash, start, loc);
-
+      // Simple array indexing.
       Bexpression* ret;
-      if (array_type->length() != NULL)
+      if (!array_type->is_slice_type())
        {
          Bexpression* array = this->array_->get_backend(context);
          ret = gogo->backend()->array_index_expression(array, start, loc);
        }
       else
        {
-         // Slice.
          Expression* valptr =
               array_type->get_value_pointer(gogo, this->array_,
                                             this->is_lvalue_);
@@ -12999,31 +13139,7 @@ Array_index_expression::do_get_backend(Translate_context* context)
       return ret;
     }
 
-  // Array slice.
-
-  if (this->cap_ != NULL)
-    {
-      cap_arg = gogo->backend()->convert_expression(int_btype, cap_arg, loc);
-
-      if (this->needs_bounds_check_)
-        {
-          Bexpression* bounds_bcheck =
-            Expression::check_bounds(this->cap_, loc)->get_backend(context);
-          bad_index =
-            gogo->backend()->binary_expression(OPERATOR_OROR, bounds_bcheck,
-                                               bad_index, loc);
-
-          Bexpression* cap_too_small =
-            gogo->backend()->binary_expression(OPERATOR_LT, cap_arg, start, loc);
-          Bexpression* cap_too_large =
-            gogo->backend()->binary_expression(OPERATOR_GT, cap_arg, capacity, loc);
-          Bexpression* bad_cap =
-            gogo->backend()->binary_expression(OPERATOR_OROR, cap_too_small,
-                                               cap_too_large, loc);
-          bad_index = gogo->backend()->binary_expression(OPERATOR_OROR, bad_cap,
-                                                         bad_index, loc);
-        }
-    }
+  // Slice expression.
 
   Bexpression* end;
   if (this->end_->is_nil_expression())
@@ -13032,24 +13148,6 @@ Array_index_expression::do_get_backend(Translate_context* context)
     {
       end = this->end_->get_backend(context);
       end = gogo->backend()->convert_expression(int_btype, end, loc);
-      if (this->needs_bounds_check_)
-        {
-          Bexpression* bounds_bcheck =
-            Expression::check_bounds(this->end_, loc)->get_backend(context);
-          bad_index =
-            gogo->backend()->binary_expression(OPERATOR_OROR, bounds_bcheck,
-                                               bad_index, loc);
-
-          Bexpression* end_too_small =
-            gogo->backend()->binary_expression(OPERATOR_LT, end, start, loc);
-          Bexpression* end_too_large =
-            gogo->backend()->binary_expression(OPERATOR_GT, end, cap_arg, loc);
-          Bexpression* bad_end =
-            gogo->backend()->binary_expression(OPERATOR_OROR, end_too_small,
-                                               end_too_large, loc);
-          bad_index = gogo->backend()->binary_expression(OPERATOR_OROR, bad_end,
-                                                         bad_index, loc);
-        }
     }
 
   Bexpression* result_length =
@@ -13081,12 +13179,7 @@ Array_index_expression::do_get_backend(Translate_context* context)
   init.push_back(result_length);
   init.push_back(result_capacity);
 
-  Bexpression* ret =
-    gogo->backend()->constructor_expression(struct_btype, init, loc);
-  if (this->needs_bounds_check_)
-    ret = gogo->backend()->conditional_expression(bfn, struct_btype, bad_index,
-                                                  crash, ret, loc);
-  return ret;
+  return gogo->backend()->constructor_expression(struct_btype, init, loc);
 }
 
 // Export an array index expression.
@@ -13164,7 +13257,15 @@ Expression*
 String_index_expression::do_flatten(Gogo*, Named_object*,
                                     Statement_inserter* inserter)
 {
+  if (this->is_flattened_)
+    return this;
+  this->is_flattened_ = true;
+
   Location loc = this->location();
+
+  if (this->is_error_expression())
+    return Expression::make_error(loc);
+
   Expression* string = this->string_;
   Expression* start = this->start_;
   Expression* end = this->end_;
@@ -13180,27 +13281,69 @@ String_index_expression::do_flatten(Gogo*, Named_object*,
     }
 
   Temporary_statement* temp;
-  if (!this->string_->is_variable())
+  if (!string->is_variable())
     {
-      temp = Statement::make_temporary(NULL, this->string_, loc);
+      temp = Statement::make_temporary(NULL, string, loc);
       inserter->insert(temp);
       this->string_ = Expression::make_temporary_reference(temp, loc);
+      string = this->string_;
     }
-  if (!this->start_->is_variable())
+  if (!start->is_variable())
     {
-      temp = Statement::make_temporary(NULL, this->start_, loc);
+      temp = Statement::make_temporary(NULL, start, loc);
       inserter->insert(temp);
       this->start_ = Expression::make_temporary_reference(temp, loc);
+      start = this->start_;
     }
-  if (this->end_ != NULL
-      && !this->end_->is_nil_expression()
-      && !this->end_->is_variable())
+  if (end != NULL
+      && !end->is_nil_expression()
+      && !end->is_variable())
     {
-      temp = Statement::make_temporary(NULL, this->end_, loc);
+      temp = Statement::make_temporary(NULL, end, loc);
       inserter->insert(temp);
       this->end_ = Expression::make_temporary_reference(temp, loc);
+      end = this->end_;
     }
 
+  Expression* len = Expression::make_string_info(string->copy(),
+                                                STRING_INFO_LENGTH, loc);
+  temp = Statement::make_temporary(NULL, len, loc);
+  inserter->insert(temp);
+  len = Expression::make_temporary_reference(temp, loc);
+
+  // The order of bounds checks here matches the order used by the gc
+  // compiler, as tested by issue30116[u].go.
+
+  if (end != NULL && !end->is_nil_expression())
+    {
+      Expression::check_bounds(end, OPERATOR_LE, len,
+                              Runtime::PANIC_SLICE_ALEN,
+                              Runtime::PANIC_SLICE_ALEN_U,
+                              Runtime::PANIC_EXTEND_SLICE_ALEN,
+                              Runtime::PANIC_EXTEND_SLICE_ALEN_U,
+                              inserter, loc);
+      Expression::check_bounds(start, OPERATOR_LE, end,
+                              Runtime::PANIC_SLICE_B,
+                              Runtime::PANIC_SLICE_B_U,
+                              Runtime::PANIC_EXTEND_SLICE_B,
+                              Runtime::PANIC_EXTEND_SLICE_B_U,
+                              inserter, loc);
+    }
+  else if (end != NULL)
+    Expression::check_bounds(start, OPERATOR_LE, len,
+                            Runtime::PANIC_SLICE_B,
+                            Runtime::PANIC_SLICE_B_U,
+                            Runtime::PANIC_EXTEND_SLICE_B,
+                            Runtime::PANIC_EXTEND_SLICE_B_U,
+                            inserter, loc);
+  else
+    Expression::check_bounds(start, OPERATOR_LT, len,
+                            Runtime::PANIC_INDEX,
+                            Runtime::PANIC_INDEX_U,
+                            Runtime::PANIC_EXTEND_INDEX,
+                            Runtime::PANIC_EXTEND_INDEX_U,
+                            inserter, loc);
+
   return this;
 }
 
@@ -13245,7 +13388,8 @@ String_index_expression::do_check_types(Gogo*)
   unsigned long v;
   if (this->start_->type()->integer_type() == NULL
       && !this->start_->type()->is_error()
-      && (!this->start_->numeric_constant_value(&nc)
+      && (!this->start_->type()->is_abstract()
+         || !this->start_->numeric_constant_value(&nc)
          || nc.to_unsigned_long(&v) == Numeric_constant::NC_UL_NOTINT))
     this->report_error(_("index must be integer"));
   if (this->end_ != NULL
@@ -13253,7 +13397,8 @@ String_index_expression::do_check_types(Gogo*)
       && !this->end_->type()->is_error()
       && !this->end_->is_nil_expression()
       && !this->end_->is_error_expression()
-      && (!this->end_->numeric_constant_value(&nc)
+      && (!this->end_->type()->is_abstract()
+         || !this->end_->numeric_constant_value(&nc)
          || nc.to_unsigned_long(&v) == Numeric_constant::NC_UL_NOTINT))
     this->report_error(_("slice end must be integer"));
 
@@ -13303,14 +13448,7 @@ Bexpression*
 String_index_expression::do_get_backend(Translate_context* context)
 {
   Location loc = this->location();
-  Expression* bad_index = Expression::check_bounds(this->start_, loc);
-
-  int code = (this->end_ == NULL
-             ? RUNTIME_ERROR_STRING_INDEX_OUT_OF_BOUNDS
-             : RUNTIME_ERROR_STRING_SLICE_OUT_OF_BOUNDS);
-
   Gogo* gogo = context->gogo();
-  Bexpression* crash = gogo->runtime_error(code, loc)->get_backend(context);
 
   Type* int_type = Type::lookup_integer_type("int");
 
@@ -13342,21 +13480,9 @@ String_index_expression::do_get_backend(Translate_context* context)
 
   if (this->end_ == NULL)
     {
-      Expression* start_too_large =
-          Expression::make_binary(OPERATOR_GE, start, length, loc);
-      bad_index = Expression::make_binary(OPERATOR_OROR, start_too_large,
-                                          bad_index, loc);
-
       ptr = gogo->backend()->pointer_offset_expression(ptr, bstart, loc);
       Btype* ubtype = Type::lookup_integer_type("uint8")->get_backend(gogo);
-      Bexpression* index =
-       gogo->backend()->indirect_expression(ubtype, ptr, true, loc);
-
-      Btype* byte_btype = bytes->type()->points_to()->get_backend(gogo);
-      Bexpression* index_error = bad_index->get_backend(context);
-      return gogo->backend()->conditional_expression(bfn, byte_btype,
-                                                     index_error, crash,
-                                                     index, loc);
+      return gogo->backend()->indirect_expression(ubtype, ptr, true, loc);
     }
 
   Expression* end = NULL;
@@ -13365,20 +13491,8 @@ String_index_expression::do_get_backend(Translate_context* context)
   else
     {
       go_assert(this->end_->is_variable());
-      Expression* bounds_check = Expression::check_bounds(this->end_, loc);
-      bad_index =
-          Expression::make_binary(OPERATOR_OROR, bounds_check, bad_index, loc);
       end = Expression::make_cast(int_type, this->end_, loc);
-
-      Expression* end_too_large =
-        Expression::make_binary(OPERATOR_GT, end, length, loc);
-      bad_index = Expression::make_binary(OPERATOR_OROR, end_too_large,
-                                          bad_index, loc);
     }
-  Expression* start_too_large =
-    Expression::make_binary(OPERATOR_GT, start->copy(), end->copy(), loc);
-  bad_index = Expression::make_binary(OPERATOR_OROR, start_too_large,
-                                      bad_index, loc);
 
   end = end->copy();
   Bexpression* bend = end->get_backend(context);
@@ -13405,12 +13519,7 @@ String_index_expression::do_get_backend(Translate_context* context)
   std::vector<Bexpression*> init;
   init.push_back(ptr);
   init.push_back(new_length);
-  Bexpression* bstrslice =
-    gogo->backend()->constructor_expression(str_btype, init, loc);
-
-  Bexpression* index_error = bad_index->get_backend(context);
-  return gogo->backend()->conditional_expression(bfn, str_btype, index_error,
-                                                crash, bstrslice, loc);
+  return gogo->backend()->constructor_expression(str_btype, init, loc);
 }
 
 // Export a string index expression.
index 3b65e7a5f0c96ed40ce898d3900640a40a8b93d4..4c743daa8472eaf645e9d30d457c7465bc983d25 100644 (file)
@@ -1059,10 +1059,11 @@ class Expression
   static Expression*
   import_expression(Import_expression*, Location);
 
-  // Return an expression which checks that VAL, of arbitrary integer type,
-  // is non-negative and is not more than the maximum integer value.
-  static Expression*
-  check_bounds(Expression* val, Location);
+  // Insert bounds checks for an index expression.
+  static void
+  check_bounds(Expression* val, Operator, Expression* bound, Runtime::Function,
+              Runtime::Function, Runtime::Function, Runtime::Function,
+              Statement_inserter*, Location);
 
   // Return an expression for constructing a direct interface type from a
   // pointer.
@@ -2998,7 +2999,7 @@ class Array_index_expression : public Expression
                         Expression* end, Expression* cap, Location location)
     : Expression(EXPRESSION_ARRAY_INDEX, location),
       array_(array), start_(start), end_(end), cap_(cap), type_(NULL),
-      is_lvalue_(false), needs_bounds_check_(true)
+      is_lvalue_(false), needs_bounds_check_(true), is_flattened_(false)
   { }
 
   // Return the array.
@@ -3121,6 +3122,8 @@ class Array_index_expression : public Expression
   bool is_lvalue_;
   // Whether bounds check is needed.
   bool needs_bounds_check_;
+  // Whether this has already been flattened.
+  bool is_flattened_;
 };
 
 // A string index.  This is used for both indexing and slicing.
@@ -3131,7 +3134,7 @@ class String_index_expression : public Expression
   String_index_expression(Expression* string, Expression* start,
                          Expression* end, Location location)
     : Expression(EXPRESSION_STRING_INDEX, location),
-      string_(string), start_(start), end_(end)
+      string_(string), start_(start), end_(end), is_flattened_(false)
   { }
 
   // Return the string being indexed.
@@ -3203,6 +3206,8 @@ class String_index_expression : public Expression
   // The end index of a slice.  This may be NULL for a single index,
   // or it may be a nil expression for the length of the string.
   Expression* end_;
+  // Whether this has already been flattened.
+  bool is_flattened_;
 };
 
 // An index into a map.
index 8a240708602cb83e203896cd41a090f83f5a9233..7aec0cf1be5292237ae4c068432bc46151559d03 100644 (file)
@@ -6300,6 +6300,7 @@ Function_declaration::get_or_make_decl(Gogo* gogo, Named_object* no)
            }
 
          if (this->asm_name_ == "runtime.gopanic"
+             || this->asm_name_.compare(0, 15, "runtime.goPanic") == 0
              || this->asm_name_ == "__go_runtime_error"
               || this->asm_name_ == "runtime.panicdottype"
               || this->asm_name_ == "runtime.block")
index e35658b8c5fc8ce553650f454f7a5da743b2f906..3cc5ded3617641ea0d8cb04c43e75e88678cb00f 100644 (file)
@@ -30,6 +30,8 @@ enum Runtime_function_type
   RFT_BOOLPTR,
   // Go type int, C type intgo.
   RFT_INT,
+  // Go type uint, C type uintgo.
+  RFT_UINT,
   // Go type uint8, C type uint8_t.
   RFT_UINT8,
   // Go type uint16, C type uint16_t.
@@ -113,6 +115,10 @@ runtime_function_type(Runtime_function_type bft)
          t = Type::lookup_integer_type("int");
          break;
 
+       case RFT_UINT:
+         t = Type::lookup_integer_type("uint");
+         break;
+
        case RFT_UINT8:
          t = Type::lookup_integer_type("uint8");
          break;
@@ -262,6 +268,7 @@ convert_to_runtime_function_type(Runtime_function_type bft, Expression* e,
     case RFT_BOOL:
     case RFT_BOOLPTR:
     case RFT_INT:
+    case RFT_UINT:
     case RFT_UINT8:
     case RFT_UINT16:
     case RFT_INT32:
index 7eac880af6a41c14fe3e7e248d821e206ffeb7c0..d7f5ee2140c386a9b1518006bce66b59e8d2ecd0 100644 (file)
@@ -499,6 +499,75 @@ DEF_GO_RUNTIME(ATOMIC_OR_FETCH_1, "__atomic_or_fetch_1",
                P3(POINTER, UINT8, INT32),
                R1(UINT8))
 
+// Panics reporting an index or slice out of bounds error.
+DEF_GO_RUNTIME(PANIC_INDEX, "runtime.goPanicIndex",
+              P2(INT, INT), R0())
+DEF_GO_RUNTIME(PANIC_INDEX_U, "runtime.goPanicIndexU",
+              P2(UINT, INT), R0())
+DEF_GO_RUNTIME(PANIC_SLICE_ALEN, "runtime.goPanicSliceAlen",
+              P2(INT, INT), R0())
+DEF_GO_RUNTIME(PANIC_SLICE_ALEN_U, "runtime.goPanicSliceAlenU",
+              P2(UINT, INT), R0())
+DEF_GO_RUNTIME(PANIC_SLICE_ACAP, "runtime.goPanicSliceAcap",
+              P2(INT, INT), R0())
+DEF_GO_RUNTIME(PANIC_SLICE_ACAP_U, "runtime.goPanicSliceAcapU",
+              P2(UINT, INT), R0())
+DEF_GO_RUNTIME(PANIC_SLICE_B, "runtime.goPanicSliceB",
+              P2(INT, INT), R0())
+DEF_GO_RUNTIME(PANIC_SLICE_B_U, "runtime.goPanicSliceBU",
+              P2(UINT, INT), R0())
+DEF_GO_RUNTIME(PANIC_SLICE3_ALEN, "runtime.goPanicSlice3Alen",
+              P2(INT, INT), R0())
+DEF_GO_RUNTIME(PANIC_SLICE3_ALEN_U, "runtime.goPanicSlice3AlenU",
+              P2(UINT, INT), R0())
+DEF_GO_RUNTIME(PANIC_SLICE3_ACAP, "runtime.goPanicSlice3Acap",
+              P2(INT, INT), R0())
+DEF_GO_RUNTIME(PANIC_SLICE3_ACAP_U, "runtime.goPanicSlice3AcapU",
+              P2(UINT, INT), R0())
+DEF_GO_RUNTIME(PANIC_SLICE3_B, "runtime.goPanicSlice3B",
+              P2(INT, INT), R0())
+DEF_GO_RUNTIME(PANIC_SLICE3_B_U, "runtime.goPanicSlice3BU",
+              P2(UINT, INT), R0())
+DEF_GO_RUNTIME(PANIC_SLICE3_C, "runtime.goPanicSlice3C",
+              P2(INT, INT), R0())
+DEF_GO_RUNTIME(PANIC_SLICE3_C_U, "runtime.goPanicSlice3CU",
+              P2(UINT, INT), R0())
+
+// Panics reporting an index or slice out of bounds error with a
+// 64-bit index type.  These are only used by 32-bit targets.
+DEF_GO_RUNTIME(PANIC_EXTEND_INDEX, "runtime.goPanicExtendIndex",
+              P2(INT64, INT), R0())
+DEF_GO_RUNTIME(PANIC_EXTEND_INDEX_U, "runtime.goPanicExtendIndexU",
+              P2(UINT64, INT), R0())
+DEF_GO_RUNTIME(PANIC_EXTEND_SLICE_ALEN, "runtime.goPanicExtendSliceAlen",
+              P2(INT64, INT), R0())
+DEF_GO_RUNTIME(PANIC_EXTEND_SLICE_ALEN_U, "runtime.goPanicExtendSliceAlenU",
+              P2(UINT64, INT), R0())
+DEF_GO_RUNTIME(PANIC_EXTEND_SLICE_ACAP, "runtime.goPanicExtendSliceAcap",
+              P2(INT64, INT), R0())
+DEF_GO_RUNTIME(PANIC_EXTEND_SLICE_ACAP_U, "runtime.goPanicExtendSliceAcapU",
+              P2(UINT64, INT), R0())
+DEF_GO_RUNTIME(PANIC_EXTEND_SLICE_B, "runtime.goPanicExtendSliceB",
+              P2(INT64, INT), R0())
+DEF_GO_RUNTIME(PANIC_EXTEND_SLICE_B_U, "runtime.goPanicExtendSliceBU",
+              P2(UINT64, INT), R0())
+DEF_GO_RUNTIME(PANIC_EXTEND_SLICE3_ALEN, "runtime.goPanicExtendSlice3Alen",
+              P2(INT64, INT), R0())
+DEF_GO_RUNTIME(PANIC_EXTEND_SLICE3_ALEN_U, "runtime.goPanicExtendSlice3AlenU",
+              P2(UINT64, INT), R0())
+DEF_GO_RUNTIME(PANIC_EXTEND_SLICE3_ACAP, "runtime.goPanicExtendSlice3Acap",
+              P2(INT64, INT), R0())
+DEF_GO_RUNTIME(PANIC_EXTEND_SLICE3_ACAP_U, "runtime.goPanicExtendSlice3AcapU",
+              P2(UINT64, INT), R0())
+DEF_GO_RUNTIME(PANIC_EXTEND_SLICE3_B, "runtime.goPanicExtendSlice3B",
+              P2(INT64, INT), R0())
+DEF_GO_RUNTIME(PANIC_EXTEND_SLICE3_B_U, "runtime.goPanicExtendSlice3BU",
+              P2(UINT64, INT), R0())
+DEF_GO_RUNTIME(PANIC_EXTEND_SLICE3_C, "runtime.goPanicExtendSlice3C",
+              P2(INT64, INT), R0())
+DEF_GO_RUNTIME(PANIC_EXTEND_SLICE3_C_U, "runtime.goPanicExtendSlice3CU",
+              P2(UINT64, INT), R0())
+
 // Remove helper macros.
 #undef ABFT6
 #undef ABFT2
index b1a3f68a06637ac32e2767d67232f9d224970e8a..0c7f631988457080bb42b17c263eca090736db21 100644 (file)
@@ -79,6 +79,21 @@ func unquote(s string) string {
        return string(r[:j])
 }
 
+//go:nosplit
+// itoa converts val to a decimal representation. The result is
+// written somewhere within buf and the location of the result is returned.
+// buf must be at least 20 bytes.
+func itoa(buf []byte, val uint64) []byte {
+       i := len(buf) - 1
+       for val >= 10 {
+               buf[i] = byte(val%10 + '0')
+               i--
+               val /= 10
+       }
+       buf[i] = byte(val + '0')
+       return buf[i:]
+}
+
 // An errorString represents a runtime error described by a single string.
 type errorString string
 
@@ -114,6 +129,99 @@ func (e plainError) Error() string {
        return string(e)
 }
 
+// An boundsError represents a an indexing or slicing operation gone wrong.
+type boundsError struct {
+       x int64
+       y int
+       // Values in an index or slice expression can be signed or unsigned.
+       // That means we'd need 65 bits to encode all possible indexes, from -2^63 to 2^64-1.
+       // Instead, we keep track of whether x should be interpreted as signed or unsigned.
+       // y is known to be nonnegative and to fit in an int.
+       signed bool
+       code   boundsErrorCode
+}
+
+type boundsErrorCode uint8
+
+const (
+       boundsIndex boundsErrorCode = iota // s[x], 0 <= x < len(s) failed
+
+       boundsSliceAlen // s[?:x], 0 <= x <= len(s) failed
+       boundsSliceAcap // s[?:x], 0 <= x <= cap(s) failed
+       boundsSliceB    // s[x:y], 0 <= x <= y failed (but boundsSliceA didn't happen)
+
+       boundsSlice3Alen // s[?:?:x], 0 <= x <= len(s) failed
+       boundsSlice3Acap // s[?:?:x], 0 <= x <= cap(s) failed
+       boundsSlice3B    // s[?:x:y], 0 <= x <= y failed (but boundsSlice3A didn't happen)
+       boundsSlice3C    // s[x:y:?], 0 <= x <= y failed (but boundsSlice3A/B didn't happen)
+
+       // Note: in the above, len(s) and cap(s) are stored in y
+)
+
+// boundsErrorFmts provide error text for various out-of-bounds panics.
+// Note: if you change these strings, you should adjust the size of the buffer
+// in boundsError.Error below as well.
+var boundsErrorFmts = [...]string{
+       boundsIndex:      "index out of range [%x] with length %y",
+       boundsSliceAlen:  "slice bounds out of range [:%x] with length %y",
+       boundsSliceAcap:  "slice bounds out of range [:%x] with capacity %y",
+       boundsSliceB:     "slice bounds out of range [%x:%y]",
+       boundsSlice3Alen: "slice bounds out of range [::%x] with length %y",
+       boundsSlice3Acap: "slice bounds out of range [::%x] with capacity %y",
+       boundsSlice3B:    "slice bounds out of range [:%x:%y]",
+       boundsSlice3C:    "slice bounds out of range [%x:%y:]",
+}
+
+// boundsNegErrorFmts are overriding formats if x is negative. In this case there's no need to report y.
+var boundsNegErrorFmts = [...]string{
+       boundsIndex:      "index out of range [%x]",
+       boundsSliceAlen:  "slice bounds out of range [:%x]",
+       boundsSliceAcap:  "slice bounds out of range [:%x]",
+       boundsSliceB:     "slice bounds out of range [%x:]",
+       boundsSlice3Alen: "slice bounds out of range [::%x]",
+       boundsSlice3Acap: "slice bounds out of range [::%x]",
+       boundsSlice3B:    "slice bounds out of range [:%x:]",
+       boundsSlice3C:    "slice bounds out of range [%x::]",
+}
+
+func (e boundsError) RuntimeError() {}
+
+func appendIntStr(b []byte, v int64, signed bool) []byte {
+       if signed && v < 0 {
+               b = append(b, '-')
+               v = -v
+       }
+       var buf [20]byte
+       b = append(b, itoa(buf[:], uint64(v))...)
+       return b
+}
+
+func (e boundsError) Error() string {
+       fmt := boundsErrorFmts[e.code]
+       if e.signed && e.x < 0 {
+               fmt = boundsNegErrorFmts[e.code]
+       }
+       // max message length is 99: "runtime error: slice bounds out of range [::%x] with capacity %y"
+       // x can be at most 20 characters. y can be at most 19.
+       b := make([]byte, 0, 100)
+       b = append(b, "runtime error: "...)
+       for i := 0; i < len(fmt); i++ {
+               c := fmt[i]
+               if c != '%' {
+                       b = append(b, c)
+                       continue
+               }
+               i++
+               switch fmt[i] {
+               case 'x':
+                       b = appendIntStr(b, e.x, e.signed)
+               case 'y':
+                       b = appendIntStr(b, int64(e.y), true)
+               }
+       }
+       return string(b)
+}
+
 type stringer interface {
        String() string
 }
index 88c0a4d8fd21303c89c2d00fde4c15f756a5fdcb..58684305ac0575b0213af068bd7187bd8e8b2c2e 100644 (file)
@@ -23,81 +23,189 @@ import (
 //go:linkname makefuncreturning runtime.makefuncreturning
 //go:linkname gorecover runtime.gorecover
 //go:linkname deferredrecover runtime.deferredrecover
+//go:linkname goPanicIndex runtime.goPanicIndex
+//go:linkname goPanicIndexU runtime.goPanicIndexU
+//go:linkname goPanicSliceAlen runtime.goPanicSliceAlen
+//go:linkname goPanicSliceAlenU runtime.goPanicSliceAlenU
+//go:linkname goPanicSliceAcap runtime.goPanicSliceAcap
+//go:linkname goPanicSliceAcapU runtime.goPanicSliceAcapU
+//go:linkname goPanicSliceB runtime.goPanicSliceB
+//go:linkname goPanicSliceBU runtime.goPanicSliceBU
+//go:linkname goPanicSlice3Alen runtime.goPanicSlice3Alen
+//go:linkname goPanicSlice3AlenU runtime.goPanicSlice3AlenU
+//go:linkname goPanicSlice3Acap runtime.goPanicSlice3Acap
+//go:linkname goPanicSlice3AcapU runtime.goPanicSlice3AcapU
+//go:linkname goPanicSlice3B runtime.goPanicSlice3B
+//go:linkname goPanicSlice3BU runtime.goPanicSlice3BU
+//go:linkname goPanicSlice3C runtime.goPanicSlice3C
+//go:linkname goPanicSlice3CU runtime.goPanicSlice3CU
 //go:linkname panicmem runtime.panicmem
 // Temporary for C code to call:
 //go:linkname throw runtime.throw
 
-// Calling panic with one of the errors below will call errorString.Error
-// which will call mallocgc to concatenate strings. That will fail if
-// malloc is locked, causing a confusing error message. Throw a better
-// error message instead.
-func panicCheckMalloc(err error) {
+// Check to make sure we can really generate a panic. If the panic
+// was generated from the runtime, or from inside malloc, then convert
+// to a throw of msg.
+// pc should be the program counter of the compiler-generated code that
+// triggered this panic.
+func panicCheck1(pc uintptr, msg string) {
+       name, _, _, _ := funcfileline(pc-1, -1)
+       if hasPrefix(name, "runtime.") {
+               throw(msg)
+       }
+       // TODO: is this redundant? How could we be in malloc
+       // but not in the runtime? runtime/internal/*, maybe?
        gp := getg()
        if gp != nil && gp.m != nil && gp.m.mallocing != 0 {
-               throw(string(err.(errorString)))
+               throw(msg)
        }
 }
 
-var indexError = error(errorString("index out of range"))
+// Same as above, but calling from the runtime is allowed.
+//
+// Using this function is necessary for any panic that may be
+// generated by runtime.sigpanic, since those are always called by the
+// runtime.
+func panicCheck2(err string) {
+       // panic allocates, so to avoid recursive malloc, turn panics
+       // during malloc into throws.
+       gp := getg()
+       if gp != nil && gp.m != nil && gp.m.mallocing != 0 {
+               throw(err)
+       }
+}
 
-// The panicindex, panicslice, and panicdivide functions are called by
+// Many of the following panic entry-points turn into throws when they
+// happen in various runtime contexts. These should never happen in
+// the runtime, and if they do, they indicate a serious issue and
+// should not be caught by user code.
+//
+// The panic{Index,Slice,divide,shift} functions are called by
 // code generated by the compiler for out of bounds index expressions,
-// out of bounds slice expressions, and division by zero. The
-// panicdivide (again), panicoverflow, panicfloat, and panicmem
+// out of bounds slice expressions, division by zero, and shift by negative.
+// The panicdivide (again), panicoverflow, panicfloat, and panicmem
 // functions are called by the signal handler when a signal occurs
 // indicating the respective problem.
 //
-// Since panicindex and panicslice are never called directly, and
+// Since panic{Index,Slice,shift} are never called directly, and
 // since the runtime package should never have an out of bounds slice
-// or array reference, if we see those functions called from the
+// or array reference or negative shift, if we see those functions called from the
 // runtime package we turn the panic into a throw. That will dump the
 // entire runtime stack for easier debugging.
+//
+// The entry points called by the signal handler will be called from
+// runtime.sigpanic, so we can't disallow calls from the runtime to
+// these (they always look like they're called from the runtime).
+// Hence, for these, we just check for clearly bad runtime conditions.
+
+// failures in the comparisons for s[x], 0 <= x < y (y == len(s))
+func goPanicIndex(x int, y int) {
+       panicCheck1(getcallerpc(), "index out of range")
+       panic(boundsError{x: int64(x), signed: true, y: y, code: boundsIndex})
+}
+func goPanicIndexU(x uint, y int) {
+       panicCheck1(getcallerpc(), "index out of range")
+       panic(boundsError{x: int64(x), signed: false, y: y, code: boundsIndex})
+}
 
-func panicindex() {
-       name, _, _, _ := funcfileline(getcallerpc()-1, -1)
-       if hasPrefix(name, "runtime.") {
-               throw(string(indexError.(errorString)))
-       }
-       panicCheckMalloc(indexError)
-       panic(indexError)
+// failures in the comparisons for s[:x], 0 <= x <= y (y == len(s) or cap(s))
+func goPanicSliceAlen(x int, y int) {
+       panicCheck1(getcallerpc(), "slice bounds out of range")
+       panic(boundsError{x: int64(x), signed: true, y: y, code: boundsSliceAlen})
+}
+func goPanicSliceAlenU(x uint, y int) {
+       panicCheck1(getcallerpc(), "slice bounds out of range")
+       panic(boundsError{x: int64(x), signed: false, y: y, code: boundsSliceAlen})
+}
+func goPanicSliceAcap(x int, y int) {
+       panicCheck1(getcallerpc(), "slice bounds out of range")
+       panic(boundsError{x: int64(x), signed: true, y: y, code: boundsSliceAcap})
+}
+func goPanicSliceAcapU(x uint, y int) {
+       panicCheck1(getcallerpc(), "slice bounds out of range")
+       panic(boundsError{x: int64(x), signed: false, y: y, code: boundsSliceAcap})
 }
 
-var sliceError = error(errorString("slice bounds out of range"))
+// failures in the comparisons for s[x:y], 0 <= x <= y
+func goPanicSliceB(x int, y int) {
+       panicCheck1(getcallerpc(), "slice bounds out of range")
+       panic(boundsError{x: int64(x), signed: true, y: y, code: boundsSliceB})
+}
+func goPanicSliceBU(x uint, y int) {
+       panicCheck1(getcallerpc(), "slice bounds out of range")
+       panic(boundsError{x: int64(x), signed: false, y: y, code: boundsSliceB})
+}
 
-func panicslice() {
-       name, _, _, _ := funcfileline(getcallerpc()-1, -1)
-       if hasPrefix(name, "runtime.") {
-               throw(string(sliceError.(errorString)))
-       }
-       panicCheckMalloc(sliceError)
-       panic(sliceError)
+// failures in the comparisons for s[::x], 0 <= x <= y (y == len(s) or cap(s))
+func goPanicSlice3Alen(x int, y int) {
+       panicCheck1(getcallerpc(), "slice bounds out of range")
+       panic(boundsError{x: int64(x), signed: true, y: y, code: boundsSlice3Alen})
+}
+func goPanicSlice3AlenU(x uint, y int) {
+       panicCheck1(getcallerpc(), "slice bounds out of range")
+       panic(boundsError{x: int64(x), signed: false, y: y, code: boundsSlice3Alen})
+}
+func goPanicSlice3Acap(x int, y int) {
+       panicCheck1(getcallerpc(), "slice bounds out of range")
+       panic(boundsError{x: int64(x), signed: true, y: y, code: boundsSlice3Acap})
+}
+func goPanicSlice3AcapU(x uint, y int) {
+       panicCheck1(getcallerpc(), "slice bounds out of range")
+       panic(boundsError{x: int64(x), signed: false, y: y, code: boundsSlice3Acap})
+}
+
+// failures in the comparisons for s[:x:y], 0 <= x <= y
+func goPanicSlice3B(x int, y int) {
+       panicCheck1(getcallerpc(), "slice bounds out of range")
+       panic(boundsError{x: int64(x), signed: true, y: y, code: boundsSlice3B})
+}
+func goPanicSlice3BU(x uint, y int) {
+       panicCheck1(getcallerpc(), "slice bounds out of range")
+       panic(boundsError{x: int64(x), signed: false, y: y, code: boundsSlice3B})
+}
+
+// failures in the comparisons for s[x:y:], 0 <= x <= y
+func goPanicSlice3C(x int, y int) {
+       panicCheck1(getcallerpc(), "slice bounds out of range")
+       panic(boundsError{x: int64(x), signed: true, y: y, code: boundsSlice3C})
+}
+func goPanicSlice3CU(x uint, y int) {
+       panicCheck1(getcallerpc(), "slice bounds out of range")
+       panic(boundsError{x: int64(x), signed: false, y: y, code: boundsSlice3C})
+}
+
+var shiftError = error(errorString("negative shift amount"))
+
+func panicshift() {
+       panicCheck1(getcallerpc(), "negative shift amount")
+       panic(shiftError)
 }
 
 var divideError = error(errorString("integer divide by zero"))
 
 func panicdivide() {
-       panicCheckMalloc(divideError)
+       panicCheck2("integer divide by zero")
        panic(divideError)
 }
 
 var overflowError = error(errorString("integer overflow"))
 
 func panicoverflow() {
-       panicCheckMalloc(overflowError)
+       panicCheck2("integer overflow")
        panic(overflowError)
 }
 
 var floatError = error(errorString("floating point error"))
 
 func panicfloat() {
-       panicCheckMalloc(floatError)
+       panicCheck2("floating point error")
        panic(floatError)
 }
 
 var memoryError = error(errorString("invalid memory address or nil pointer dereference"))
 
 func panicmem() {
-       panicCheckMalloc(memoryError)
+       panicCheck2("invalid memory address or nil pointer dereference")
        panic(memoryError)
 }
 
diff --git a/libgo/go/runtime/panic32.go b/libgo/go/runtime/panic32.go
new file mode 100644 (file)
index 0000000..f68d4c7
--- /dev/null
@@ -0,0 +1,108 @@
+// Copyright 2019 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build 386 amd64p32 arm mips mipsle m68k nios2 sh shbe
+
+package runtime
+
+import _ "unsafe" // for go:linkname
+
+// For gccgo, use go:linkname to rename compiler-called functions to
+// themselves, so that the compiler will export them.
+//
+//go:linkname goPanicExtendIndex runtime.goPanicExtendIndex
+//go:linkname goPanicExtendIndexU runtime.goPanicExtendIndexU
+//go:linkname goPanicExtendSliceAlen runtime.goPanicExtendSliceAlen
+//go:linkname goPanicExtendSliceAlenU runtime.goPanicExtendSliceAlenU
+//go:linkname goPanicExtendSliceAcap runtime.goPanicExtendSliceAcap
+//go:linkname goPanicExtendSliceAcapU runtime.goPanicExtendSliceAcapU
+//go:linkname goPanicExtendSliceB runtime.goPanicExtendSliceB
+//go:linkname goPanicExtendSliceBU runtime.goPanicExtendSliceBU
+//go:linkname goPanicExtendSlice3Alen runtime.goPanicExtendSlice3Alen
+//go:linkname goPanicExtendSlice3AlenU runtime.goPanicExtendSlice3AlenU
+//go:linkname goPanicExtendSlice3Acap runtime.goPanicExtendSlice3Acap
+//go:linkname goPanicExtendSlice3AcapU runtime.goPanicExtendSlice3AcapU
+//go:linkname goPanicExtendSlice3B runtime.goPanicExtendSlice3B
+//go:linkname goPanicExtendSlice3BU runtime.goPanicExtendSlice3BU
+//go:linkname goPanicExtendSlice3C runtime.goPanicExtendSlice3C
+//go:linkname goPanicExtendSlice3CU runtime.goPanicExtendSlice3CU
+
+// Additional index/slice error paths for 32-bit platforms.
+// Used when the high word of a 64-bit index is not zero.
+
+// failures in the comparisons for s[x], 0 <= x < y (y == len(s))
+func goPanicExtendIndex(x int64, y int) {
+       panicCheck1(getcallerpc(), "index out of range")
+       panic(boundsError{x: x, signed: true, y: y, code: boundsIndex})
+}
+func goPanicExtendIndexU(x uint64, y int) {
+       panicCheck1(getcallerpc(), "index out of range")
+       panic(boundsError{x: int64(x), signed: false, y: y, code: boundsIndex})
+}
+
+// failures in the comparisons for s[:x], 0 <= x <= y (y == len(s) or cap(s))
+func goPanicExtendSliceAlen(x int64, y int) {
+       panicCheck1(getcallerpc(), "slice bounds out of range")
+       panic(boundsError{x: x, signed: true, y: y, code: boundsSliceAlen})
+}
+func goPanicExtendSliceAlenU(x uint64, y int) {
+       panicCheck1(getcallerpc(), "slice bounds out of range")
+       panic(boundsError{x: int64(x), signed: false, y: y, code: boundsSliceAlen})
+}
+func goPanicExtendSliceAcap(x int64, y int) {
+       panicCheck1(getcallerpc(), "slice bounds out of range")
+       panic(boundsError{x: x, signed: true, y: y, code: boundsSliceAcap})
+}
+func goPanicExtendSliceAcapU(x uint64, y int) {
+       panicCheck1(getcallerpc(), "slice bounds out of range")
+       panic(boundsError{x: int64(x), signed: false, y: y, code: boundsSliceAcap})
+}
+
+// failures in the comparisons for s[x:y], 0 <= x <= y
+func goPanicExtendSliceB(x int64, y int) {
+       panicCheck1(getcallerpc(), "slice bounds out of range")
+       panic(boundsError{x: x, signed: true, y: y, code: boundsSliceB})
+}
+func goPanicExtendSliceBU(x uint64, y int) {
+       panicCheck1(getcallerpc(), "slice bounds out of range")
+       panic(boundsError{x: int64(x), signed: false, y: y, code: boundsSliceB})
+}
+
+// failures in the comparisons for s[::x], 0 <= x <= y (y == len(s) or cap(s))
+func goPanicExtendSlice3Alen(x int64, y int) {
+       panicCheck1(getcallerpc(), "slice bounds out of range")
+       panic(boundsError{x: x, signed: true, y: y, code: boundsSlice3Alen})
+}
+func goPanicExtendSlice3AlenU(x uint64, y int) {
+       panicCheck1(getcallerpc(), "slice bounds out of range")
+       panic(boundsError{x: int64(x), signed: false, y: y, code: boundsSlice3Alen})
+}
+func goPanicExtendSlice3Acap(x int64, y int) {
+       panicCheck1(getcallerpc(), "slice bounds out of range")
+       panic(boundsError{x: x, signed: true, y: y, code: boundsSlice3Acap})
+}
+func goPanicExtendSlice3AcapU(x uint64, y int) {
+       panicCheck1(getcallerpc(), "slice bounds out of range")
+       panic(boundsError{x: int64(x), signed: false, y: y, code: boundsSlice3Acap})
+}
+
+// failures in the comparisons for s[:x:y], 0 <= x <= y
+func goPanicExtendSlice3B(x int64, y int) {
+       panicCheck1(getcallerpc(), "slice bounds out of range")
+       panic(boundsError{x: x, signed: true, y: y, code: boundsSlice3B})
+}
+func goPanicExtendSlice3BU(x uint64, y int) {
+       panicCheck1(getcallerpc(), "slice bounds out of range")
+       panic(boundsError{x: int64(x), signed: false, y: y, code: boundsSlice3B})
+}
+
+// failures in the comparisons for s[x:y:], 0 <= x <= y
+func goPanicExtendSlice3C(x int64, y int) {
+       panicCheck1(getcallerpc(), "slice bounds out of range")
+       panic(boundsError{x: x, signed: true, y: y, code: boundsSlice3C})
+}
+func goPanicExtendSlice3CU(x uint64, y int) {
+       panicCheck1(getcallerpc(), "slice bounds out of range")
+       panic(boundsError{x: int64(x), signed: false, y: y, code: boundsSlice3C})
+}
index cd95595c731fec8fddc85cbd6ff6dfa63a126aa1..d29da9b6846b886b670162f6d6aabccad4984ede 100755 (executable)
@@ -15,14 +15,15 @@ rm -f runtime.inc.tmp2 runtime.inc.tmp3
 # types and should not be exported back to C
 # semt is a Go translation of the C type sem_t; it fails to convert on
 # some systems and need not be exported back to C.
-# sigset conflicts with system type sigset on AIX, so we need to rename it
+# sigset conflicts with system type sigset on AIX, so we need to rename it.
+# boundsError has a field name that is a C keyword, and we don't need it.
 
 grep -v "#define _" ${IN} | grep -v "#define [cm][01234] " | grep -v "#define empty " | grep -v "#define \\$" > runtime.inc.tmp2
 for pattern in '_[GP][a-z]' _Max _Lock _Sig _Trace _MHeap _Num
 do
   grep "#define $pattern" ${IN} >> runtime.inc.tmp2
 done
-TYPES="_Complex_lock _Reader_lock semt"
+TYPES="_Complex_lock _Reader_lock semt boundsError"
 for TYPE in $TYPES
 do
   sed -e '/struct '${TYPE}' {/,/^}/s/^.*$//' runtime.inc.tmp2 > runtime.inc.tmp3;