Skip to content

Commit ad667e7

Browse files
compiler, runtime: support unsafe.Add and unsafe.Slice
For golang/go#19367 For golang/go#40481 Change-Id: I989d042a518a38fd24c0d14f9c78ae564e5799a2 Reviewed-on: https://go-review.googlesource.com/c/gofrontend/+/338949 Trust: Ian Lance Taylor <[email protected]> Reviewed-by: Cherry Mui <[email protected]>
1 parent 920549b commit ad667e7

File tree

5 files changed

+281
-1
lines changed

5 files changed

+281
-1
lines changed

go/expressions.cc

+227
Original file line numberDiff line numberDiff line change
@@ -8252,12 +8252,16 @@ Builtin_call_expression::Builtin_call_expression(Gogo* gogo,
82528252
this->code_ = BUILTIN_REAL;
82538253
else if (name == "recover")
82548254
this->code_ = BUILTIN_RECOVER;
8255+
else if (name == "Add")
8256+
this->code_ = BUILTIN_ADD;
82558257
else if (name == "Alignof")
82568258
this->code_ = BUILTIN_ALIGNOF;
82578259
else if (name == "Offsetof")
82588260
this->code_ = BUILTIN_OFFSETOF;
82598261
else if (name == "Sizeof")
82608262
this->code_ = BUILTIN_SIZEOF;
8263+
else if (name == "Slice")
8264+
this->code_ = BUILTIN_SLICE;
82618265
else
82628266
go_unreachable();
82638267
}
@@ -8694,6 +8698,119 @@ Builtin_call_expression::do_flatten(Gogo* gogo, Named_object* function,
86948698

86958699
return Runtime::make_call(code, loc, 3, e1, e2, e3);
86968700
}
8701+
8702+
case BUILTIN_ADD:
8703+
{
8704+
Expression* ptr = this->args()->front();
8705+
Type* uintptr_type = Type::lookup_integer_type("uintptr");
8706+
ptr = Expression::make_cast(uintptr_type, ptr, loc);
8707+
Expression* len = this->args()->back();
8708+
len = Expression::make_cast(uintptr_type, len, loc);
8709+
Expression* add = Expression::make_binary(OPERATOR_PLUS, ptr, len,
8710+
loc);
8711+
return Expression::make_cast(this->args()->front()->type(), add, loc);
8712+
}
8713+
8714+
case BUILTIN_SLICE:
8715+
{
8716+
Expression* ptr = this->args()->front();
8717+
Temporary_statement* ptr_temp = NULL;
8718+
if (!ptr->is_multi_eval_safe())
8719+
{
8720+
ptr_temp = Statement::make_temporary(NULL, ptr, loc);
8721+
inserter->insert(ptr_temp);
8722+
ptr = Expression::make_temporary_reference(ptr_temp, loc);
8723+
}
8724+
8725+
Expression* len = this->args()->back();
8726+
Temporary_statement* len_temp = NULL;
8727+
if (!len->is_multi_eval_safe())
8728+
{
8729+
len_temp = Statement::make_temporary(NULL, len, loc);
8730+
inserter->insert(len_temp);
8731+
len = Expression::make_temporary_reference(len_temp, loc);
8732+
}
8733+
8734+
bool fits_in_int;
8735+
Numeric_constant nc;
8736+
if (this->args()->back()->numeric_constant_value(&nc))
8737+
{
8738+
// We gave an error for constants that don't fit in int in
8739+
// check_types.
8740+
fits_in_int = true;
8741+
}
8742+
else
8743+
{
8744+
Integer_type* itype = this->args()->back()->type()->integer_type();
8745+
go_assert(itype != NULL);
8746+
int ebits = itype->bits();
8747+
int intbits =
8748+
Type::lookup_integer_type("int")->integer_type()->bits();
8749+
8750+
// We can treat ebits == intbits as small even for an
8751+
// unsigned integer type, because we will convert the
8752+
// value to int and then reject it in the runtime if it is
8753+
// negative.
8754+
8755+
fits_in_int = ebits <= intbits;
8756+
}
8757+
8758+
Runtime::Function code = (fits_in_int
8759+
? Runtime::UNSAFESLICE
8760+
: Runtime::UNSAFESLICE64);
8761+
Expression* td =
8762+
Expression::make_type_descriptor(ptr->type()->points_to(), loc);
8763+
Expression* check = Runtime::make_call(code, loc, 3,
8764+
td, ptr, len);
8765+
8766+
if (ptr_temp == NULL)
8767+
ptr = ptr->copy();
8768+
else
8769+
ptr = Expression::make_temporary_reference(ptr_temp, loc);
8770+
Expression* nil = Expression::make_nil(loc);
8771+
nil = Expression::make_cast(ptr->type(), nil, loc);
8772+
Expression* is_nil = Expression::make_binary(OPERATOR_EQEQ, ptr, nil,
8773+
loc);
8774+
8775+
if (len_temp == NULL)
8776+
len = len->copy();
8777+
else
8778+
len = Expression::make_temporary_reference(len_temp, loc);
8779+
Expression* zero = Expression::make_integer_ul(0, len->type(), loc);
8780+
Expression* is_zero = Expression::make_binary(OPERATOR_EQEQ, len, zero,
8781+
loc);
8782+
8783+
Expression* cond = Expression::make_binary(OPERATOR_ANDAND, is_nil,
8784+
is_zero, loc);
8785+
8786+
Type* slice_type = Type::make_array_type(ptr->type()->points_to(),
8787+
NULL);
8788+
nil = Expression::make_nil(loc);
8789+
Expression* nil_slice = Expression::make_cast(slice_type, nil, loc);
8790+
8791+
if (ptr_temp == NULL)
8792+
ptr = ptr->copy();
8793+
else
8794+
ptr = Expression::make_temporary_reference(ptr_temp, loc);
8795+
8796+
if (len_temp == NULL)
8797+
len = len->copy();
8798+
else
8799+
len = Expression::make_temporary_reference(len_temp, loc);
8800+
8801+
Expression* cap;
8802+
if (len_temp == NULL)
8803+
cap = len->copy();
8804+
else
8805+
cap = Expression::make_temporary_reference(len_temp, loc);
8806+
8807+
Expression* slice = Expression::make_slice_value(slice_type, ptr,
8808+
len, cap, loc);
8809+
8810+
slice = Expression::make_conditional(cond, nil_slice, slice, loc);
8811+
8812+
return Expression::make_compound(check, slice, loc);
8813+
}
86978814
}
86988815

86998816
return this;
@@ -9781,9 +9898,11 @@ Builtin_call_expression::do_discarding_value()
97819898
case BUILTIN_MAKE:
97829899
case BUILTIN_NEW:
97839900
case BUILTIN_REAL:
9901+
case BUILTIN_ADD:
97849902
case BUILTIN_ALIGNOF:
97859903
case BUILTIN_OFFSETOF:
97869904
case BUILTIN_SIZEOF:
9905+
case BUILTIN_SLICE:
97879906
this->unused_value_error();
97889907
return false;
97899908

@@ -9890,6 +10009,18 @@ Builtin_call_expression::do_type()
989010009
t = Type::make_error_type();
989110010
return t;
989210011
}
10012+
10013+
case BUILTIN_ADD:
10014+
return Type::make_pointer_type(Type::make_void_type());
10015+
10016+
case BUILTIN_SLICE:
10017+
const Expression_list* args = this->args();
10018+
if (args == NULL || args->size() != 2)
10019+
return Type::make_error_type();
10020+
Type* pt = args->front()->type()->points_to();
10021+
if (pt == NULL)
10022+
return Type::make_error_type();
10023+
return Type::make_array_type(pt, NULL);
989310024
}
989410025
}
989510026

@@ -9954,6 +10085,28 @@ Builtin_call_expression::do_determine_type(const Type_context* context)
995410085
is_print = false;
995510086
break;
995610087

10088+
case BUILTIN_ADD:
10089+
case BUILTIN_SLICE:
10090+
// Both unsafe.Add and unsafe.Slice take two arguments, and the
10091+
// second arguments defaults to "int".
10092+
if (args != NULL && args->size() == 2)
10093+
{
10094+
if (this->code_ == BUILTIN_SLICE)
10095+
args->front()->determine_type_no_context();
10096+
else
10097+
{
10098+
Type* pointer = Type::make_pointer_type(Type::make_void_type());
10099+
Type_context subcontext(pointer, false);
10100+
args->front()->determine_type(&subcontext);
10101+
}
10102+
Type* int_type = Type::lookup_integer_type("int");
10103+
Type_context subcontext(int_type, false);
10104+
args->back()->determine_type(&subcontext);
10105+
return;
10106+
}
10107+
is_print = false;
10108+
break;
10109+
995710110
default:
995810111
is_print = false;
995910112
break;
@@ -10353,6 +10506,78 @@ Builtin_call_expression::do_check_types(Gogo*)
1035310506
}
1035410507
break;
1035510508

10509+
case BUILTIN_ADD:
10510+
case BUILTIN_SLICE:
10511+
{
10512+
Numeric_constant nc;
10513+
unsigned long v;
10514+
const Expression_list* args = this->args();
10515+
if (args == NULL || args->size() < 2)
10516+
this->report_error(_("not enough arguments"));
10517+
else if (args->size() > 2)
10518+
this->report_error(_("too many arguments"));
10519+
else if (args->front()->is_error_expression()
10520+
|| args->front()->type()->is_error()
10521+
|| args->back()->is_error_expression()
10522+
|| args->back()->type()->is_error())
10523+
this->set_is_error();
10524+
else if (args->back()->type()->integer_type() == NULL
10525+
&& (!args->back()->type()->is_abstract()
10526+
|| !args->back()->numeric_constant_value(&nc)
10527+
|| (nc.to_unsigned_long(&v)
10528+
== Numeric_constant::NC_UL_NOTINT)))
10529+
{
10530+
if (this->code_ == BUILTIN_ADD)
10531+
go_error_at(args->back()->location(), "non-integer offset");
10532+
else
10533+
go_error_at(args->back()->location(), "non-integer size");
10534+
}
10535+
else if (this->code_ == BUILTIN_ADD)
10536+
{
10537+
Type* pointer_type =
10538+
Type::make_pointer_type(Type::make_void_type());
10539+
std::string reason;
10540+
if (!Type::are_assignable(pointer_type, args->front()->type(),
10541+
&reason))
10542+
{
10543+
if (reason.empty())
10544+
go_error_at(args->front()->location(),
10545+
"argument 1 has incompatible type");
10546+
else
10547+
go_error_at(args->front()->location(),
10548+
"argument 1 has incompatible type (%s)",
10549+
reason.c_str());
10550+
this->set_is_error();
10551+
}
10552+
}
10553+
else
10554+
{
10555+
if (args->front()->type()->points_to() == NULL)
10556+
{
10557+
go_error_at(args->front()->location(),
10558+
"argument 1 must be a pointer");
10559+
this->set_is_error();
10560+
}
10561+
10562+
unsigned int int_bits =
10563+
Type::lookup_integer_type("int")->integer_type()->bits();
10564+
10565+
mpz_t ival;
10566+
if (args->back()->numeric_constant_value(&nc) && nc.to_int(&ival))
10567+
{
10568+
if (mpz_sgn(ival) < 0
10569+
|| mpz_sizeinbase(ival, 2) >= int_bits)
10570+
{
10571+
go_error_at(args->back()->location(),
10572+
"slice length out of range");
10573+
this->set_is_error();
10574+
}
10575+
mpz_clear(ival);
10576+
}
10577+
}
10578+
}
10579+
break;
10580+
1035610581
default:
1035710582
go_unreachable();
1035810583
}
@@ -10397,6 +10622,8 @@ Builtin_call_expression::do_get_backend(Translate_context* context)
1039710622
case BUILTIN_INVALID:
1039810623
case BUILTIN_NEW:
1039910624
case BUILTIN_MAKE:
10625+
case BUILTIN_ADD:
10626+
case BUILTIN_SLICE:
1040010627
go_unreachable();
1040110628

1040210629
case BUILTIN_LEN:

go/expressions.h

+3-1
Original file line numberDiff line numberDiff line change
@@ -2608,9 +2608,11 @@ class Builtin_call_expression : public Call_expression
26082608
BUILTIN_RECOVER,
26092609

26102610
// Builtin functions from the unsafe package.
2611+
BUILTIN_ADD,
26112612
BUILTIN_ALIGNOF,
26122613
BUILTIN_OFFSETOF,
2613-
BUILTIN_SIZEOF
2614+
BUILTIN_SIZEOF,
2615+
BUILTIN_SLICE
26142616
};
26152617

26162618
Builtin_function_code

go/runtime.def

+6
Original file line numberDiff line numberDiff line change
@@ -489,6 +489,12 @@ DEF_GO_RUNTIME(ATOMIC_OR_FETCH_1, "__atomic_or_fetch_1",
489489
P3(POINTER, UINT8, INT32),
490490
R1(UINT8))
491491

492+
// Check the length of an unsafe slice.
493+
DEF_GO_RUNTIME(UNSAFESLICE, "runtime.unsafeslice",
494+
P3(TYPE, POINTER, INT), R0())
495+
DEF_GO_RUNTIME(UNSAFESLICE64, "runtime.unsafeslice64",
496+
P3(TYPE, POINTER, INT64), R0())
497+
492498
// Panic reporting a division by zero.
493499
DEF_GO_RUNTIME(PANIC_DIVIDE, "runtime.panicdivide", P0(), R0())
494500

go/unsafe.cc

+16
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,22 @@ Gogo::import_unsafe(const std::string& local_name, bool is_local_name_exported,
8686
if (add_to_globals)
8787
this->add_dot_import_object(no);
8888

89+
// Add.
90+
results = new Typed_identifier_list;
91+
results->push_back(Typed_identifier("", pointer_type, bloc));
92+
fntype = Type::make_function_type(NULL, NULL, results, bloc);
93+
fntype->set_is_builtin();
94+
no = bindings->add_function_declaration("Add", package, fntype, bloc);
95+
if (add_to_globals)
96+
this->add_dot_import_object(no);
97+
98+
// Slice.
99+
fntype = Type::make_function_type(NULL, NULL, NULL, bloc);
100+
fntype->set_is_builtin();
101+
no = bindings->add_function_declaration("Slice", package, fntype, bloc);
102+
if (add_to_globals)
103+
this->add_dot_import_object(no);
104+
89105
if (!this->imported_unsafe_)
90106
{
91107
go_imported_unsafe();

libgo/go/runtime/slice.go

+29
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ import (
1818
//go:linkname checkMakeSlice
1919
//go:linkname makeslice64
2020
//go:linkname growslice
21+
//go:linkname unsafeslice
22+
//go:linkname unsafeslice64
2123

2224
type slice struct {
2325
array unsafe.Pointer
@@ -127,6 +129,33 @@ func makeslice64(et *_type, len64, cap64 int64) unsafe.Pointer {
127129
return makeslice(et, len, cap)
128130
}
129131

132+
func unsafeslice(et *_type, ptr unsafe.Pointer, len int) {
133+
if len == 0 {
134+
return
135+
}
136+
137+
if ptr == nil {
138+
panic(errorString("unsafe.Slice: ptr is nil and len is not zero"))
139+
}
140+
141+
mem, overflow := math.MulUintptr(et.size, uintptr(len))
142+
if overflow || mem > maxAlloc || len < 0 {
143+
panicunsafeslicelen()
144+
}
145+
}
146+
147+
func unsafeslice64(et *_type, ptr unsafe.Pointer, len64 int64) {
148+
len := int(len64)
149+
if int64(len) != len64 {
150+
panicunsafeslicelen()
151+
}
152+
unsafeslice(et, ptr, len)
153+
}
154+
155+
func panicunsafeslicelen() {
156+
panic(errorString("unsafe.Slice: len out of range"))
157+
}
158+
130159
// growslice handles slice growth during append.
131160
// It is passed the slice element type, the old slice, and the desired new minimum capacity,
132161
// and it returns a new slice with at least that capacity, with the old data

0 commit comments

Comments
 (0)