The upcoming Go 1.17 release adds two new functions to the unsafe
package: unsafe.Add and unsafe.Slice.  These functions must be
implemented in the compiler.  This patch implements them for gccgo.
Bootstrapped and ran Go testsuite on x86_64-pc-linux-gnu.  Committed
to mainline.

Ian
06d0437d4a5faca2b695918cbe1d54a61935c98b
diff --git a/gcc/go/gofrontend/MERGE b/gcc/go/gofrontend/MERGE
index 5323e186eab..19fbd647840 100644
--- a/gcc/go/gofrontend/MERGE
+++ b/gcc/go/gofrontend/MERGE
@@ -1,4 +1,4 @@
-920549b6382a2623538d31001271941f0e9e5a51
+ad667e7c70cea9fa5730660d72ad891b5753eb62
 
 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/expressions.cc b/gcc/go/gofrontend/expressions.cc
index a0472acc209..46e71e5a13b 100644
--- a/gcc/go/gofrontend/expressions.cc
+++ b/gcc/go/gofrontend/expressions.cc
@@ -8252,12 +8252,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();
 }
@@ -8694,6 +8698,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;
@@ -9781,9 +9898,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;
 
@@ -9890,6 +10009,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);
     }
 }
 
@@ -9954,6 +10085,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;
@@ -10353,6 +10506,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();
     }
@@ -10397,6 +10622,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:
diff --git a/gcc/go/gofrontend/expressions.h b/gcc/go/gofrontend/expressions.h
index 57c974d3c70..492849bb33e 100644
--- a/gcc/go/gofrontend/expressions.h
+++ b/gcc/go/gofrontend/expressions.h
@@ -2608,9 +2608,11 @@ 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
diff --git a/gcc/go/gofrontend/runtime.def b/gcc/go/gofrontend/runtime.def
index ec01be0afe4..231d92d2661 100644
--- a/gcc/go/gofrontend/runtime.def
+++ b/gcc/go/gofrontend/runtime.def
@@ -489,6 +489,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())
 
diff --git a/gcc/go/gofrontend/unsafe.cc b/gcc/go/gofrontend/unsafe.cc
index 9ed5b9d0d3c..18bd99eca15 100644
--- a/gcc/go/gofrontend/unsafe.cc
+++ b/gcc/go/gofrontend/unsafe.cc
@@ -86,6 +86,22 @@ Gogo::import_unsafe(const std::string& local_name, bool 
is_local_name_exported,
   if (add_to_globals)
     this->add_dot_import_object(no);
 
+  // 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();
+  no = bindings->add_function_declaration("Add", package, fntype, bloc);
+  if (add_to_globals)
+    this->add_dot_import_object(no);
+
+  // Slice.
+  fntype = Type::make_function_type(NULL, NULL, NULL, bloc);
+  fntype->set_is_builtin();
+  no = bindings->add_function_declaration("Slice", package, fntype, bloc);
+  if (add_to_globals)
+    this->add_dot_import_object(no);
+
   if (!this->imported_unsafe_)
     {
       go_imported_unsafe();
diff --git a/gcc/testsuite/go.test/test/unsafebuiltins.go 
b/gcc/testsuite/go.test/test/unsafebuiltins.go
new file mode 100644
index 00000000000..4c940aa8559
--- /dev/null
+++ b/gcc/testsuite/go.test/test/unsafebuiltins.go
@@ -0,0 +1,64 @@
+// run
+
+// Copyright 2021 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.
+
+package main
+
+import (
+       "math"
+       "unsafe"
+)
+
+const maxUintptr = 1 << (8 * unsafe.Sizeof(uintptr(0)))
+
+func main() {
+       var p [10]byte
+
+       // unsafe.Add
+       {
+               p1 := unsafe.Pointer(&p[1])
+               assert(unsafe.Add(p1, 1) == unsafe.Pointer(&p[2]))
+               assert(unsafe.Add(p1, -1) == unsafe.Pointer(&p[0]))
+       }
+
+       // unsafe.Slice
+       {
+               s := unsafe.Slice(&p[0], len(p))
+               assert(&s[0] == &p[0])
+               assert(len(s) == len(p))
+               assert(cap(s) == len(p))
+
+               // nil pointer with zero length returns nil
+               assert(unsafe.Slice((*int)(nil), 0) == nil)
+
+               // nil pointer with positive length panics
+               mustPanic(func() { _ = unsafe.Slice((*int)(nil), 1) })
+
+               // negative length
+               var neg int = -1
+               mustPanic(func() { _ = unsafe.Slice(new(byte), neg) })
+
+               // length too large
+               var tooBig uint64 = math.MaxUint64
+               mustPanic(func() { _ = unsafe.Slice(new(byte), tooBig) })
+
+               // size overflows address space
+               mustPanic(func() { _ = unsafe.Slice(new(uint64), maxUintptr/8) 
})
+               mustPanic(func() { _ = unsafe.Slice(new(uint64), 
maxUintptr/8+1) })
+       }
+}
+
+func assert(ok bool) {
+       if !ok {
+               panic("FAIL")
+       }
+}
+
+func mustPanic(f func()) {
+       defer func() {
+               assert(recover() != nil)
+       }()
+       f()
+}
diff --git a/libgo/go/runtime/slice.go b/libgo/go/runtime/slice.go
index 27a97773169..d4c0e9028b7 100644
--- a/libgo/go/runtime/slice.go
+++ b/libgo/go/runtime/slice.go
@@ -18,6 +18,8 @@ import (
 //go:linkname checkMakeSlice
 //go:linkname makeslice64
 //go:linkname growslice
+//go:linkname unsafeslice
+//go:linkname unsafeslice64
 
 type slice struct {
        array unsafe.Pointer
@@ -127,6 +129,33 @@ func makeslice64(et *_type, len64, cap64 int64) 
unsafe.Pointer {
        return makeslice(et, len, cap)
 }
 
+func unsafeslice(et *_type, ptr unsafe.Pointer, len int) {
+       if len == 0 {
+               return
+       }
+
+       if ptr == nil {
+               panic(errorString("unsafe.Slice: ptr is nil and len is not 
zero"))
+       }
+
+       mem, overflow := math.MulUintptr(et.size, uintptr(len))
+       if overflow || mem > maxAlloc || len < 0 {
+               panicunsafeslicelen()
+       }
+}
+
+func unsafeslice64(et *_type, ptr unsafe.Pointer, len64 int64) {
+       len := int(len64)
+       if int64(len) != len64 {
+               panicunsafeslicelen()
+       }
+       unsafeslice(et, ptr, len)
+}
+
+func panicunsafeslicelen() {
+       panic(errorString("unsafe.Slice: len out of range"))
+}
+
 // growslice handles slice growth during append.
 // It is passed the slice element type, the old slice, and the desired new 
minimum capacity,
 // and it returns a new slice with at least that capacity, with the old data

Reply via email to