compiler, runtime: support unsafe.Add and unsafe.Slice
For golang/go#19367 For golang/go#40481 Reviewed-on: https://go-review.googlesource.com/c/gofrontend/+/338949
This commit is contained in:
parent
14d8a5ae47
commit
06d0437d4a
@ -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.
|
||||
|
@ -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:
|
||||
|
@ -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
|
||||
|
@ -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())
|
||||
|
||||
|
@ -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();
|
||||
|
64
gcc/testsuite/go.test/test/unsafebuiltins.go
Normal file
64
gcc/testsuite/go.test/test/unsafebuiltins.go
Normal file
@ -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()
|
||||
}
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user