[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:
committed by
commit-bot@chromium.org
parent
5ec65552d5
commit
d117760ba6
@@ -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);
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
@@ -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 ]
|
||||
|
||||
@@ -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 = ¶m->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 = ¶m->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;
|
||||
|
||||
@@ -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).
|
||||
|
||||
|
||||
Reference in New Issue
Block a user