From cbbd439a33e889a1a47b103951c53472fc8558eb Mon Sep 17 00:00:00 2001 From: Ian Lance Taylor Date: Mon, 2 Aug 2021 16:27:02 -0700 Subject: compiler: check slice to pointer-to-array conversion element type When checking a slice to pointer-to-array conversion, I forgot to verify that the elements types are identical. For golang/go#395 Reviewed-on: https://go-review.googlesource.com/c/gofrontend/+/339329 --- gcc/go/gofrontend/MERGE | 2 +- gcc/go/gofrontend/expressions.cc | 5 ++++- gcc/go/gofrontend/types.cc | 4 +++- 3 files changed, 8 insertions(+), 3 deletions(-) (limited to 'gcc/go') diff --git a/gcc/go/gofrontend/MERGE b/gcc/go/gofrontend/MERGE index 95b9340..801e039 100644 --- a/gcc/go/gofrontend/MERGE +++ b/gcc/go/gofrontend/MERGE @@ -1,4 +1,4 @@ -0a4d612e6b211780b294717503fc739bbd1f509c +54361805bd611d896042b879ee7f6d2d4d088537 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 15c9eab..51a8b7e 100644 --- a/gcc/go/gofrontend/expressions.cc +++ b/gcc/go/gofrontend/expressions.cc @@ -3962,7 +3962,10 @@ Type_conversion_expression::do_lower(Gogo*, Named_object*, if (type->points_to() != NULL && type->points_to()->array_type() != NULL && !type->points_to()->is_slice_type() - && val->type()->is_slice_type()) + && val->type()->is_slice_type() + && Type::are_identical(type->points_to()->array_type()->element_type(), + val->type()->array_type()->element_type(), + 0, NULL)) { Temporary_statement* val_temp = NULL; if (!val->is_multi_eval_safe()) diff --git a/gcc/go/gofrontend/types.cc b/gcc/go/gofrontend/types.cc index 7c7b2eb..0c44186 100644 --- a/gcc/go/gofrontend/types.cc +++ b/gcc/go/gofrontend/types.cc @@ -846,7 +846,9 @@ Type::are_convertible(const Type* lhs, const Type* rhs, std::string* reason) if (rhs->is_slice_type() && lhs->points_to() != NULL && lhs->points_to()->array_type() != NULL - && !lhs->points_to()->is_slice_type()) + && !lhs->points_to()->is_slice_type() + && Type::are_identical(lhs->points_to()->array_type()->element_type(), + rhs->array_type()->element_type(), 0, reason)) return true; // An unsafe.Pointer type may be converted to any pointer type or to -- cgit v1.1 From e435e72ad713cadd661072427588ec1c777c04e3 Mon Sep 17 00:00:00 2001 From: Ian Lance Taylor Date: Tue, 3 Aug 2021 11:36:24 -0700 Subject: compile, runtime: make selectnbrecv return two values The only different between selectnbrecv and selectnbrecv2 is the later set the input pointer value by second return value from chanrecv. So by making selectnbrecv return two values from chanrecv, we can get rid of selectnbrecv2, the compiler can now call only selectnbrecv and generate simpler code. This is the gofrontend version of https://golang.org/cl/292890. Reviewed-on: https://go-review.googlesource.com/c/gofrontend/+/339529 --- gcc/go/gofrontend/MERGE | 2 +- gcc/go/gofrontend/runtime.def | 8 ++---- gcc/go/gofrontend/statements.cc | 59 ++++++++++++++++++++++------------------- 3 files changed, 34 insertions(+), 35 deletions(-) (limited to 'gcc/go') diff --git a/gcc/go/gofrontend/MERGE b/gcc/go/gofrontend/MERGE index 801e039..5a097ff 100644 --- a/gcc/go/gofrontend/MERGE +++ b/gcc/go/gofrontend/MERGE @@ -1,4 +1,4 @@ -54361805bd611d896042b879ee7f6d2d4d088537 +2031f0be9c0b5fda6421d290a0261eb6bd1c8205 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/runtime.def b/gcc/go/gofrontend/runtime.def index fad8ceb..87a2708 100644 --- a/gcc/go/gofrontend/runtime.def +++ b/gcc/go/gofrontend/runtime.def @@ -204,12 +204,8 @@ DEF_GO_RUNTIME(SELECTNBSEND, "runtime.selectnbsend", P2(CHAN, POINTER), R1(BOOL) // Non-blocking receive a value from a channel, used for two-case select // statement with a default case. -DEF_GO_RUNTIME(SELECTNBRECV, "runtime.selectnbrecv", P2(POINTER, CHAN), R1(BOOL)) - -// Non-blocking tuple receive from a channel, used for two-case select -// statement with a default case. -DEF_GO_RUNTIME(SELECTNBRECV2, "runtime.selectnbrecv2", P3(POINTER, POINTER, CHAN), - R1(BOOL)) +DEF_GO_RUNTIME(SELECTNBRECV, "runtime.selectnbrecv", P2(POINTER, CHAN), + R2(BOOL, BOOL)) // Block execution. Used for zero-case select. DEF_GO_RUNTIME(BLOCK, "runtime.block", P0(), R0()) diff --git a/gcc/go/gofrontend/statements.cc b/gcc/go/gofrontend/statements.cc index 9643d1b..95fa3c4 100644 --- a/gcc/go/gofrontend/statements.cc +++ b/gcc/go/gofrontend/statements.cc @@ -6051,7 +6051,7 @@ Select_statement::lower_two_case(Block* b) Expression* chanref = Expression::make_temporary_reference(chantmp, loc); Block* bchan; - Expression* call; + Expression* cond; if (chancase.is_send()) { // if selectnbsend(chan, &val) { body } else { default body } @@ -6065,7 +6065,7 @@ Select_statement::lower_two_case(Block* b) Expression* ref = Expression::make_temporary_reference(ts, loc); Expression* addr = Expression::make_unary(OPERATOR_AND, ref, loc); - call = Runtime::make_call(Runtime::SELECTNBSEND, loc, 2, chanref, addr); + cond = Runtime::make_call(Runtime::SELECTNBSEND, loc, 2, chanref, addr); bchan = chancase.statements(); } else @@ -6075,34 +6075,31 @@ Select_statement::lower_two_case(Block* b) Expression* ref = Expression::make_temporary_reference(ts, loc); Expression* addr = Expression::make_unary(OPERATOR_AND, ref, loc); - Expression* okref = NULL; - if (chancase.closed() == NULL && chancase.closedvar() == NULL) - { - // Simple receive. - // if selectnbrecv(&lhs, chan) { body } else { default body } - call = Runtime::make_call(Runtime::SELECTNBRECV, loc, 2, addr, chanref); - } - else - { - // Tuple receive. - // if selectnbrecv2(&lhs, &ok, chan) { body } else { default body } - - Type* booltype = Type::make_boolean_type(); - Temporary_statement* okts = Statement::make_temporary(booltype, NULL, - loc); - b->add_statement(okts); - - okref = Expression::make_temporary_reference(okts, loc); - Expression* okaddr = Expression::make_unary(OPERATOR_AND, okref, loc); - call = Runtime::make_call(Runtime::SELECTNBRECV2, loc, 3, addr, okaddr, - chanref); - } + + // selected, ok = selectnbrecv(&lhs, chan) + Call_expression* call = Runtime::make_call(Runtime::SELECTNBRECV, loc, 2, + addr, chanref); + + Temporary_statement* selected_temp = + Statement::make_temporary(Type::make_boolean_type(), + Expression::make_call_result(call, 0), + loc); + b->add_statement(selected_temp); + + Temporary_statement* ok_temp = + Statement::make_temporary(Type::make_boolean_type(), + Expression::make_call_result(call, 1), + loc); + b->add_statement(ok_temp); + + cond = Expression::make_temporary_reference(selected_temp, loc); Location cloc = chancase.location(); bchan = new Block(b, loc); if (chancase.val() != NULL && !chancase.val()->is_sink_expression()) { - Statement* as = Statement::make_assignment(chancase.val(), ref->copy(), + Statement* as = Statement::make_assignment(chancase.val(), + ref->copy(), cloc); bchan->add_statement(as); } @@ -6114,12 +6111,18 @@ Select_statement::lower_two_case(Block* b) if (chancase.closed() != NULL && !chancase.closed()->is_sink_expression()) { + Expression* okref = Expression::make_temporary_reference(ok_temp, + cloc); Statement* as = Statement::make_assignment(chancase.closed(), - okref->copy(), cloc); + okref, cloc); bchan->add_statement(as); } else if (chancase.closedvar() != NULL) - chancase.closedvar()->var_value()->set_init(okref->copy()); + { + Expression* okref = Expression::make_temporary_reference(ok_temp, + cloc); + chancase.closedvar()->var_value()->set_init(okref); + } Statement* bs = Statement::make_block_statement(chancase.statements(), cloc); @@ -6127,7 +6130,7 @@ Select_statement::lower_two_case(Block* b) } Statement* ifs = - Statement::make_if_statement(call, bchan, defcase.statements(), loc); + Statement::make_if_statement(cond, bchan, defcase.statements(), loc); b->add_statement(ifs); Statement* label = -- cgit v1.1 From 22e40cc7feb8abda85762e4f07719836d5c57f1a Mon Sep 17 00:00:00 2001 From: Cherry Mui Date: Tue, 3 Aug 2021 19:35:55 -0400 Subject: compiler: support new language constructs in escape analysis Previous CLs add new language constructs in Go 1.17, specifically, unsafe.Add, unsafe.Slice, and conversion from a slice to a pointer to an array. This CL handles them in the escape analysis. At the point of the escape analysis, unsafe.Add and unsafe.Slice are still builtin calls, so just handle them in data flow. Conversion from a slice to a pointer to an array has already been lowered to a combination of compound expression, conditional expression and slice info expressions, so handle them in the escape analysis. Reviewed-on: https://go-review.googlesource.com/c/gofrontend/+/339671 --- gcc/go/gofrontend/MERGE | 2 +- gcc/go/gofrontend/escape.cc | 63 +++++++++++++++++++++++++++++---- gcc/go/gofrontend/expressions.cc | 44 +---------------------- gcc/go/gofrontend/expressions.h | 75 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 134 insertions(+), 50 deletions(-) (limited to 'gcc/go') diff --git a/gcc/go/gofrontend/MERGE b/gcc/go/gofrontend/MERGE index 5a097ff..be1a90f 100644 --- a/gcc/go/gofrontend/MERGE +++ b/gcc/go/gofrontend/MERGE @@ -1,4 +1,4 @@ -2031f0be9c0b5fda6421d290a0261eb6bd1c8205 +616ee658a6238e7de53592ebda5997f6de6a00de 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/escape.cc b/gcc/go/gofrontend/escape.cc index cf68874..347ac25 100644 --- a/gcc/go/gofrontend/escape.cc +++ b/gcc/go/gofrontend/escape.cc @@ -2325,19 +2325,55 @@ Escape_analysis_assign::assign(Node* dst, Node* src) } break; + case Expression::EXPRESSION_SLICE_INFO: + { + Slice_info_expression* sie = e->slice_info_expression(); + if (sie->info() == Expression::SLICE_INFO_VALUE_POINTER) + { + Node* slice = Node::make_node(sie->slice()); + this->assign(dst, slice); + } + } + break; + case Expression::EXPRESSION_CALL: { Call_expression* call = e->call_expression(); if (call->is_builtin()) { Builtin_call_expression* bce = call->builtin_call_expression(); - if (bce->code() == Builtin_call_expression::BUILTIN_APPEND) + switch (bce->code()) { - // Append returns the first argument. - // The subsequent arguments are already leaked because - // they are operands to append. - Node* appendee = Node::make_node(call->args()->front()); - this->assign(dst, appendee); + case Builtin_call_expression::BUILTIN_APPEND: + { + // Append returns the first argument. + // The subsequent arguments are already leaked because + // they are operands to append. + Node* appendee = Node::make_node(call->args()->front()); + this->assign(dst, appendee); + } + break; + + case Builtin_call_expression::BUILTIN_ADD: + { + // unsafe.Add(p, off). + // Flow p to result. + Node* arg = Node::make_node(call->args()->front()); + this->assign(dst, arg); + } + break; + + case Builtin_call_expression::BUILTIN_SLICE: + { + // unsafe.Slice(p, len). + // The resulting slice has the same backing store as p. Flow p to result. + Node* arg = Node::make_node(call->args()->front()); + this->assign(dst, arg); + } + break; + + default: + break; } break; } @@ -2592,6 +2628,21 @@ Escape_analysis_assign::assign(Node* dst, Node* src) } break; + case Expression::EXPRESSION_CONDITIONAL: + { + Conditional_expression* ce = e->conditional_expression(); + this->assign(dst, Node::make_node(ce->then_expr())); + this->assign(dst, Node::make_node(ce->else_expr())); + } + break; + + case Expression::EXPRESSION_COMPOUND: + { + Compound_expression* ce = e->compound_expression(); + this->assign(dst, Node::make_node(ce->expr())); + } + break; + default: // TODO(cmang): Add debug info here; this should not be reachable. // For now, just to be conservative, we'll just say dst flows to src. diff --git a/gcc/go/gofrontend/expressions.cc b/gcc/go/gofrontend/expressions.cc index 51a8b7e..3e433d6 100644 --- a/gcc/go/gofrontend/expressions.cc +++ b/gcc/go/gofrontend/expressions.cc @@ -18009,49 +18009,7 @@ Expression::make_type_info(Type* type, Type_info type_info) return new Type_info_expression(type, type_info); } -// An expression that evaluates to some characteristic of a slice. -// This is used when indexing, bound-checking, or nil checking a slice. - -class Slice_info_expression : public Expression -{ - public: - Slice_info_expression(Expression* slice, Slice_info slice_info, - Location location) - : Expression(EXPRESSION_SLICE_INFO, location), - slice_(slice), slice_info_(slice_info) - { } - - protected: - Type* - do_type(); - - void - do_determine_type(const Type_context*) - { } - - Expression* - do_copy() - { - return new Slice_info_expression(this->slice_->copy(), this->slice_info_, - this->location()); - } - - Bexpression* - do_get_backend(Translate_context* context); - - void - do_dump_expression(Ast_dump_context*) const; - - void - do_issue_nil_check() - { this->slice_->issue_nil_check(); } - - private: - // The slice for which we are getting information. - Expression* slice_; - // What information we want. - Slice_info slice_info_; -}; +// Slice_info_expression. // Return the type of the slice info. diff --git a/gcc/go/gofrontend/expressions.h b/gcc/go/gofrontend/expressions.h index 492849b..79a8785 100644 --- a/gcc/go/gofrontend/expressions.h +++ b/gcc/go/gofrontend/expressions.h @@ -62,6 +62,7 @@ class Type_guard_expression; class Heap_expression; class Receive_expression; class Slice_value_expression; +class Slice_info_expression; class Conditional_expression; class Compound_expression; class Numeric_constant; @@ -900,6 +901,14 @@ class Expression compound_expression() { return this->convert(); } + // If this is a slice info expression, return the + // Slice_info_expression structure. Otherwise, return NULL. + Slice_info_expression* + slice_info_expression() + { + return this->convert(); + } + // Return true if this is a composite literal. bool is_composite_literal() const; @@ -4262,6 +4271,60 @@ class Slice_value_expression : public Expression Expression* cap_; }; +// An expression that evaluates to some characteristic of a slice. +// This is used when indexing, bound-checking, or nil checking a slice. + +class Slice_info_expression : public Expression +{ + public: + Slice_info_expression(Expression* slice, Slice_info slice_info, + Location location) + : Expression(EXPRESSION_SLICE_INFO, location), + slice_(slice), slice_info_(slice_info) + { } + + // The slice operand of this slice info expression. + Expression* + slice() const + { return this->slice_; } + + // The info this expression is about. + Slice_info + info() const + { return this->slice_info_; } + + protected: + Type* + do_type(); + + void + do_determine_type(const Type_context*) + { } + + Expression* + do_copy() + { + return new Slice_info_expression(this->slice_->copy(), this->slice_info_, + this->location()); + } + + Bexpression* + do_get_backend(Translate_context* context); + + void + do_dump_expression(Ast_dump_context*) const; + + void + do_issue_nil_check() + { this->slice_->issue_nil_check(); } + + private: + // The slice for which we are getting information. + Expression* slice_; + // What information we want. + Slice_info slice_info_; +}; + // Conditional expressions. class Conditional_expression : public Expression @@ -4277,6 +4340,14 @@ class Conditional_expression : public Expression condition() const { return this->cond_; } + Expression* + then_expr() const + { return this->then_; } + + Expression* + else_expr() const + { return this->else_; } + protected: int do_traverse(Traverse*); @@ -4322,6 +4393,10 @@ class Compound_expression : public Expression init() const { return this->init_; } + Expression* + expr() const + { return this->expr_; } + protected: int do_traverse(Traverse*); -- cgit v1.1 From ac8a2fbedf59eecda6d1c049952e10946ffc4a61 Mon Sep 17 00:00:00 2001 From: Cherry Mui Date: Wed, 4 Aug 2021 18:24:47 -0400 Subject: compiler: make escape analysis more robust about builtin functions In the places where we handle builtin functions, list all supported ones, and fail if an unexpected one is seen. So if a new builtin function is added in the future we can detect it, instead of silently treating it as nonescaping. Reviewed-on: https://go-review.googlesource.com/c/gofrontend/+/339992 --- gcc/go/gofrontend/MERGE | 2 +- gcc/go/gofrontend/escape.cc | 56 +++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 55 insertions(+), 3 deletions(-) (limited to 'gcc/go') diff --git a/gcc/go/gofrontend/MERGE b/gcc/go/gofrontend/MERGE index be1a90f..394530c 100644 --- a/gcc/go/gofrontend/MERGE +++ b/gcc/go/gofrontend/MERGE @@ -1,4 +1,4 @@ -616ee658a6238e7de53592ebda5997f6de6a00de +b47bcf942daa9a0c252db9b57b8f138adbfcdaa2 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/escape.cc b/gcc/go/gofrontend/escape.cc index 347ac25..c8978ac 100644 --- a/gcc/go/gofrontend/escape.cc +++ b/gcc/go/gofrontend/escape.cc @@ -1608,8 +1608,33 @@ Escape_analysis_assign::expression(Expression** pexpr) } break; - default: + case Builtin_call_expression::BUILTIN_CLOSE: + case Builtin_call_expression::BUILTIN_DELETE: + case Builtin_call_expression::BUILTIN_PRINT: + case Builtin_call_expression::BUILTIN_PRINTLN: + case Builtin_call_expression::BUILTIN_LEN: + case Builtin_call_expression::BUILTIN_CAP: + case Builtin_call_expression::BUILTIN_COMPLEX: + case Builtin_call_expression::BUILTIN_REAL: + case Builtin_call_expression::BUILTIN_IMAG: + case Builtin_call_expression::BUILTIN_RECOVER: + case Builtin_call_expression::BUILTIN_ALIGNOF: + case Builtin_call_expression::BUILTIN_OFFSETOF: + case Builtin_call_expression::BUILTIN_SIZEOF: + // these do not escape. + break; + + case Builtin_call_expression::BUILTIN_ADD: + case Builtin_call_expression::BUILTIN_SLICE: + // handled in ::assign. break; + + case Builtin_call_expression::BUILTIN_MAKE: + case Builtin_call_expression::BUILTIN_NEW: + // should have been lowered to runtime calls at this point. + // fallthrough + default: + go_unreachable(); } break; } @@ -2372,8 +2397,35 @@ Escape_analysis_assign::assign(Node* dst, Node* src) } break; - default: + case Builtin_call_expression::BUILTIN_LEN: + case Builtin_call_expression::BUILTIN_CAP: + case Builtin_call_expression::BUILTIN_COMPLEX: + case Builtin_call_expression::BUILTIN_REAL: + case Builtin_call_expression::BUILTIN_IMAG: + case Builtin_call_expression::BUILTIN_RECOVER: + case Builtin_call_expression::BUILTIN_ALIGNOF: + case Builtin_call_expression::BUILTIN_OFFSETOF: + case Builtin_call_expression::BUILTIN_SIZEOF: + // these do not escape. + break; + + case Builtin_call_expression::BUILTIN_COPY: + // handled in ::expression. break; + + case Builtin_call_expression::BUILTIN_CLOSE: + case Builtin_call_expression::BUILTIN_DELETE: + case Builtin_call_expression::BUILTIN_PRINT: + case Builtin_call_expression::BUILTIN_PRINTLN: + case Builtin_call_expression::BUILTIN_PANIC: + // these do not have result. + // fallthrough + case Builtin_call_expression::BUILTIN_MAKE: + case Builtin_call_expression::BUILTIN_NEW: + // should have been lowered to runtime calls at this point. + // fallthrough + default: + go_unreachable(); } break; } -- cgit v1.1