compiler: Use backend interface for interface info and field expressions.

* go-gcc.cc (Gcc_backend::compound_expression): New function.
	(Gcc_backend::conditional_expression): New function.

From-SVN: r206615
This commit is contained in:
Chris Manghane 2014-01-15 00:17:22 +00:00 committed by Ian Lance Taylor
parent 3f3bd8aa27
commit eb6eb86237
5 changed files with 305 additions and 141 deletions

View File

@ -1,3 +1,8 @@
2014-01-14 Chris Manghane <cmang@google.com>
* go-gcc.cc (Gcc_backend::compound_expression): New function.
(Gcc_backend::conditional_expression): New function.
2014-01-02 Richard Sandiford <rdsandiford@googlemail.com>
Update copyright years

View File

@ -246,6 +246,12 @@ class Gcc_backend : public Backend
Bexpression*
struct_field_expression(Bexpression*, size_t, Location);
Bexpression*
compound_expression(Bstatement*, Bexpression*, Location);
Bexpression*
conditional_expression(Bexpression*, Bexpression*, Bexpression*, Location);
// Statements.
Bstatement*
@ -1034,6 +1040,41 @@ Gcc_backend::struct_field_expression(Bexpression* bstruct, size_t index,
return tree_to_expr(ret);
}
// Return an expression that executes BSTAT before BEXPR.
Bexpression*
Gcc_backend::compound_expression(Bstatement* bstat, Bexpression* bexpr,
Location location)
{
tree stat = bstat->get_tree();
tree expr = bexpr->get_tree();
if (stat == error_mark_node || expr == error_mark_node)
return this->error_expression();
tree ret = fold_build2_loc(location.gcc_location(), COMPOUND_EXPR,
TREE_TYPE(expr), stat, expr);
return this->make_expression(ret);
}
// Return an expression that executes THEN_EXPR if CONDITION is true, or
// ELSE_EXPR otherwise.
Bexpression*
Gcc_backend::conditional_expression(Bexpression* condition,
Bexpression* then_expr,
Bexpression* else_expr, Location location)
{
tree cond_tree = condition->get_tree();
tree then_tree = then_expr->get_tree();
tree else_tree = else_expr == NULL ? NULL_TREE : else_expr->get_tree();
if (cond_tree == error_mark_node
|| then_tree == error_mark_node
|| else_tree == error_mark_node)
return this->error_expression();
tree ret = build3_loc(location.gcc_location(), COND_EXPR, void_type_node,
cond_tree, then_tree, else_tree);
return this->make_expression(ret);
}
// An expression as a statement.
Bstatement*

View File

@ -284,6 +284,16 @@ class Backend
virtual Bexpression*
struct_field_expression(Bexpression* bstruct, size_t index, Location) = 0;
// Create an expression that executes BSTAT before BEXPR.
virtual Bexpression*
compound_expression(Bstatement* bstat, Bexpression* bexpr, Location) = 0;
// Return an expression that executes THEN_EXPR if CONDITION is true, or
// ELSE_EXPR otherwise. ELSE_EXPR may be NULL.
virtual Bexpression*
conditional_expression(Bexpression* condition, Bexpression* then_expr,
Bexpression* else_expr, Location) = 0;
// Statements.
// Create an error statement. This is used for cases which should

View File

@ -6473,11 +6473,11 @@ Expression::make_binary(Operator op, Expression* left, Expression* right,
tree
Expression::comparison_tree(Translate_context* context, Type* result_type,
Operator op, Expression* left_expr,
Expression* right_expr, Location location)
Operator op, Expression* left, Expression* right,
Location location)
{
Type* left_type = left_expr->type();
Type* right_type = right_expr->type();
Type* left_type = left->type();
Type* right_type = right->type();
mpz_t zval;
mpz_init_set_ui(zval, 0UL);
@ -6509,17 +6509,11 @@ Expression::comparison_tree(Translate_context* context, Type* result_type,
go_unreachable();
}
// FIXME: Computing the tree here means it will be computed multiple times,
// which is wasteful. This is a temporary modification until all tree code
// here can be replaced with frontend expressions.
tree left_tree = left_expr->get_tree(context);
tree right_tree = right_expr->get_tree(context);
if (left_type->is_string_type() && right_type->is_string_type())
{
Expression* strcmp_call = Runtime::make_call(Runtime::STRCMP, location, 2,
left_expr, right_expr);
left_tree = strcmp_call->get_tree(context);
right_tree = zexpr->get_tree(context);
left = Runtime::make_call(Runtime::STRCMP, location, 2,
left, right);
right = zexpr;
}
else if ((left_type->interface_type() != NULL
&& right_type->interface_type() == NULL
@ -6532,31 +6526,30 @@ Expression::comparison_tree(Translate_context* context, Type* result_type,
if (left_type->interface_type() == NULL)
{
std::swap(left_type, right_type);
std::swap(left_expr, right_expr);
std::swap(left, right);
}
// The right operand is not an interface. We need to take its
// address if it is not a pointer.
Expression* pointer_arg = NULL;
if (right_type->points_to() != NULL)
pointer_arg = right_expr;
pointer_arg = right;
else
{
go_assert(right_expr->is_addressable());
pointer_arg = Expression::make_unary(OPERATOR_AND, right_expr,
go_assert(right->is_addressable());
pointer_arg = Expression::make_unary(OPERATOR_AND, right,
location);
}
Expression* descriptor_expr = Expression::make_type_descriptor(right_type,
location);
Call_expression* iface_valcmp =
Expression* descriptor =
Expression::make_type_descriptor(right_type, location);
left =
Runtime::make_call((left_type->interface_type()->is_empty()
? Runtime::EMPTY_INTERFACE_VALUE_COMPARE
: Runtime::INTERFACE_VALUE_COMPARE),
location, 3, left_expr, descriptor_expr,
location, 3, left, descriptor,
pointer_arg);
left_tree = iface_valcmp->get_tree(context);
right_tree = zexpr->get_tree(context);
right = zexpr;
}
else if (left_type->interface_type() != NULL
&& right_type->interface_type() != NULL)
@ -6574,56 +6567,42 @@ Expression::comparison_tree(Translate_context* context, Type* result_type,
{
go_assert(op == OPERATOR_EQEQ || op == OPERATOR_NOTEQ);
std::swap(left_type, right_type);
std::swap(left_expr, right_expr);
std::swap(left, right);
}
go_assert(!left_type->interface_type()->is_empty());
go_assert(right_type->interface_type()->is_empty());
compare_function = Runtime::INTERFACE_EMPTY_COMPARE;
}
Call_expression* ifacecmp_call =
Runtime::make_call(compare_function, location, 2,
left_expr, right_expr);
left_tree = ifacecmp_call->get_tree(context);
right_tree = zexpr->get_tree(context);
left = Runtime::make_call(compare_function, location, 2, left, right);
right = zexpr;
}
if (left_type->is_nil_type()
&& (op == OPERATOR_EQEQ || op == OPERATOR_NOTEQ))
{
std::swap(left_type, right_type);
std::swap(left_tree, right_tree);
std::swap(left_expr, right_expr);
std::swap(left, right);
}
if (right_type->is_nil_type())
{
right = Expression::make_nil(location);
if (left_type->array_type() != NULL
&& left_type->array_type()->length() == NULL)
{
Array_type* at = left_type->array_type();
left_expr = at->get_value_pointer(context->gogo(), left_expr);
left_tree = left_expr->get_tree(context);
right_tree = fold_convert(TREE_TYPE(left_tree), null_pointer_node);
left = at->get_value_pointer(context->gogo(), left);
}
else if (left_type->interface_type() != NULL)
{
// An interface is nil if the first field is nil.
tree left_type_tree = TREE_TYPE(left_tree);
go_assert(TREE_CODE(left_type_tree) == RECORD_TYPE);
tree field = TYPE_FIELDS(left_type_tree);
left_tree = build3(COMPONENT_REF, TREE_TYPE(field), left_tree,
field, NULL_TREE);
right_tree = fold_convert(TREE_TYPE(left_tree), null_pointer_node);
}
else
{
go_assert(POINTER_TYPE_P(TREE_TYPE(left_tree)));
right_tree = fold_convert(TREE_TYPE(left_tree), null_pointer_node);
left = Expression::make_field_reference(left, 0, location);
}
}
tree left_tree = left->get_tree(context);
tree right_tree = right->get_tree(context);
if (left_tree == error_mark_node || right_tree == error_mark_node)
return error_mark_node;
@ -9745,21 +9724,13 @@ Call_expression::do_must_eval_in_order() const
// Get the function and the first argument to use when calling an
// interface method.
tree
Expression*
Call_expression::interface_method_function(
Translate_context* context,
Interface_field_reference_expression* interface_method,
tree* first_arg_ptr)
Expression** first_arg_ptr)
{
tree expr = interface_method->expr()->get_tree(context);
if (expr == error_mark_node)
return error_mark_node;
expr = save_expr(expr);
tree first_arg = interface_method->get_underlying_object_tree(context, expr);
if (first_arg == error_mark_node)
return error_mark_node;
*first_arg_ptr = first_arg;
return interface_method->get_function_tree(context, expr);
*first_arg_ptr = interface_method->get_underlying_object();
return interface_method->get_function();
}
// Build the call expression.
@ -9889,8 +9860,12 @@ Call_expression::do_get_tree(Translate_context* context)
}
else
{
fn = this->interface_method_function(context, interface_method,
&args[0]);
Expression* first_arg;
Expression* fn_expr =
this->interface_method_function(interface_method, &first_arg);
args[0] = first_arg->get_tree(context);
fn = fn_expr->get_tree(context);
if (fn == error_mark_node)
return error_mark_node;
closure_tree = NULL_TREE;
@ -11623,58 +11598,39 @@ Expression::make_field_reference(Expression* expr, unsigned int field_index,
// Class Interface_field_reference_expression.
// Return a tree for the pointer to the function to call.
// Return an expression for the pointer to the function to call.
tree
Interface_field_reference_expression::get_function_tree(Translate_context*,
tree expr)
Expression*
Interface_field_reference_expression::get_function()
{
if (this->expr_->type()->points_to() != NULL)
expr = build_fold_indirect_ref(expr);
Expression* ref = this->expr_;
Location loc = this->location();
if (ref->type()->points_to() != NULL)
ref = Expression::make_unary(OPERATOR_MULT, ref, loc);
tree expr_type = TREE_TYPE(expr);
go_assert(TREE_CODE(expr_type) == RECORD_TYPE);
tree field = TYPE_FIELDS(expr_type);
go_assert(strcmp(IDENTIFIER_POINTER(DECL_NAME(field)), "__methods") == 0);
tree table = build3(COMPONENT_REF, TREE_TYPE(field), expr, field, NULL_TREE);
go_assert(POINTER_TYPE_P(TREE_TYPE(table)));
table = build_fold_indirect_ref(table);
go_assert(TREE_CODE(TREE_TYPE(table)) == RECORD_TYPE);
Expression* mtable =
Expression::make_interface_info(ref, INTERFACE_INFO_METHODS, loc);
Struct_type* mtable_type = mtable->type()->points_to()->struct_type();
std::string name = Gogo::unpack_hidden_name(this->name_);
for (field = DECL_CHAIN(TYPE_FIELDS(TREE_TYPE(table)));
field != NULL_TREE;
field = DECL_CHAIN(field))
{
if (name == IDENTIFIER_POINTER(DECL_NAME(field)))
break;
}
go_assert(field != NULL_TREE);
return build3(COMPONENT_REF, TREE_TYPE(field), table, field, NULL_TREE);
unsigned int index;
const Struct_field* field = mtable_type->find_local_field(name, &index);
go_assert(field != NULL);
mtable = Expression::make_unary(OPERATOR_MULT, mtable, loc);
return Expression::make_field_reference(mtable, index, loc);
}
// Return a tree for the first argument to pass to the interface
// Return an expression for the first argument to pass to the interface
// function.
tree
Interface_field_reference_expression::get_underlying_object_tree(
Translate_context*,
tree expr)
Expression*
Interface_field_reference_expression::get_underlying_object()
{
if (this->expr_->type()->points_to() != NULL)
expr = build_fold_indirect_ref(expr);
tree expr_type = TREE_TYPE(expr);
go_assert(TREE_CODE(expr_type) == RECORD_TYPE);
tree field = DECL_CHAIN(TYPE_FIELDS(expr_type));
go_assert(strcmp(IDENTIFIER_POINTER(DECL_NAME(field)), "__object") == 0);
return build3(COMPONENT_REF, TREE_TYPE(field), expr, field, NULL_TREE);
Expression* expr = this->expr_;
if (expr->type()->points_to() != NULL)
expr = Expression::make_unary(OPERATOR_MULT, expr, this->location());
return Expression::make_interface_info(expr, INTERFACE_INFO_OBJECT,
this->location());
}
// Traversal.
@ -11694,9 +11650,7 @@ Interface_field_reference_expression::do_lower(Gogo*, Named_object*,
Statement_inserter* inserter,
int)
{
if (this->expr_->var_expression() == NULL
&& this->expr_->temporary_reference_expression() == NULL
&& this->expr_->set_and_use_temporary_expression() == NULL)
if (!this->expr_->is_variable())
{
Temporary_statement* temp =
Statement::make_temporary(this->expr_->type(), NULL, this->location());
@ -11923,30 +11877,22 @@ Interface_field_reference_expression::do_get_tree(Translate_context* context)
Expression* expr = Expression::make_struct_composite_literal(st, vals, loc);
expr = Expression::make_heap_composite(expr, loc);
tree closure_tree = expr->get_tree(context);
Bexpression* bclosure = tree_to_expr(expr->get_tree(context));
Expression* nil_check =
Expression::make_binary(OPERATOR_EQEQ, this->expr_,
Expression::make_nil(loc), loc);
Bexpression* bnil_check = tree_to_expr(nil_check->get_tree(context));
// Note that we are evaluating this->expr_ twice, but that is OK
// because in the lowering pass we forced it into a temporary
// variable.
tree nil_check_tree = Expression::comparison_tree(context,
Type::lookup_bool_type(),
OPERATOR_EQEQ,
this->expr_,
Expression::make_nil(loc),
loc);
Expression* crash_expr =
context->gogo()->runtime_error(RUNTIME_ERROR_NIL_DEREFERENCE, loc);
tree crash = crash_expr->get_tree(context);
if (closure_tree == error_mark_node
|| nil_check_tree == error_mark_node
|| crash == error_mark_node)
return error_mark_node;
return fold_build2_loc(loc.gcc_location(), COMPOUND_EXPR,
TREE_TYPE(closure_tree),
build3_loc(loc.gcc_location(), COND_EXPR,
void_type_node, nil_check_tree, crash,
NULL_TREE),
closure_tree);
Gogo* gogo = context->gogo();
Expression* crash = gogo->runtime_error(RUNTIME_ERROR_NIL_DEREFERENCE, loc);
Bexpression* bcrash = tree_to_expr(crash->get_tree(context));
Bexpression* bcond =
gogo->backend()->conditional_expression(bnil_check, bcrash, NULL, loc);
Bstatement* cond_statement = gogo->backend()->expression_statement(bcond);
Bexpression* ret =
gogo->backend()->compound_expression(cond_statement, bclosure, loc);
return expr_to_tree(ret);
}
// Dump ast representation for an interface field reference.
@ -14754,6 +14700,155 @@ Expression::make_slice_info(Expression* slice, Slice_info slice_info,
return new Slice_info_expression(slice, slice_info, location);
}
// An expression that evaluates to some characteristic of a non-empty interface.
// This is used to access the method table or underlying object of an interface.
class Interface_info_expression : public Expression
{
public:
Interface_info_expression(Expression* iface, Interface_info iface_info,
Location location)
: Expression(EXPRESSION_INTERFACE_INFO, location),
iface_(iface), iface_info_(iface_info)
{ }
protected:
Type*
do_type();
void
do_determine_type(const Type_context*)
{ }
Expression*
do_copy()
{
return new Interface_info_expression(this->iface_->copy(),
this->iface_info_, this->location());
}
tree
do_get_tree(Translate_context* context);
void
do_dump_expression(Ast_dump_context*) const;
void
do_issue_nil_check()
{ this->iface_->issue_nil_check(); }
private:
// The interface for which we are getting information.
Expression* iface_;
// What information we want.
Interface_info iface_info_;
};
// Return the type of the interface info.
Type*
Interface_info_expression::do_type()
{
switch (this->iface_info_)
{
case INTERFACE_INFO_METHODS:
{
Location loc = this->location();
Struct_field_list* sfl = new Struct_field_list();
Type* pdt = Type::make_type_descriptor_ptr_type();
sfl->push_back(
Struct_field(Typed_identifier("__type_descriptor", pdt, loc)));
Interface_type* itype = this->iface_->type()->interface_type();
for (Typed_identifier_list::const_iterator p = itype->methods()->begin();
p != itype->methods()->end();
++p)
{
Function_type* ft = p->type()->function_type();
go_assert(ft->receiver() == NULL);
const Typed_identifier_list* params = ft->parameters();
Typed_identifier_list* mparams = new Typed_identifier_list();
if (params != NULL)
mparams->reserve(params->size() + 1);
Type* vt = Type::make_pointer_type(Type::make_void_type());
mparams->push_back(Typed_identifier("", vt, ft->location()));
if (params != NULL)
{
for (Typed_identifier_list::const_iterator pp = params->begin();
pp != params->end();
++pp)
mparams->push_back(*pp);
}
Typed_identifier_list* mresults = (ft->results() == NULL
? NULL
: ft->results()->copy());
Backend_function_type* mft =
Type::make_backend_function_type(NULL, mparams, mresults,
ft->location());
std::string fname = Gogo::unpack_hidden_name(p->name());
sfl->push_back(Struct_field(Typed_identifier(fname, mft, loc)));
}
return Type::make_pointer_type(Type::make_struct_type(sfl, loc));
}
case INTERFACE_INFO_OBJECT:
return Type::make_pointer_type(Type::make_void_type());
default:
go_unreachable();
}
}
// Return interface information in GENERIC.
tree
Interface_info_expression::do_get_tree(Translate_context* context)
{
Gogo* gogo = context->gogo();
Bexpression* biface = tree_to_expr(this->iface_->get_tree(context));
Bexpression* ret;
switch (this->iface_info_)
{
case INTERFACE_INFO_METHODS:
case INTERFACE_INFO_OBJECT:
ret = gogo->backend()->struct_field_expression(biface, this->iface_info_,
this->location());
break;
default:
go_unreachable();
}
return expr_to_tree(ret);
}
// Dump ast representation for an interface info expression.
void
Interface_info_expression::do_dump_expression(
Ast_dump_context* ast_dump_context) const
{
ast_dump_context->ostream() << "interfaceinfo(";
this->iface_->dump_expression(ast_dump_context);
ast_dump_context->ostream() << ",";
ast_dump_context->ostream() <<
(this->iface_info_ == INTERFACE_INFO_METHODS ? "methods"
: this->iface_info_ == INTERFACE_INFO_OBJECT ? "object"
: "unknown");
ast_dump_context->ostream() << ")";
}
// Make an interface info expression.
Expression*
Expression::make_interface_info(Expression* iface, Interface_info iface_info,
Location location)
{
return new Interface_info_expression(iface, iface_info, location);
}
// An expression which evaluates to the offset of a field within a
// struct. This, like Type_info_expression, q.v., is only used to
// initialize fields of a type descriptor.

View File

@ -103,6 +103,7 @@ class Expression
EXPRESSION_TYPE_DESCRIPTOR,
EXPRESSION_TYPE_INFO,
EXPRESSION_SLICE_INFO,
EXPRESSION_INTERFACE_INFO,
EXPRESSION_STRUCT_FIELD_OFFSET,
EXPRESSION_MAP_DESCRIPTOR,
EXPRESSION_LABEL_ADDR
@ -356,6 +357,21 @@ class Expression
static Expression*
make_slice_info(Expression* slice, Slice_info, Location);
// Make an expression that evaluates to some characteristic of a
// interface. For simplicity, the enum values must match the field indexes
// of a non-empty interface in the underlying struct.
enum Interface_info
{
// The methods of an interface.
INTERFACE_INFO_METHODS,
// The first argument to pass to an interface method.
INTERFACE_INFO_OBJECT
};
static Expression*
make_interface_info(Expression* iface, Interface_info, Location);
// Make an expression which evaluates to the offset of a field in a
// struct. This is only used for type descriptors, so there is no
// location parameter.
@ -1508,10 +1524,9 @@ class Call_expression : public Expression
bool
check_argument_type(int, const Type*, const Type*, Location, bool);
tree
interface_method_function(Translate_context*,
Interface_field_reference_expression*,
tree*);
Expression*
interface_method_function(Interface_field_reference_expression*,
Expression**);
tree
set_results(Translate_context*, tree);
@ -2115,16 +2130,14 @@ class Interface_field_reference_expression : public Expression
static Named_object*
create_thunk(Gogo*, Interface_type* type, const std::string& name);
// Return a tree for the pointer to the function to call, given a
// tree for the expression.
tree
get_function_tree(Translate_context*, tree);
// Return an expression for the pointer to the function to call.
Expression*
get_function();
// Return a tree for the first argument to pass to the interface
// function, given a tree for the expression. This is the real
// object associated with the interface object.
tree
get_underlying_object_tree(Translate_context*, tree);
// Return an expression for the first argument to pass to the interface
// function. This is the real object associated with the interface object.
Expression*
get_underlying_object();
protected:
int