Files
sdk/runtime/vm/parser.h
T
Tess Strickland 787446213c [vm/compiler] Move argument shape (not type) checks out of closures.
This CL performs the following checks in the invoke field dispatcher for
dynamic closure calls when lazy dispatchers are enabled:

* The provided function type arguments vector (if any) has the correct
  length.

* No function type arguments should be provided if the closure has
  delayed type arguments.

* All required arguments (positional in all modes, named in appropriate
  null safety modes) have been provided by the caller.

* If there are optional positional arguments, an appropriate number
  has been provided.

* If there are optional named arguments, their names are valid.

Since the runtime already handles checking the argument shapes when lazy
dispatchers are disabled, these checks are now completely removed from
closure bodies in all cases. Thus, the only remaining checks in closure
bodies are the type checks performed by AssertSubtype and
AssertAssignable when lazy dispatchers are enabled.

Changes in the Flutter Gallery:

* ARM7, release: -3.61% instructions, -2.19% total
* ARM7, sizeopt: -3.62% instructions, -2.55% total
* ARM8, release: -3.66% instructions, -1.98% total
* ARM8, sizeopt: -3.65% instructions, -2.37% total

Most of these changes are already exercised by existing tests such as
(but not limited to):

* corelib{,_2}/dynamic_nosuchmethod_test
* language{,_2}/call/call_test
* language{,_2}/closure/tearoff_dynamic_test
* language{,_2}/generic/function_bounds_test
* language{,_2}/parameter/named_with_conversions_test
* language{,_2}/vm/no_such_args_error_message_vm_test

I've added one test to specifically check the interaction between
dynamic calls and required named parameters. There is some coverage in
other NNBD tests, but those are not directly focused on testing this
specifically.

Other changes:

* Adds initial cached ranges for certain BinarySmiOp and ShiftIntegerOp
  instructions when the RHS is a constant, to avoid false negatives for
  deoptimization and throw checks prior to range analysis.

* Adds new slots for various Function fields.

* Adds the ability to define unboxed native slots, which are always
  unboxed after retrieval even in unoptimized code. In the first
  iteration, the backend only handles loads from Uint32 unboxed native
  slots. Part of https://github.com/dart-lang/sdk/issues/42793.

* Removed the special handling for loading from non-nullable int fields
  in AOT compilation. Instead, their treatment is unified with the
  treatment of the new unboxed native fields, since the source field is
  always unboxed and the result of the load is also always unboxed, as
  code involving them is always optimized.

Bug: https://github.com/dart-lang/sdk/issues/40813
Change-Id: Ia02aa3e872c1fefd906fd67b55021ea1797556e4
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/155604
Commit-Queue: Tess Strickland <sstrickl@google.com>
Reviewed-by: Alexander Markov <alexmarkov@google.com>
2020-08-17 08:58:08 +00:00

325 lines
10 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.
#ifndef RUNTIME_VM_PARSER_H_
#define RUNTIME_VM_PARSER_H_
#include "include/dart_api.h"
#include "lib/invocation_mirror.h"
#include "platform/assert.h"
#include "platform/globals.h"
#include "vm/allocation.h"
#include "vm/class_finalizer.h"
#include "vm/hash_table.h"
#include "vm/kernel.h"
#include "vm/object.h"
#include "vm/raw_object.h"
#include "vm/scopes.h"
#include "vm/token.h"
namespace dart {
// Forward declarations.
namespace kernel {
class ScopeBuildingResult;
} // namespace kernel
class ArgumentsDescriptor;
class BitVector;
class Isolate;
class LocalScope;
class LocalVariable;
struct RegExpCompileData;
class SourceLabel;
template <typename T>
class GrowableArray;
class Parser;
struct CatchParamDesc;
class ClassDesc;
struct MemberDesc;
struct ParamList;
struct QualIdent;
class TopLevel;
class RecursionChecker;
// The class ParsedFunction holds the result of parsing a function.
class ParsedFunction : public ZoneAllocated {
public:
ParsedFunction(Thread* thread, const Function& function);
const Function& function() const { return function_; }
const Code& code() const { return code_; }
LocalScope* scope() const { return scope_; }
void set_scope(LocalScope* scope) {
ASSERT(scope_ == nullptr);
ASSERT(scope != nullptr);
scope_ = scope;
}
RegExpCompileData* regexp_compile_data() const {
return regexp_compile_data_;
}
void SetRegExpCompileData(RegExpCompileData* regexp_compile_data);
LocalVariable* function_type_arguments() const {
return function_type_arguments_;
}
void set_function_type_arguments(LocalVariable* function_type_arguments) {
ASSERT(function_type_arguments != NULL);
function_type_arguments_ = function_type_arguments;
}
LocalVariable* parent_type_arguments() const {
return parent_type_arguments_;
}
void set_parent_type_arguments(LocalVariable* parent_type_arguments) {
ASSERT(parent_type_arguments != NULL);
parent_type_arguments_ = parent_type_arguments;
}
void set_default_parameter_values(ZoneGrowableArray<const Instance*>* list) {
default_parameter_values_ = list;
#if defined(DEBUG)
if (list == NULL) return;
for (intptr_t i = 0; i < list->length(); i++) {
ASSERT(list->At(i)->IsZoneHandle() || list->At(i)->InVMIsolateHeap());
}
#endif
}
const Instance& DefaultParameterValueAt(intptr_t i) const {
ASSERT(default_parameter_values_ != NULL);
return *default_parameter_values_->At(i);
}
ZoneGrowableArray<const Instance*>* default_parameter_values() const {
return default_parameter_values_;
}
LocalVariable* current_context_var() const { return current_context_var_; }
bool has_arg_desc_var() const { return arg_desc_var_ != NULL; }
LocalVariable* arg_desc_var() const { return arg_desc_var_; }
LocalVariable* receiver_var() const {
ASSERT(receiver_var_ != nullptr);
return receiver_var_;
}
void set_receiver_var(LocalVariable* value) {
ASSERT(receiver_var_ == nullptr);
ASSERT(value != nullptr);
receiver_var_ = value;
}
bool has_receiver_var() const { return receiver_var_ != nullptr; }
LocalVariable* expression_temp_var() const {
ASSERT(has_expression_temp_var());
return expression_temp_var_;
}
void set_expression_temp_var(LocalVariable* value) {
ASSERT(!has_expression_temp_var());
expression_temp_var_ = value;
}
bool has_expression_temp_var() const { return expression_temp_var_ != NULL; }
LocalVariable* entry_points_temp_var() const {
ASSERT(has_entry_points_temp_var());
return entry_points_temp_var_;
}
void set_entry_points_temp_var(LocalVariable* value) {
ASSERT(!has_entry_points_temp_var());
entry_points_temp_var_ = value;
}
bool has_entry_points_temp_var() const {
return entry_points_temp_var_ != NULL;
}
LocalVariable* finally_return_temp_var() const {
ASSERT(has_finally_return_temp_var());
return finally_return_temp_var_;
}
void set_finally_return_temp_var(LocalVariable* value) {
ASSERT(!has_finally_return_temp_var());
finally_return_temp_var_ = value;
}
bool has_finally_return_temp_var() const {
return finally_return_temp_var_ != NULL;
}
void EnsureFinallyReturnTemp(bool is_async);
LocalVariable* EnsureExpressionTemp();
LocalVariable* EnsureEntryPointsTemp();
ZoneGrowableArray<const Field*>* guarded_fields() const {
return guarded_fields_;
}
VariableIndex first_parameter_index() const { return first_parameter_index_; }
int num_stack_locals() const { return num_stack_locals_; }
void AllocateVariables();
void AllocateIrregexpVariables(intptr_t num_stack_locals);
void AllocateBytecodeVariables(intptr_t num_stack_locals);
void record_await() { have_seen_await_expr_ = true; }
bool have_seen_await() const { return have_seen_await_expr_; }
bool is_forwarding_stub() const {
return forwarding_stub_super_target_ != nullptr;
}
const Function* forwarding_stub_super_target() const {
return forwarding_stub_super_target_;
}
void MarkForwardingStub(const Function* forwarding_target) {
forwarding_stub_super_target_ = forwarding_target;
}
Thread* thread() const { return thread_; }
Isolate* isolate() const { return thread_->isolate(); }
Zone* zone() const { return thread_->zone(); }
// Adds only relevant fields: field must be unique and its guarded_cid()
// relevant.
void AddToGuardedFields(const Field* field) const;
void Bailout(const char* origin, const char* reason) const;
kernel::ScopeBuildingResult* EnsureKernelScopes();
LocalVariable* RawTypeArgumentsVariable() const {
return raw_type_arguments_var_;
}
void SetRawTypeArgumentsVariable(LocalVariable* raw_type_arguments_var) {
raw_type_arguments_var_ = raw_type_arguments_var;
}
void SetRawParameters(ZoneGrowableArray<LocalVariable*>* raw_parameters) {
raw_parameters_ = raw_parameters;
}
LocalVariable* RawParameterVariable(intptr_t i) const {
return raw_parameters_->At(i);
}
LocalVariable* ParameterVariable(intptr_t i) const {
ASSERT((i >= 0) && (i < function_.NumParameters()));
ASSERT(scope() != nullptr);
return scope()->VariableAt(i);
}
void SetDefaultFunctionTypeArguments(const TypeArguments& value) {
default_function_type_arguments_ = value.raw();
}
const TypeArguments& DefaultFunctionTypeArguments() const {
return default_function_type_arguments_;
}
// Remembers the set of covariant parameters.
// [covariant_parameters] is a bitvector of function.NumParameters() length.
void SetCovariantParameters(const BitVector* covariant_parameters);
// Remembers the set of generic-covariant-impl parameters.
// [covariant_parameters] is a bitvector of function.NumParameters() length.
void SetGenericCovariantImplParameters(
const BitVector* generic_covariant_impl_parameters);
bool HasCovariantParametersInfo() const {
return covariant_parameters_ != nullptr;
}
// Returns true if i-th parameter is covariant.
// SetCovariantParameters should be called before using this method.
bool IsCovariantParameter(intptr_t i) const;
// Returns true if i-th parameter is generic-covariant-impl.
// SetGenericCovariantImplParameters should be called before using this
// method.
bool IsGenericCovariantImplParameter(intptr_t i) const;
// Variables needed for the InvokeFieldDispatcher for dynamic closure calls.
struct DynamicClosureCallVars : ZoneAllocated {
#define FOR_EACH_DYNAMIC_CLOSURE_CALL_VARIABLE(V) \
V(current_param_index, Smi, CurrentParamIndex) \
V(has_named_params, Bool, HasNamed) \
V(num_fixed_params, Smi, NumFixed) \
V(num_opt_params, Smi, NumOpt) \
V(num_max_params, Smi, MaxParams) \
V(parameter_names, Array, ParameterNames)
#define DEFINE_FIELD(Name, _, __) LocalVariable* Name = nullptr;
FOR_EACH_DYNAMIC_CLOSURE_CALL_VARIABLE(DEFINE_FIELD)
#undef DEFINE_FIELD
};
DynamicClosureCallVars* dynamic_closure_call_vars() const {
return dynamic_closure_call_vars_;
}
DynamicClosureCallVars* EnsureDynamicClosureCallVars();
private:
Thread* thread_;
const Function& function_;
Code& code_;
LocalScope* scope_;
RegExpCompileData* regexp_compile_data_;
LocalVariable* function_type_arguments_;
LocalVariable* parent_type_arguments_;
LocalVariable* current_context_var_;
LocalVariable* arg_desc_var_;
LocalVariable* receiver_var_ = nullptr;
LocalVariable* expression_temp_var_;
LocalVariable* entry_points_temp_var_;
LocalVariable* finally_return_temp_var_;
DynamicClosureCallVars* dynamic_closure_call_vars_;
ZoneGrowableArray<const Field*>* guarded_fields_;
ZoneGrowableArray<const Instance*>* default_parameter_values_;
LocalVariable* raw_type_arguments_var_;
ZoneGrowableArray<LocalVariable*>* raw_parameters_ = nullptr;
VariableIndex first_parameter_index_;
int num_stack_locals_;
bool have_seen_await_expr_;
const Function* forwarding_stub_super_target_ = nullptr;
kernel::ScopeBuildingResult* kernel_scopes_;
TypeArguments& default_function_type_arguments_;
const BitVector* covariant_parameters_ = nullptr;
const BitVector* generic_covariant_impl_parameters_ = nullptr;
friend class Parser;
DISALLOW_COPY_AND_ASSIGN(ParsedFunction);
};
class Parser : public ValueObject {
public:
// Parse a function to retrieve parameter information that is not retained in
// the Function object. Returns either an error if the parse fails (which
// could be the case for local functions), or a flat array of entries for each
// parameter. Each parameter entry contains: * a Dart bool indicating whether
// the parameter was declared final * its default value (or null if none was
// declared) * an array of metadata (or null if no metadata was declared).
enum {
kParameterIsFinalOffset,
kParameterDefaultValueOffset,
kParameterMetadataOffset,
kParameterEntrySize,
};
private:
DISALLOW_COPY_AND_ASSIGN(Parser);
};
} // namespace dart
#endif // RUNTIME_VM_PARSER_H_