[vm/kernel/aot] Skip unnecessary type checks on parameters of instance methods.

This relands 75a9579ea0.

The approach works as follows:

Step 1: Kernel transform. Under the closed world assumption compute the
set of selectors dispatched dynamically, then mark all procedures that don't
match any of those selectors as 'not-dispatched-dynamically'.

Step 2: VM backend. When building IR for a function if this function was
marked as not-dispatched-dynamically then omit type checks for any parameter
that is not marked as generic-covariant-impl, as such arguments are guaranteed
to be checked on the caller side (by front-end).


+------------------------+------------+----------+-----------+--------------+
|       benchmark        |  baseline  |  current |  with opt |  improved by |
+------------------------+------------+----------+-----------+--------------+
| stock_layout_iteration |  2366.3786 |   2724.3 |   2562.75 |  -5.93%      |
| stock_build_iteration  |     3824.3 |   4914.8 |      4681 |  -4.76%      |
+------------------------+------------+----------+-----------+--------------+

* Flutter gallery Instructions size is reduced by 11% (8748720 bytes to 7846368 bytes).
Baseline is at 6196496 bytes.


Alternatively to annotating individual procedures, I considered annotating Program node
with a set of dynamically dispatched selectors. Decoding and passing this information
around proved to be quite cumbersome in the "streaming" world, so I opted for a simpler
approach where all individual procedures are annotated.

Bug: https://github.com/dart-lang/sdk/issues/3179
Change-Id: I2f32a609e3872c74d5ae7bbd97555453aaedf15f
Reviewed-on: https://dart-review.googlesource.com/38125
Commit-Queue: Samir Jindel <sjindel@google.com>
Reviewed-by: Samir Jindel <sjindel@google.com>
This commit is contained in:
Vyacheslav Egorov
2018-02-02 11:43:55 +00:00
committed by commit-bot@chromium.org
parent 5ec65552d5
commit d117760ba6
10 changed files with 537 additions and 69 deletions
+3
View File
@@ -11,6 +11,8 @@ import 'package:kernel/binary/ast_from_binary.dart'
import 'package:vm/metadata/direct_call.dart' show DirectCallMetadataRepository;
import 'package:vm/metadata/inferred_type.dart'
show InferredTypeMetadataRepository;
import 'package:vm/metadata/procedure_attributes.dart'
show ProcedureAttributesMetadataRepository;
final String _usage = '''
Usage: dump_kernel input.dill output.txt
@@ -31,6 +33,7 @@ main(List<String> arguments) async {
// Register VM-specific metadata.
program.addMetadataRepository(new DirectCallMetadataRepository());
program.addMetadataRepository(new InferredTypeMetadataRepository());
program.addMetadataRepository(new ProcedureAttributesMetadataRepository());
final List<int> bytes = new File(input).readAsBytesSync();
new BinaryBuilderWithMetadata(bytes).readProgram(program);
+3
View File
@@ -19,6 +19,8 @@ import 'package:kernel/core_types.dart' show CoreTypes;
import 'transformations/devirtualization.dart' as devirtualization
show transformProgram;
import 'transformations/no_dynamic_invocations_annotator.dart'
as no_dynamic_invocations_annotator show transformProgram;
import 'transformations/type_flow/transformer.dart' as globalTypeFlow
show transformProgram;
@@ -58,6 +60,7 @@ _runGlobalTransformations(Program program, bool strongMode) {
globalTypeFlow.transformProgram(coreTypes, program);
} else {
devirtualization.transformProgram(coreTypes, program);
no_dynamic_invocations_annotator.transformProgram(coreTypes, program);
}
}
}
@@ -0,0 +1,41 @@
// Copyright (c) 2017, 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.
library vm.metadata.procedure_attributes;
import 'package:kernel/ast.dart';
/// Metadata for annotating procedures with various attributes.
class ProcedureAttributesMetadata {
final bool hasDynamicInvocations;
const ProcedureAttributesMetadata({this.hasDynamicInvocations});
const ProcedureAttributesMetadata.noDynamicInvocations()
: hasDynamicInvocations = false;
@override
String toString() => "hasDynamicInvocations:${hasDynamicInvocations}";
}
/// Repository for [ProcedureAttributesMetadata].
class ProcedureAttributesMetadataRepository
extends MetadataRepository<ProcedureAttributesMetadata> {
@override
final String tag = 'vm.procedure-attributes.metadata';
@override
final Map<TreeNode, ProcedureAttributesMetadata> mapping =
<TreeNode, ProcedureAttributesMetadata>{};
@override
void writeToBinary(ProcedureAttributesMetadata metadata, BinarySink sink) {
assert(!metadata.hasDynamicInvocations);
}
@override
ProcedureAttributesMetadata readFromBinary(BinarySource source) {
return const ProcedureAttributesMetadata.noDynamicInvocations();
}
}
@@ -0,0 +1,117 @@
// Copyright (c) 2017, 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.
library vm.transformations.no_dynamic_invocations_annotator;
import 'package:kernel/ast.dart';
import 'package:kernel/core_types.dart' show CoreTypes;
import '../metadata/procedure_attributes.dart';
/// Assumes strong mode and closed world. If a procedure can not be riched
/// via dynamic invocation from anywhere then annotates it with appropriate
/// [ProcedureAttributeMetadata] annotation.
Program transformProgram(CoreTypes coreTypes, Program program) {
new NoDynamicInvocationsAnnotator(program).visitProgram(program);
return program;
}
enum Action { get, set, invoke }
class Selector {
final Action action;
final Name target;
Selector(this.action, this.target);
bool operator ==(other) {
return other is Selector &&
other.action == this.action &&
other.target == this.target;
}
int get hashCode => (action.index * 31) ^ target.hashCode;
@override
String toString() {
switch (action) {
case Action.get:
return 'get:${target}';
case Action.set:
return 'set:${target}';
case Action.invoke:
return '${target}';
}
return '?';
}
}
class NoDynamicInvocationsAnnotator extends RecursiveVisitor<Null> {
final Set<Selector> _dynamicSelectors;
final ProcedureAttributesMetadataRepository _metadata;
NoDynamicInvocationsAnnotator(Program program)
: _dynamicSelectors = DynamicSelectorsCollector.collect(program),
_metadata = new ProcedureAttributesMetadataRepository() {
program.addMetadataRepository(_metadata);
}
@override
visitProcedure(Procedure node) {
if (node.isStatic || node.name.name == 'call') {
return;
}
Selector selector;
if (node.kind == ProcedureKind.Method) {
selector = new Selector(Action.invoke, node.name);
} else if (node.kind == ProcedureKind.Setter) {
selector = new Selector(Action.set, node.name);
} else {
return;
}
if (!_dynamicSelectors.contains(selector)) {
_metadata.mapping[node] =
const ProcedureAttributesMetadata.noDynamicInvocations();
}
}
}
class DynamicSelectorsCollector extends RecursiveVisitor<Null> {
final Set<Selector> dynamicSelectors = new Set<Selector>();
static Set<Selector> collect(Program program) {
final v = new DynamicSelectorsCollector();
v.visitProgram(program);
return v.dynamicSelectors;
}
@override
visitMethodInvocation(MethodInvocation node) {
super.visitMethodInvocation(node);
if (node.dispatchCategory == DispatchCategory.dynamicDispatch) {
dynamicSelectors.add(new Selector(Action.invoke, node.name));
}
}
@override
visitPropertyGet(PropertyGet node) {
super.visitPropertyGet(node);
if (node.dispatchCategory == DispatchCategory.dynamicDispatch) {
dynamicSelectors.add(new Selector(Action.get, node.name));
}
}
@override
visitPropertySet(PropertySet node) {
super.visitPropertySet(node);
if (node.dispatchCategory == DispatchCategory.dynamicDispatch) {
dynamicSelectors.add(new Selector(Action.set, node.name));
}
}
}
@@ -0,0 +1,87 @@
// Copyright (c) 2017, 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.
// VMOptions=--reify-generic-functions
import "package:expect/expect.dart";
// This test tests that AOT compiler does not optimize away necessary
// type checks.
class A {
int _addOneToArgument(int x) => x + 1;
}
abstract class G<T> {
int _addOneToArgument(T x);
}
class B extends A implements G<int> {}
class C {
int _addTwoToArgument(int x) => x + 2;
}
class D {
int _addThreeToArgument(num x) {
return 0;
}
}
class E extends D {
int _addThreeToArgument(covariant int x) {
return x + 3;
}
}
typedef dynamic F0<T>(T val);
typedef U F1<T, U>(T val);
class F<T> {
T fMethod1(F0<T> f, T val) => f(val) as T;
U fMethod2<U>(F1<T, U> f, T val) => f(val);
}
final arr = <Object>[
new B(),
new C(),
new E(),
new D(), // Just to confuse CHA
new F<int>(),
];
int _add42Int(int v) => v + 42;
double _add42Double(double v) => v + 42;
double _add42_0Int(int v) => v + 42.0;
main() {
final b = arr[0] as G<num>;
Expect.equals(1, b._addOneToArgument(0));
Expect.equals(0, b._addOneToArgument(-1));
Expect.throwsTypeError(() => b._addOneToArgument(1.1));
final c = (arr[1] as C);
final tornMethod = c._addTwoToArgument;
Expect.equals(2, c._addTwoToArgument(0));
Expect.equals(0, c._addTwoToArgument(-2));
Expect.throwsTypeError(() => (tornMethod as dynamic)(1.1));
final e = (arr[2] as D);
Expect.equals(3, e._addThreeToArgument(0));
Expect.equals(0, e._addThreeToArgument(-3));
Expect.throwsTypeError(() => e._addThreeToArgument(1.1));
final f = (arr[4] as F<num>);
final torn1 = f.fMethod1 as dynamic;
Expect.equals(43, torn1(_add42Int, 1));
Expect.throwsTypeError(() => torn1(_add42Double, 1));
Expect.throwsTypeError(() => torn1(_add42Int, 1.1));
final torn2 = f.fMethod2 as dynamic;
Expect.equals(43, torn2<int>(_add42Int, 1));
Expect.equals(43.0, torn2<double>(_add42_0Int, 1));
Expect.throwsTypeError(() => torn2<double>(_add42Int, 1));
Expect.throwsTypeError(() => torn2<int>(_add42_0Int, 1));
}
+3
View File
@@ -105,6 +105,9 @@ dart/spawn_shutdown_test: Skip # OOM crash can bring down the OS.
cc/CorelibCompilerStats: Skip
cc/Service_Profile: Skip
[ !$strong ]
dart/callee_side_type_checks_test: SkipByDesign
# Following tests are failing in a weird way on macos/ia32/debug builds
# need to investigate.
[ $arch == ia32 && $mode == debug && $runtime == vm && $system == macos ]
+12 -2
View File
@@ -942,8 +942,18 @@ CompileType ParameterInstr::ComputeType() const {
return CompileType(CompileType::kNonNullable, cid, &type);
}
// TODO(dartbug.com/30480): Figure out how to use parameter types
// without interfering with argument type checks.
if (Isolate::Current()->strong() && FLAG_use_strong_mode_types) {
LocalScope* scope = graph_entry->parsed_function().node_sequence()->scope();
// Note: in catch-blocks we have ParameterInstr for each local variable
// not only for normal parameters.
if (index() < scope->num_variables()) {
LocalVariable* param = scope->VariableAt(index());
if (param->was_type_checked_by_caller()) {
return CompileType::FromAbstractType(param->type(),
CompileType::kNullable);
}
}
}
return CompileType::Dynamic();
}
@@ -566,9 +566,8 @@ void MetadataHelper::SetMetadataMappings(intptr_t mappings_offset,
}
#endif // DEBUG
last_node_offset_ = builder_->data_program_offset_ +
builder_->parsed_function()->function().kernel_offset();
last_mapping_index_ = FindMetadataMapping(last_node_offset_);
last_node_offset_ = kIntptrMax;
last_mapping_index_ = 0;
}
intptr_t MetadataHelper::FindMetadataMapping(intptr_t node_offset) {
@@ -732,6 +731,25 @@ DirectCallMetadata DirectCallMetadataHelper::GetDirectTargetForMethodInvocation(
return DirectCallMetadata(target, check_receiver_for_null);
}
bool ProcedureAttributesMetadataHelper::ReadMetadata(intptr_t node_offset,
bool* has_dynamic_calls) {
intptr_t md_offset = GetNextMetadataPayloadOffset(node_offset);
if (md_offset < 0) {
*has_dynamic_calls = true;
return false;
}
*has_dynamic_calls = false;
return true;
}
ProcedureAttributesMetadata
ProcedureAttributesMetadataHelper::GetProcedureAttributes(
intptr_t node_offset) {
bool has_dynamic_calls = true;
ReadMetadata(node_offset, &has_dynamic_calls);
return ProcedureAttributesMetadata(has_dynamic_calls);
}
InferredTypeMetadata InferredTypeMetadataHelper::GetInferredType(
intptr_t node_offset) {
const intptr_t md_offset = GetNextMetadataPayloadOffset(node_offset);
@@ -803,7 +821,21 @@ ScopeBuildingResult* StreamingScopeBuilder::BuildScopes() {
ActiveTypeParametersScope active_type_params(&active_class_, function, Z);
LocalScope* enclosing_scope = NULL;
if (function.IsLocalFunction()) {
if (function.IsImplicitClosureFunction() && !function.is_static()) {
// Create artificial enclosing scope for the tear-off that contains
// captured receiver value. This ensure that AssertAssignable will correctly
// load instantiator type arguments if they are needed.
Class& klass = Class::Handle(Z, function.Owner());
Type& klass_type = H.GetCanonicalType(klass);
result_->this_variable =
MakeVariable(TokenPosition::kNoSource, TokenPosition::kNoSource,
Symbols::This(), klass_type);
result_->this_variable->set_index(0);
result_->this_variable->set_is_captured();
enclosing_scope = new (Z) LocalScope(NULL, 0, 0);
enclosing_scope->set_context_level(1);
enclosing_scope->AddVariable(result_->this_variable);
} else if (function.IsLocalFunction()) {
enclosing_scope = LocalScope::RestoreOuterScope(
ContextScope::Handle(Z, function.context_scope()));
}
@@ -835,6 +867,9 @@ ScopeBuildingResult* StreamingScopeBuilder::BuildScopes() {
builder_->SetOffset(function.kernel_offset());
FunctionNodeHelper function_node_helper(builder_);
const ProcedureAttributesMetadata attrs =
builder_->procedure_attributes_metadata_helper_.GetProcedureAttributes(
function.kernel_offset());
switch (function.kind()) {
case RawFunction::kClosureFunction:
@@ -904,9 +939,34 @@ ScopeBuildingResult* StreamingScopeBuilder::BuildScopes() {
result_->type_arguments_variable = variable;
}
ParameterTypeCheckMode type_check_mode = kTypeCheckAllParameters;
if (!function.IsImplicitClosureFunction()) {
if (function.is_static()) {
// In static functions we don't check anything.
type_check_mode = kTypeCheckForStaticFunction;
} else if (!attrs.has_dynamic_invocations) {
// If the current function is never a target of a dynamic invocation
// and this parameter is not marked with generic-covariant-impl
// (which means that among all super-interfaces no type parameters
// ever occur at the position of this parameter) then we don't need
// to check this parameter on the callee side, because strong mode
// guarantees that it was checked at the caller side.
type_check_mode = kTypeCheckForNonDynamicallyInvokedMethod;
}
} else {
if (!attrs.has_dynamic_invocations) {
// This is a tear-off of an instance method that can not be reached
// from any dynamic invocation. The method would not check any
// parameters except covariant ones and those annotated with
// generic-covariant-impl. Which means that we have to check
// the rest in the tear-off itself..
type_check_mode = kTypeCheckForTearOffOfNonDynamicallyInvokedMethod;
}
}
// Continue reading FunctionNode:
// read positional_parameters and named_parameters.
AddPositionalAndNamedParameters(pos);
AddPositionalAndNamedParameters(pos, type_check_mode);
// We generate a synthetic body for implicit closure functions - which
// will forward the call to the real function.
@@ -1945,7 +2005,7 @@ void StreamingScopeBuilder::HandleLocalFunction(intptr_t parent_kernel_offset) {
// read positional_parameters and named_parameters.
function_node_helper.ReadUntilExcluding(
FunctionNodeHelper::kPositionalParameters);
AddPositionalAndNamedParameters();
AddPositionalAndNamedParameters(0, kTypeCheckAllParameters);
// "Peek" is now done.
builder_->SetOffset(offset);
@@ -1971,21 +2031,25 @@ void StreamingScopeBuilder::ExitScope(TokenPosition start_position,
scope_ = scope_->parent();
}
void StreamingScopeBuilder::AddPositionalAndNamedParameters(intptr_t pos) {
void StreamingScopeBuilder::AddPositionalAndNamedParameters(
intptr_t pos,
ParameterTypeCheckMode type_check_mode /* = kTypeCheckAllParameters*/) {
// List of positional.
intptr_t list_length = builder_->ReadListLength(); // read list length.
for (intptr_t i = 0; i < list_length; ++i) {
AddVariableDeclarationParameter(pos++); // read ith positional parameter.
AddVariableDeclarationParameter(pos++, type_check_mode);
}
// List of named.
list_length = builder_->ReadListLength(); // read list length.
for (intptr_t i = 0; i < list_length; ++i) {
AddVariableDeclarationParameter(pos++); // read ith named parameter.
AddVariableDeclarationParameter(pos++, type_check_mode);
}
}
void StreamingScopeBuilder::AddVariableDeclarationParameter(intptr_t pos) {
void StreamingScopeBuilder::AddVariableDeclarationParameter(
intptr_t pos,
ParameterTypeCheckMode type_check_mode) {
intptr_t kernel_offset = builder_->ReaderOffset(); // no tag.
VariableDeclarationHelper helper(builder_);
helper.ReadUntilExcluding(VariableDeclarationHelper::kType);
@@ -2002,6 +2066,35 @@ void StreamingScopeBuilder::AddVariableDeclarationParameter(intptr_t pos) {
if (variable->name().raw() == Symbols::IteratorParameter().raw()) {
variable->set_is_forced_stack();
}
const bool is_covariant =
helper.IsGenericCovariantImpl() || helper.IsCovariant();
switch (type_check_mode) {
case kTypeCheckAllParameters:
variable->set_type_check_mode(LocalVariable::kDoTypeCheck);
break;
case kTypeCheckForTearOffOfNonDynamicallyInvokedMethod:
if (is_covariant) {
// Don't type check covariant parameters - they will be checked by
// a function we forward to. Their types however are not known.
variable->set_type_check_mode(LocalVariable::kSkipTypeCheck);
} else {
variable->set_type_check_mode(LocalVariable::kDoTypeCheck);
}
break;
case kTypeCheckForNonDynamicallyInvokedMethod:
if (is_covariant) {
variable->set_type_check_mode(LocalVariable::kDoTypeCheck);
} else {
// Types of non-covariant parameters are guaranteed to match by
// front-end enforcing strong mode types at call site.
variable->set_type_check_mode(LocalVariable::kTypeCheckedByCaller);
}
break;
case kTypeCheckForStaticFunction:
variable->set_type_check_mode(LocalVariable::kTypeCheckedByCaller);
break;
}
scope_->InsertParameterAt(pos, variable);
result_->locals.Insert(builder_->data_program_offset_ + kernel_offset,
variable);
@@ -3980,11 +4073,25 @@ FlowGraph* StreamingFlowGraphBuilder::BuildGraphOfImplicitClosureFunction(
FunctionNodeHelper function_node_helper(this);
function_node_helper.ReadUntilExcluding(FunctionNodeHelper::kTypeParameters);
// Tearoffs of static methods needs to perform arguments checks since static
// methods they forward to don't do it themselves.
if (I->argument_type_checks() && !target.NeedsArgumentTypeChecks(I)) {
AlternativeReadingScope _(reader_);
body += BuildArgumentTypeChecks();
if (I->argument_type_checks()) {
if (!target.NeedsArgumentTypeChecks(I)) {
// Tearoffs of static methods needs to perform arguments checks since
// static methods they forward to don't do it themselves.
AlternativeReadingScope _(reader_);
body += BuildArgumentTypeChecks();
} else {
// Check if target function was annotated with no-dynamic-invocations.
const ProcedureAttributesMetadata attrs =
procedure_attributes_metadata_helper_.GetProcedureAttributes(
target.kernel_offset());
if (!attrs.has_dynamic_invocations) {
// If it was then we might need to build some checks in the
// tear-off.
AlternativeReadingScope _(reader_);
body +=
BuildArgumentTypeChecks(kTypeChecksForNoDynamicInvocationsTearOff);
}
}
}
function_node_helper.ReadUntilExcluding(
@@ -4041,7 +4148,8 @@ FlowGraph* StreamingFlowGraphBuilder::BuildGraphOfImplicitClosureFunction(
flow_graph_builder_->last_used_block_id_, prologue_info);
}
Fragment StreamingFlowGraphBuilder::BuildArgumentTypeChecks() {
Fragment StreamingFlowGraphBuilder::BuildArgumentTypeChecks(
TypeChecksToBuild mode /*= kDefaultTypeChecks*/) {
FunctionNodeHelper function_node_helper(this);
function_node_helper.SetNext(FunctionNodeHelper::kTypeParameters);
const Function& dart_function = parsed_function()->function();
@@ -4060,53 +4168,60 @@ Fragment StreamingFlowGraphBuilder::BuildArgumentTypeChecks() {
}
// Type parameters
intptr_t list_length = ReadListLength();
TypeArguments& forwarding_params = TypeArguments::Handle(Z);
if (forwarding_target != NULL) {
forwarding_params = forwarding_target->type_parameters();
ASSERT(forwarding_params.Length() == list_length);
}
TypeParameter& forwarding_param = TypeParameter::Handle(Z);
for (intptr_t i = 0; i < list_length; ++i) {
ReadFlags(); // skip flags
SkipListOfExpressions(); // skip annotations
String& name = H.DartSymbol(ReadStringReference()); // read name
AbstractType& bound = T.BuildType(); // read bound
if (mode == kDefaultTypeChecks) {
intptr_t num_type_params = ReadListLength();
TypeArguments& forwarding_params = TypeArguments::Handle(Z);
if (forwarding_target != NULL) {
forwarding_param ^= forwarding_params.TypeAt(i);
bound = forwarding_param.bound();
forwarding_params = forwarding_target->type_parameters();
ASSERT(forwarding_params.Length() == num_type_params);
}
TypeParameter& forwarding_param = TypeParameter::Handle(Z);
for (intptr_t i = 0; i < num_type_params; ++i) {
ReadFlags(); // skip flags
SkipListOfExpressions(); // skip annotations
String& name = H.DartSymbol(ReadStringReference()); // read name
AbstractType& bound = T.BuildType(); // read bound
if (I->strong() && !bound.IsObjectType() &&
(I->reify_generic_functions() || dart_function.IsFactory())) {
ASSERT(!bound.IsDynamicType());
TypeParameter& param = TypeParameter::Handle(Z);
if (dart_function.IsFactory()) {
param ^= TypeArguments::Handle(
Class::Handle(dart_function.Owner()).type_parameters())
.TypeAt(i);
} else {
param ^=
TypeArguments::Handle(dart_function.type_parameters()).TypeAt(i);
if (forwarding_target != NULL) {
forwarding_param ^= forwarding_params.TypeAt(i);
bound = forwarding_param.bound();
}
if (I->strong() && !bound.IsObjectType() &&
(I->reify_generic_functions() || dart_function.IsFactory())) {
ASSERT(!bound.IsDynamicType());
TypeParameter& param = TypeParameter::Handle(Z);
if (dart_function.IsFactory()) {
param ^= TypeArguments::Handle(
Class::Handle(dart_function.Owner()).type_parameters())
.TypeAt(i);
} else {
param ^=
TypeArguments::Handle(dart_function.type_parameters()).TypeAt(i);
}
ASSERT(param.IsFinalized());
body += CheckTypeArgumentBound(param, bound, name);
}
ASSERT(param.IsFinalized());
body += CheckTypeArgumentBound(param, bound, name);
}
function_node_helper.SetJustRead(FunctionNodeHelper::kTypeParameters);
}
function_node_helper.SetJustRead(FunctionNodeHelper::kTypeParameters);
function_node_helper.ReadUntilExcluding(
FunctionNodeHelper::kPositionalParameters);
// Positional.
list_length = ReadListLength();
const intptr_t positional_length = list_length;
const intptr_t num_positional_params = ReadListLength();
const intptr_t kFirstParameterOffset = 1;
for (intptr_t i = 0; i < list_length; ++i) {
for (intptr_t i = 0; i < num_positional_params; ++i) {
// ith variable offset.
const intptr_t offset = ReaderOffset();
SkipVariableDeclaration();
LocalVariable* param = LookupVariable(offset + data_program_offset_);
if (!param->needs_type_check()) {
continue;
}
const AbstractType* target_type = &param->type();
if (forwarding_target != NULL) {
// We add 1 to the parameter index to account for the receiver.
@@ -4116,25 +4231,29 @@ Fragment StreamingFlowGraphBuilder::BuildArgumentTypeChecks() {
body += LoadLocal(param);
body += CheckArgumentType(param, *target_type);
body += Drop();
SkipVariableDeclaration(); // read ith variable.
}
// Named.
list_length = ReadListLength();
for (intptr_t i = 0; i < list_length; ++i) {
const intptr_t num_named_params = ReadListLength();
for (intptr_t i = 0; i < num_named_params; ++i) {
// ith variable offset.
LocalVariable* param =
LookupVariable(ReaderOffset() + data_program_offset_);
body += LoadLocal(param);
const intptr_t offset = ReaderOffset();
SkipVariableDeclaration();
LocalVariable* param = LookupVariable(offset + data_program_offset_);
if (!param->needs_type_check()) {
continue;
}
const AbstractType* target_type = &param->type();
if (forwarding_target != NULL) {
// We add 1 to the parameter index to account for the receiver.
target_type = &AbstractType::ZoneHandle(
Z, forwarding_target->ParameterTypeAt(positional_length + i + 1));
Z, forwarding_target->ParameterTypeAt(num_positional_params + i + 1));
}
body += LoadLocal(param);
body += CheckArgumentType(param, *target_type);
body += Drop();
SkipVariableDeclaration(); // read ith variable.
}
return body;
@@ -9584,6 +9703,9 @@ void StreamingFlowGraphBuilder::EnsureMetadataIsScanned() {
const intptr_t kUInt32Size = 4;
Reader reader(H.metadata_mappings());
if (reader.size() == 0) {
return;
}
// Scan through metadata mappings in reverse direction.
@@ -9631,6 +9753,18 @@ void StreamingFlowGraphBuilder::EnsureMetadataIsScanned() {
inferred_type_metadata_helper_.SetMetadataMappings(offset + kUInt32Size,
mappings_num);
}
} else if (H.StringEquals(tag, ProcedureAttributesMetadataHelper::tag())) {
ASSERT(node_references_num == 0);
if (mappings_num > 0) {
if (!FLAG_precompiled_mode) {
FATAL(
"ProcedureAttributesMetadata is allowed in precompiled mode "
"only");
}
procedure_attributes_metadata_helper_.SetMetadataMappings(
offset + kUInt32Size, mappings_num);
}
}
}
}
@@ -590,6 +590,26 @@ class InferredTypeMetadataHelper : public MetadataHelper {
InferredTypeMetadata GetInferredType(intptr_t node_offset);
};
struct ProcedureAttributesMetadata {
explicit ProcedureAttributesMetadata(bool has_dynamic_invocations)
: has_dynamic_invocations(has_dynamic_invocations) {}
const bool has_dynamic_invocations;
};
// Helper class which provides access to direct call metadata.
class ProcedureAttributesMetadataHelper : public MetadataHelper {
public:
static const char* tag() { return "vm.procedure-attributes.metadata"; }
explicit ProcedureAttributesMetadataHelper(StreamingFlowGraphBuilder* builder)
: MetadataHelper(builder) {}
ProcedureAttributesMetadata GetProcedureAttributes(intptr_t node_offset);
private:
bool ReadMetadata(intptr_t node_offset, bool* has_dynamic_invocations);
};
class StreamingDartTypeTranslator {
public:
StreamingDartTypeTranslator(StreamingFlowGraphBuilder* builder,
@@ -692,16 +712,34 @@ class StreamingScopeBuilder {
void EnterScope(intptr_t kernel_offset);
void ExitScope(TokenPosition start_position, TokenPosition end_position);
/**
* This assumes that the reader is at a FunctionNode,
* about to read the positional parameters.
*/
void AddPositionalAndNamedParameters(intptr_t pos = 0);
/**
* This assumes that the reader is at a FunctionNode,
* about to read a parameter (i.e. VariableDeclaration).
*/
void AddVariableDeclarationParameter(intptr_t pos);
// This enum controls which parameters would be marked as requring type
// check on the callee side.
enum ParameterTypeCheckMode {
// All parameters will be checked.
kTypeCheckAllParameters,
// Only parameters marked as covariant or generic-covariant-impl will be
// checked.
kTypeCheckForNonDynamicallyInvokedMethod,
// Only parameters *not* marked as covariant or generic-covariant-impl will
// be checked. The rest would be checked in the method itself.
// Inverse of kTypeCheckOnlyGenericCovariantImplParameters.
kTypeCheckForTearOffOfNonDynamicallyInvokedMethod,
// No parameters will be checked.
kTypeCheckForStaticFunction,
};
// This assumes that the reader is at a FunctionNode,
// about to read the positional parameters.
void AddPositionalAndNamedParameters(intptr_t pos,
ParameterTypeCheckMode type_check_mode);
// This assumes that the reader is at a FunctionNode,
// about to read a parameter (i.e. VariableDeclaration).
void AddVariableDeclarationParameter(intptr_t pos,
ParameterTypeCheckMode type_check_mode);
LocalVariable* MakeVariable(TokenPosition declaration_pos,
TokenPosition token_pos,
@@ -894,6 +932,7 @@ class StreamingFlowGraphBuilder {
record_yield_positions_into_(NULL),
direct_call_metadata_helper_(this),
inferred_type_metadata_helper_(this),
procedure_attributes_metadata_helper_(this),
metadata_scanned_(false) {}
StreamingFlowGraphBuilder(TranslationHelper* translation_helper,
@@ -915,6 +954,7 @@ class StreamingFlowGraphBuilder {
record_yield_positions_into_(NULL),
direct_call_metadata_helper_(this),
inferred_type_metadata_helper_(this),
procedure_attributes_metadata_helper_(this),
metadata_scanned_(false) {}
StreamingFlowGraphBuilder(TranslationHelper* translation_helper,
@@ -936,6 +976,7 @@ class StreamingFlowGraphBuilder {
record_yield_positions_into_(NULL),
direct_call_metadata_helper_(this),
inferred_type_metadata_helper_(this),
procedure_attributes_metadata_helper_(this),
metadata_scanned_(false) {}
~StreamingFlowGraphBuilder() { delete reader_; }
@@ -1126,7 +1167,14 @@ class StreamingFlowGraphBuilder {
const InferredTypeMetadata* result_type = NULL,
intptr_t argument_check_bits = 0,
intptr_t type_argument_check_bits = 0);
Fragment BuildArgumentTypeChecks();
enum TypeChecksToBuild {
kDefaultTypeChecks,
kTypeChecksForNoDynamicInvocationsTearOff
};
Fragment BuildArgumentTypeChecks(TypeChecksToBuild mode = kDefaultTypeChecks);
Fragment ThrowException(TokenPosition position);
Fragment BooleanNegate();
Fragment TranslateInstantiatedTypeArguments(
@@ -1323,12 +1371,14 @@ class StreamingFlowGraphBuilder {
GrowableArray<intptr_t>* record_yield_positions_into_;
DirectCallMetadataHelper direct_call_metadata_helper_;
InferredTypeMetadataHelper inferred_type_metadata_helper_;
ProcedureAttributesMetadataHelper procedure_attributes_metadata_helper_;
bool metadata_scanned_;
friend class ClassHelper;
friend class ConstantHelper;
friend class ConstructorHelper;
friend class DirectCallMetadataHelper;
friend class ProcedureAttributesMetadataHelper;
friend class FieldHelper;
friend class FunctionNodeHelper;
friend class InferredTypeMetadataHelper;
+20
View File
@@ -35,6 +35,7 @@ class LocalVariable : public ZoneAllocated {
is_invisible_(false),
is_captured_parameter_(false),
is_forced_stack_(false),
type_check_mode_(kDoTypeCheck),
index_(LocalVariable::kUninitializedIndex) {
ASSERT(type.IsZoneHandle() || type.IsReadOnlyHandle());
ASSERT(type.IsFinalized());
@@ -65,6 +66,24 @@ class LocalVariable : public ZoneAllocated {
bool is_forced_stack() const { return is_forced_stack_; }
void set_is_forced_stack() { is_forced_stack_ = true; }
enum TypeCheckMode {
kDoTypeCheck,
kSkipTypeCheck,
kTypeCheckedByCaller,
};
// Returns true if this local variable represents a parameter that needs type
// check when we enter the function.
bool needs_type_check() const { return type_check_mode_ == kDoTypeCheck; }
// Returns true if this local variable represents a parameter which type is
// guaranteed by the caller.
bool was_type_checked_by_caller() const {
return type_check_mode_ == kTypeCheckedByCaller;
}
void set_type_check_mode(TypeCheckMode mode) { type_check_mode_ = mode; }
bool HasIndex() const { return index_ != kUninitializedIndex; }
int index() const {
ASSERT(HasIndex());
@@ -124,6 +143,7 @@ class LocalVariable : public ZoneAllocated {
bool is_invisible_;
bool is_captured_parameter_;
bool is_forced_stack_;
TypeCheckMode type_check_mode_;
int index_; // Allocation index in words relative to frame pointer (if not
// captured), or relative to the context pointer (if captured).