d9685d1c4c
These checks are now performed in the dynamic closure call dispatcher.
To avoid having to create type argument vectors (TAVs) for default type
arguments at runtime, we cache a compile-time created TAV in the
ClosureData for the closure function which is retrieved by the
dispatcher when needed.
We also keep an associated packed field of information that can also be
determined at compile time:
* Whether the cached TAV needs instantiation or can share its
instantiator or function type arguments.
* The number of parent type parameters.
The former allows the generated IL to keep the invariant that the
InstantiateTypeArguments instruction (and the runtime entry it calls) is
only used for uninstantiated TAVs.
Also changes the destination name to an Value input for the
AssertSubtype instruction and adds handling for non-constant types and
names in that instruction's backend.
Additional changes:
* Adds new slots for ClosureData, Function, and TypeArguments.
* Adds a new kUnboxedUint8 representation (needed for
TypeParameterLayout::flags_, which is of type uint8_t).
* Extends LoadField to handle uint8_t unboxed native fields.
* Adds BoxUint8 for boxing unboxed uint8_t values.
Code size impact on Flutter gallery in release mode:
* arm7: total +0.08%, vmisolate: +0.56%, isolate: +0.42%,
readonly: -0.04%, instructions: -0.03%
* arm8: total +0.12%, vmisolate: +0.56%, isolate: +0.42%,
readonly: -0.002%, instructions: +0.03%
Cq-Include-Trybots: luci.dart.try:vm-kernel-linux-debug-ia32-try,vm-kernel-linux-debug-x64-try,vm-kernel-nnbd-linux-debug-ia32-try,vm-kernel-nnbd-linux-debug-x64-try,vm-kernel-precomp-linux-debug-simarm_x64-try,vm-kernel-precomp-linux-debug-x64-try,vm-kernel-precomp-nnbd-linux-debug-simarm_x64-try,vm-kernel-precomp-nnbd-linux-debug-x64-try,vm-kernel-linux-release-simarm-try,vm-kernel-linux-release-simarm64-try,vm-kernel-nnbd-linux-release-simarm-try,vm-kernel-nnbd-linux-release-simarm64-try,vm-kernel-precomp-linux-release-simarm-try,vm-kernel-precomp-linux-release-simarm64-try,vm-kernel-precomp-nnbd-linux-release-simarm64-try
Bug: https://github.com/dart-lang/sdk/issues/40813
Change-Id: I5a7de27a17e3119e27752bd0d10e1c6bc1b52a16
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/158844
Commit-Queue: Tess Strickland <sstrickl@google.com>
Reviewed-by: Martin Kustermann <kustermann@google.com>
Reviewed-by: Régis Crelier <regis@google.com>
367 lines
14 KiB
C++
367 lines
14 KiB
C++
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
|
|
// for details. All rights reserved. Use of this source code is governed by a
|
|
// BSD-style license that can be found in the LICENSE file.
|
|
|
|
#include "vm/parser.h"
|
|
#include "vm/flags.h"
|
|
|
|
#ifndef DART_PRECOMPILED_RUNTIME
|
|
|
|
#include "lib/invocation_mirror.h"
|
|
#include "platform/utils.h"
|
|
#include "vm/bit_vector.h"
|
|
#include "vm/bootstrap.h"
|
|
#include "vm/class_finalizer.h"
|
|
#include "vm/compiler/aot/precompiler.h"
|
|
#include "vm/compiler/backend/il_printer.h"
|
|
#include "vm/compiler/frontend/scope_builder.h"
|
|
#include "vm/compiler/jit/compiler.h"
|
|
#include "vm/dart_api_impl.h"
|
|
#include "vm/dart_entry.h"
|
|
#include "vm/growable_array.h"
|
|
#include "vm/handles.h"
|
|
#include "vm/hash_table.h"
|
|
#include "vm/heap/heap.h"
|
|
#include "vm/heap/safepoint.h"
|
|
#include "vm/isolate.h"
|
|
#include "vm/longjump.h"
|
|
#include "vm/native_arguments.h"
|
|
#include "vm/native_entry.h"
|
|
#include "vm/object.h"
|
|
#include "vm/object_store.h"
|
|
#include "vm/os.h"
|
|
#include "vm/regexp_assembler.h"
|
|
#include "vm/resolver.h"
|
|
#include "vm/scopes.h"
|
|
#include "vm/stack_frame.h"
|
|
#include "vm/symbols.h"
|
|
#include "vm/tags.h"
|
|
#include "vm/timeline.h"
|
|
#include "vm/zone.h"
|
|
|
|
namespace dart {
|
|
|
|
// Quick access to the current thread, isolate and zone.
|
|
#define T (thread())
|
|
#define I (isolate())
|
|
#define Z (zone())
|
|
|
|
ParsedFunction::ParsedFunction(Thread* thread, const Function& function)
|
|
: thread_(thread),
|
|
function_(function),
|
|
code_(Code::Handle(zone(), function.unoptimized_code())),
|
|
scope_(NULL),
|
|
regexp_compile_data_(NULL),
|
|
function_type_arguments_(NULL),
|
|
parent_type_arguments_(NULL),
|
|
current_context_var_(NULL),
|
|
arg_desc_var_(NULL),
|
|
expression_temp_var_(NULL),
|
|
entry_points_temp_var_(NULL),
|
|
finally_return_temp_var_(NULL),
|
|
dynamic_closure_call_vars_(nullptr),
|
|
guarded_fields_(new ZoneGrowableArray<const Field*>()),
|
|
default_parameter_values_(NULL),
|
|
raw_type_arguments_var_(NULL),
|
|
first_parameter_index_(),
|
|
num_stack_locals_(0),
|
|
have_seen_await_expr_(false),
|
|
kernel_scopes_(NULL) {
|
|
ASSERT(function.IsZoneHandle());
|
|
// Every function has a local variable for the current context.
|
|
LocalVariable* temp = new (zone())
|
|
LocalVariable(function.token_pos(), function.token_pos(),
|
|
Symbols::CurrentContextVar(), Object::dynamic_type());
|
|
current_context_var_ = temp;
|
|
|
|
const bool reify_generic_argument = function.IsGeneric();
|
|
|
|
const bool load_optional_arguments = function.HasOptionalParameters();
|
|
|
|
const bool check_arguments = function.CanReceiveDynamicInvocation();
|
|
|
|
const bool need_argument_descriptor =
|
|
load_optional_arguments || check_arguments || reify_generic_argument;
|
|
|
|
if (need_argument_descriptor) {
|
|
arg_desc_var_ = new (zone())
|
|
LocalVariable(TokenPosition::kNoSource, TokenPosition::kNoSource,
|
|
Symbols::ArgDescVar(), Object::dynamic_type());
|
|
}
|
|
}
|
|
|
|
void ParsedFunction::AddToGuardedFields(const Field* field) const {
|
|
if ((field->guarded_cid() == kDynamicCid) ||
|
|
(field->guarded_cid() == kIllegalCid)) {
|
|
return;
|
|
}
|
|
|
|
for (intptr_t j = 0; j < guarded_fields_->length(); j++) {
|
|
const Field* other = (*guarded_fields_)[j];
|
|
if (field->Original() == other->Original()) {
|
|
// Abort background compilation early if the guarded state of this field
|
|
// has changed during compilation. We will not be able to commit
|
|
// the resulting code anyway.
|
|
if (Compiler::IsBackgroundCompilation()) {
|
|
if (!other->IsConsistentWith(*field)) {
|
|
Compiler::AbortBackgroundCompilation(
|
|
DeoptId::kNone,
|
|
"Field's guarded state changed during compilation");
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Note: the list of guarded fields must contain copies during background
|
|
// compilation because we will look at their guarded_cid when copying
|
|
// the array of guarded fields from callee into the caller during
|
|
// inlining.
|
|
ASSERT(!field->IsOriginal() || Thread::Current()->IsMutatorThread());
|
|
guarded_fields_->Add(&Field::ZoneHandle(Z, field->raw()));
|
|
}
|
|
|
|
void ParsedFunction::Bailout(const char* origin, const char* reason) const {
|
|
Report::MessageF(Report::kBailout, Script::Handle(function_.script()),
|
|
function_.token_pos(), Report::AtLocation,
|
|
"%s Bailout in %s: %s", origin,
|
|
String::Handle(function_.name()).ToCString(), reason);
|
|
UNREACHABLE();
|
|
}
|
|
|
|
kernel::ScopeBuildingResult* ParsedFunction::EnsureKernelScopes() {
|
|
if (kernel_scopes_ == NULL) {
|
|
kernel::ScopeBuilder builder(this);
|
|
kernel_scopes_ = builder.BuildScopes();
|
|
}
|
|
return kernel_scopes_;
|
|
}
|
|
|
|
LocalVariable* ParsedFunction::EnsureExpressionTemp() {
|
|
if (!has_expression_temp_var()) {
|
|
LocalVariable* temp =
|
|
new (Z) LocalVariable(function_.token_pos(), function_.token_pos(),
|
|
Symbols::ExprTemp(), Object::dynamic_type());
|
|
ASSERT(temp != NULL);
|
|
set_expression_temp_var(temp);
|
|
}
|
|
ASSERT(has_expression_temp_var());
|
|
return expression_temp_var();
|
|
}
|
|
|
|
LocalVariable* ParsedFunction::EnsureEntryPointsTemp() {
|
|
if (!has_entry_points_temp_var()) {
|
|
LocalVariable* temp = new (Z)
|
|
LocalVariable(function_.token_pos(), function_.token_pos(),
|
|
Symbols::EntryPointsTemp(), Object::dynamic_type());
|
|
ASSERT(temp != NULL);
|
|
set_entry_points_temp_var(temp);
|
|
}
|
|
ASSERT(has_entry_points_temp_var());
|
|
return entry_points_temp_var();
|
|
}
|
|
|
|
void ParsedFunction::EnsureFinallyReturnTemp(bool is_async) {
|
|
if (!has_finally_return_temp_var()) {
|
|
LocalVariable* temp =
|
|
new (Z) LocalVariable(function_.token_pos(), function_.token_pos(),
|
|
Symbols::FinallyRetVal(), Object::dynamic_type());
|
|
ASSERT(temp != NULL);
|
|
temp->set_is_final();
|
|
if (is_async) {
|
|
temp->set_is_captured();
|
|
}
|
|
set_finally_return_temp_var(temp);
|
|
}
|
|
ASSERT(has_finally_return_temp_var());
|
|
}
|
|
|
|
void ParsedFunction::SetRegExpCompileData(
|
|
RegExpCompileData* regexp_compile_data) {
|
|
ASSERT(regexp_compile_data_ == NULL);
|
|
ASSERT(regexp_compile_data != NULL);
|
|
regexp_compile_data_ = regexp_compile_data;
|
|
}
|
|
|
|
void ParsedFunction::AllocateVariables() {
|
|
ASSERT(!function().IsIrregexpFunction());
|
|
LocalScope* scope = this->scope();
|
|
const intptr_t num_fixed_params = function().num_fixed_parameters();
|
|
const intptr_t num_opt_params = function().NumOptionalParameters();
|
|
const intptr_t num_params = num_fixed_params + num_opt_params;
|
|
|
|
// Before we start allocating indices to variables, we'll setup the
|
|
// parameters array, which can be used to access the raw parameters (i.e. not
|
|
// the potentially variables which are in the context)
|
|
|
|
raw_parameters_ = new (Z) ZoneGrowableArray<LocalVariable*>(Z, num_params);
|
|
for (intptr_t param = 0; param < num_params; ++param) {
|
|
LocalVariable* variable = ParameterVariable(param);
|
|
LocalVariable* raw_parameter = variable;
|
|
if (variable->is_captured()) {
|
|
String& tmp = String::ZoneHandle(Z);
|
|
tmp = Symbols::FromConcat(T, Symbols::OriginalParam(), variable->name());
|
|
|
|
RELEASE_ASSERT(scope->LocalLookupVariable(tmp) == NULL);
|
|
raw_parameter = new LocalVariable(
|
|
variable->declaration_token_pos(), variable->token_pos(), tmp,
|
|
variable->type(), variable->parameter_type(),
|
|
variable->parameter_value());
|
|
if (variable->is_explicit_covariant_parameter()) {
|
|
raw_parameter->set_is_explicit_covariant_parameter();
|
|
}
|
|
if (variable->needs_covariant_check_in_method()) {
|
|
raw_parameter->set_needs_covariant_check_in_method();
|
|
}
|
|
raw_parameter->set_type_check_mode(variable->type_check_mode());
|
|
if (function().HasOptionalParameters()) {
|
|
bool ok = scope->AddVariable(raw_parameter);
|
|
ASSERT(ok);
|
|
|
|
// Currently our optimizer cannot prove liveness of variables properly
|
|
// when a function has try/catch. It therefore makes the conservative
|
|
// estimate that all [LocalVariable]s in the frame are live and spills
|
|
// them before call sites (in some shape or form).
|
|
//
|
|
// Since we are guaranteed to not need that, we tell the try/catch
|
|
// sync moves mechanism not to care about this variable.
|
|
//
|
|
// Receiver (this variable) is an exception from this rule because
|
|
// it is immutable and we don't reload captured it from the context but
|
|
// instead use raw_parameter to access it. This means we must still
|
|
// consider it when emitting the catch entry moves.
|
|
const bool is_receiver_var =
|
|
function().HasThisParameter() && receiver_var_ == variable;
|
|
if (!is_receiver_var) {
|
|
raw_parameter->set_is_captured_parameter(true);
|
|
}
|
|
|
|
} else {
|
|
raw_parameter->set_index(
|
|
VariableIndex(function().NumParameters() - param));
|
|
}
|
|
}
|
|
raw_parameters_->Add(raw_parameter);
|
|
}
|
|
if (function_type_arguments_ != NULL) {
|
|
LocalVariable* raw_type_args_parameter = function_type_arguments_;
|
|
if (function_type_arguments_->is_captured()) {
|
|
String& tmp = String::ZoneHandle(Z);
|
|
tmp = Symbols::FromConcat(T, Symbols::OriginalParam(),
|
|
function_type_arguments_->name());
|
|
|
|
ASSERT(scope->LocalLookupVariable(tmp) == NULL);
|
|
raw_type_args_parameter =
|
|
new LocalVariable(raw_type_args_parameter->declaration_token_pos(),
|
|
raw_type_args_parameter->token_pos(), tmp,
|
|
raw_type_args_parameter->type());
|
|
bool ok = scope->AddVariable(raw_type_args_parameter);
|
|
ASSERT(ok);
|
|
}
|
|
raw_type_arguments_var_ = raw_type_args_parameter;
|
|
}
|
|
|
|
// The copy parameters implementation will still write to local variables
|
|
// which we assign indices as with the old CopyParams implementation.
|
|
VariableIndex parameter_index_start;
|
|
VariableIndex reamining_local_variables_start;
|
|
{
|
|
// Compute start indices to parameters and locals, and the number of
|
|
// parameters to copy.
|
|
if (num_opt_params == 0) {
|
|
parameter_index_start = first_parameter_index_ =
|
|
VariableIndex(num_params);
|
|
reamining_local_variables_start = VariableIndex(0);
|
|
} else {
|
|
parameter_index_start = first_parameter_index_ = VariableIndex(0);
|
|
reamining_local_variables_start = VariableIndex(-num_params);
|
|
}
|
|
}
|
|
|
|
if (function_type_arguments_ != NULL && num_opt_params > 0) {
|
|
reamining_local_variables_start =
|
|
VariableIndex(reamining_local_variables_start.value() - 1);
|
|
}
|
|
|
|
// Allocate parameters and local variables, either in the local frame or
|
|
// in the context(s).
|
|
bool found_captured_variables = false;
|
|
VariableIndex first_local_index =
|
|
VariableIndex(parameter_index_start.value() > 0 ? 0 : -num_params);
|
|
VariableIndex next_free_index = scope->AllocateVariables(
|
|
parameter_index_start, num_params, first_local_index, NULL,
|
|
&found_captured_variables);
|
|
|
|
num_stack_locals_ = -next_free_index.value();
|
|
}
|
|
|
|
void ParsedFunction::AllocateIrregexpVariables(intptr_t num_stack_locals) {
|
|
ASSERT(function().IsIrregexpFunction());
|
|
ASSERT(function().NumOptionalParameters() == 0);
|
|
const intptr_t num_params = function().num_fixed_parameters();
|
|
ASSERT(num_params == RegExpMacroAssembler::kParamCount);
|
|
// Compute start indices to parameters and locals, and the number of
|
|
// parameters to copy.
|
|
first_parameter_index_ = VariableIndex(num_params);
|
|
|
|
// Frame indices are relative to the frame pointer and are decreasing.
|
|
num_stack_locals_ = num_stack_locals;
|
|
}
|
|
|
|
void ParsedFunction::AllocateBytecodeVariables(intptr_t num_stack_locals) {
|
|
ASSERT(!function().IsIrregexpFunction());
|
|
first_parameter_index_ = VariableIndex(function().num_fixed_parameters());
|
|
num_stack_locals_ = num_stack_locals;
|
|
}
|
|
|
|
void ParsedFunction::SetCovariantParameters(
|
|
const BitVector* covariant_parameters) {
|
|
ASSERT(covariant_parameters_ == nullptr);
|
|
ASSERT(covariant_parameters->length() == function_.NumParameters());
|
|
covariant_parameters_ = covariant_parameters;
|
|
}
|
|
|
|
void ParsedFunction::SetGenericCovariantImplParameters(
|
|
const BitVector* generic_covariant_impl_parameters) {
|
|
ASSERT(generic_covariant_impl_parameters_ == nullptr);
|
|
ASSERT(generic_covariant_impl_parameters->length() ==
|
|
function_.NumParameters());
|
|
generic_covariant_impl_parameters_ = generic_covariant_impl_parameters;
|
|
}
|
|
|
|
bool ParsedFunction::IsCovariantParameter(intptr_t i) const {
|
|
ASSERT(covariant_parameters_ != nullptr);
|
|
ASSERT((i >= 0) && (i < function_.NumParameters()));
|
|
return covariant_parameters_->Contains(i);
|
|
}
|
|
|
|
bool ParsedFunction::IsGenericCovariantImplParameter(intptr_t i) const {
|
|
ASSERT(generic_covariant_impl_parameters_ != nullptr);
|
|
ASSERT((i >= 0) && (i < function_.NumParameters()));
|
|
return generic_covariant_impl_parameters_->Contains(i);
|
|
}
|
|
|
|
ParsedFunction::DynamicClosureCallVars*
|
|
ParsedFunction::EnsureDynamicClosureCallVars() {
|
|
ASSERT(function().IsDynamicClosureCallDispatcher(thread()));
|
|
if (dynamic_closure_call_vars_ != nullptr) return dynamic_closure_call_vars_;
|
|
dynamic_closure_call_vars_ = new (zone()) DynamicClosureCallVars();
|
|
|
|
const auto& type_Dynamic = Object::dynamic_type();
|
|
const auto& type_Function =
|
|
Type::ZoneHandle(zone(), Type::DartFunctionType());
|
|
const auto& type_Smi = Type::ZoneHandle(zone(), Type::SmiType());
|
|
#define INIT_FIELD(Name, TypeName, Symbol) \
|
|
dynamic_closure_call_vars_->Name = new (zone()) \
|
|
LocalVariable(function().token_pos(), function().token_pos(), \
|
|
Symbols::DynamicCall##Symbol##Var(), type_##TypeName);
|
|
FOR_EACH_DYNAMIC_CLOSURE_CALL_VARIABLE(INIT_FIELD);
|
|
#undef INIT_FIELD
|
|
|
|
return dynamic_closure_call_vars_;
|
|
}
|
|
|
|
} // namespace dart
|
|
|
|
#endif // DART_PRECOMPILED_RUNTIME
|