compiler: open code some type assertions

Now that type equality is just simple pointer equality, we can
    open code some type assertions instead of making runtime calls.
    
    Reviewed-on: https://go-review.googlesource.com/c/gofrontend/+/182977

From-SVN: r272577
This commit is contained in:
Ian Lance Taylor 2019-06-21 22:00:57 +00:00
parent fd4e7255b6
commit 0514cb3374
7 changed files with 45 additions and 71 deletions

View File

@ -1,4 +1,4 @@
593f94f008c24f5abfe7f917a717cf2b0a2585e2
5bca69ab3b41df535193474baecc3a8a4c0b3dbe
The first line of this file holds the git revision number of the last
merge done from the gofrontend repository.

View File

@ -486,9 +486,11 @@ Expression::convert_interface_to_type(Type *lhs_type, Expression* rhs,
// We are going to evaluate RHS multiple times.
go_assert(rhs->is_variable());
// Call a function to check that the type is valid. The function
// will panic with an appropriate runtime type error if the type is
// not valid.
// Build an expression to check that the type is valid. It will
// panic with an appropriate runtime type error if the type is not
// valid.
// (lhs_type != rhs_type ? panicdottype(lhs_type, rhs_type, inter_type) :
// nil /*dummy*/)
Expression* lhs_type_expr = Expression::make_type_descriptor(lhs_type,
location);
Expression* rhs_descriptor =
@ -498,11 +500,18 @@ Expression::convert_interface_to_type(Type *lhs_type, Expression* rhs,
Expression* rhs_inter_expr = Expression::make_type_descriptor(rhs_type,
location);
Expression* check_iface = Runtime::make_call(Runtime::ASSERTI2T,
location, 3, lhs_type_expr,
rhs_descriptor, rhs_inter_expr);
Expression* cond = Expression::make_binary(OPERATOR_NOTEQ, lhs_type_expr,
rhs_descriptor, location);
rhs_descriptor = Expression::get_interface_type_descriptor(rhs);
Expression* panic = Runtime::make_call(Runtime::PANICDOTTYPE, location,
3, lhs_type_expr->copy(),
rhs_descriptor,
rhs_inter_expr);
Expression* nil = Expression::make_nil(location);
Expression* check = Expression::make_conditional(cond, panic, nil,
location);
// If the call succeeds, pull out the value.
// If the conversion succeeds, pull out the value.
Expression* obj = Expression::make_interface_info(rhs, INTERFACE_INFO_OBJECT,
location);
@ -517,7 +526,7 @@ Expression::convert_interface_to_type(Type *lhs_type, Expression* rhs,
obj = Expression::make_dereference(obj, NIL_CHECK_NOT_NEEDED,
location);
}
return Expression::make_compound(check_iface, obj, location);
return Expression::make_compound(check, obj, location);
}
// Convert an expression to its backend representation. This is implemented by

View File

@ -1074,6 +1074,11 @@ class Expression
static Expression*
unpack_direct_iface(Expression*, Location);
// Return an expression representing the type descriptor field of an
// interface.
static Expression*
get_interface_type_descriptor(Expression*);
// Look through the expression of a Slice_value_expression's valmem to
// find an call to makeslice.
static std::pair<Call_expression*, Temporary_statement*>
@ -1256,9 +1261,6 @@ class Expression
: NULL);
}
static Expression*
get_interface_type_descriptor(Expression*);
static Expression*
convert_interface_to_type(Type*, Expression*, Location);

View File

@ -6243,7 +6243,8 @@ Function_declaration::get_or_make_decl(Gogo* gogo, Named_object* no)
}
if (this->asm_name_ == "runtime.gopanic"
|| this->asm_name_ == "__go_runtime_error")
|| this->asm_name_ == "__go_runtime_error"
|| this->asm_name_ == "runtime.panicdottype")
flags |= Backend::function_does_not_return;
}

View File

@ -320,23 +320,13 @@ DEF_GO_RUNTIME(ASSERTITAB, "runtime.assertitab", P2(TYPE, TYPE), R1(POINTER))
DEF_GO_RUNTIME(REQUIREITAB, "runtime.requireitab", P2(TYPE, TYPE),
R1(POINTER))
// Check whether an interface type may be converted to a
// non-interface type.
DEF_GO_RUNTIME(ASSERTI2T, "runtime.assertI2T", P3(TYPE, TYPE, TYPE), R0())
// Panic when an interface type to non-interface type conversion fails.
DEF_GO_RUNTIME(PANICDOTTYPE, "runtime.panicdottype", P3(TYPE, TYPE, TYPE),
R0())
// Return whether we can convert a type to an interface type.
DEF_GO_RUNTIME(IFACET2IP, "runtime.ifaceT2Ip", P2(TYPE, TYPE), R1(BOOL))
// Get the type descriptor of an empty interface.
DEF_GO_RUNTIME(EFACETYPE, "runtime.efacetype", P1(EFACE), R1(TYPE))
// Get the type descriptor of a non-empty interface.
DEF_GO_RUNTIME(IFACETYPE, "runtime.ifacetype", P1(IFACE), R1(TYPE))
// Compare two type descriptors for equality.
DEF_GO_RUNTIME(IFACETYPEEQ, "runtime.ifacetypeeq", P2(TYPE, TYPE), R1(BOOL))
// Compare two empty interface values.
DEF_GO_RUNTIME(EFACEEQ, "runtime.efaceeq", P2(EFACE, EFACE), R1(BOOL))

View File

@ -4614,11 +4614,12 @@ Type_case_clauses::Type_case_clause::lower(Type* switch_val_type,
cond = Expression::make_binary(OPERATOR_EQEQ, ref,
Expression::make_nil(loc),
loc);
else if (type->interface_type() == NULL)
cond = Expression::make_binary(OPERATOR_EQEQ, ref,
Expression::make_type_descriptor(type, loc),
loc);
else
cond = Runtime::make_call((type->interface_type() == NULL
? Runtime::IFACETYPEEQ
: Runtime::IFACET2IP),
loc, 2,
cond = Runtime::make_call(Runtime::IFACET2IP, loc, 2,
Expression::make_type_descriptor(type, loc),
ref);
@ -4871,23 +4872,23 @@ Type_switch_statement::do_lower(Gogo*, Named_object*, Block* enclosing,
return Statement::make_error_statement(loc);
}
Temporary_statement* val_temp =
Statement::make_temporary(NULL, this->expr_, loc);
b->add_statement(val_temp);
// var descriptor_temp DESCRIPTOR_TYPE
Type* descriptor_type = Type::make_type_descriptor_ptr_type();
Temporary_statement* descriptor_temp =
Statement::make_temporary(descriptor_type, NULL, loc);
b->add_statement(descriptor_temp);
// descriptor_temp = ifacetype(val_temp) FIXME: This should be
// inlined.
bool is_empty = val_type->interface_type()->is_empty();
Expression* call = Runtime::make_call((is_empty
? Runtime::EFACETYPE
: Runtime::IFACETYPE),
loc, 1, this->expr_);
// descriptor_temp = ifacetype(val_temp)
Expression* ref = Expression::make_temporary_reference(val_temp, loc);
Expression* td = Expression::get_interface_type_descriptor(ref);
Temporary_reference_expression* lhs =
Expression::make_temporary_reference(descriptor_temp, loc);
lhs->set_is_lvalue();
Statement* s = Statement::make_assignment(lhs, call, loc);
Statement* s = Statement::make_assignment(lhs, td, loc);
b->add_statement(s);
if (this->clauses_ != NULL)

View File

@ -15,10 +15,7 @@ import (
//
//go:linkname requireitab runtime.requireitab
//go:linkname assertitab runtime.assertitab
//go:linkname assertI2T runtime.assertI2T
//go:linkname ifacetypeeq runtime.ifacetypeeq
//go:linkname efacetype runtime.efacetype
//go:linkname ifacetype runtime.ifacetype
//go:linkname panicdottype runtime.panicdottype
//go:linkname ifaceE2E2 runtime.ifaceE2E2
//go:linkname ifaceI2E2 runtime.ifaceI2E2
//go:linkname ifaceE2I2 runtime.ifaceE2I2
@ -356,35 +353,9 @@ func assertitab(lhs, rhs *_type) unsafe.Pointer {
return getitab(lhs, rhs, false)
}
// Check whether an interface type may be converted to a non-interface
// type, panicing if not.
func assertI2T(lhs, rhs, inter *_type) {
if rhs == nil {
panic(&TypeAssertionError{nil, nil, lhs, ""})
}
if !eqtype(lhs, rhs) {
panic(&TypeAssertionError{inter, rhs, lhs, ""})
}
}
// Compare two type descriptors for equality.
func ifacetypeeq(a, b *_type) bool {
return eqtype(a, b)
}
// Return the type descriptor of an empty interface.
// FIXME: This should be inlined by the compiler.
func efacetype(e eface) *_type {
return e._type
}
// Return the type descriptor of a non-empty interface.
// FIXME: This should be inlined by the compiler.
func ifacetype(i iface) *_type {
if i.tab == nil {
return nil
}
return *(**_type)(i.tab)
// panicdottype is called when doing an i.(T) conversion and the conversion fails.
func panicdottype(lhs, rhs, inter *_type) {
panic(&TypeAssertionError{inter, rhs, lhs, ""})
}
// Convert an empty interface to an empty interface, for a comma-ok