[vm] Cleanup flags which disable argument type checks and strong mode types

The following options are removed:
--use_strong_mode_types
--experimental_unsafe_mode_use_at_your_own_risk

The following flags and predicates are removed:

Dart_IsolateFlags::unsafe_trust_strong_mode_types
Isolate::argument_type_checks()
Isolate::can_use_strong_mode_types()
Isolate::should_emit_strong_mode_checks()

Also, everything depending on these flags is cleaned up.

Change-Id: I9328009ad5a42ea2173842386d612c465e3ebec1
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/147325
Reviewed-by: Martin Kustermann <kustermann@google.com>
Commit-Queue: Alexander Markov <alexmarkov@google.com>
This commit is contained in:
Alexander Markov
2020-05-08 15:53:09 +00:00
committed by commit-bot@chromium.org
parent 485ad3ba4c
commit dd2d040ccb
28 changed files with 135 additions and 427 deletions
+1 -2
View File
@@ -518,7 +518,7 @@ typedef struct {
* for each part.
*/
#define DART_FLAGS_CURRENT_VERSION (0x0000000b)
#define DART_FLAGS_CURRENT_VERSION (0x0000000c)
typedef struct {
int32_t version;
@@ -528,7 +528,6 @@ typedef struct {
bool obfuscate;
Dart_QualifiedFunctionName* entry_points;
bool load_vmservice_library;
bool unsafe_trust_strong_mode_types;
bool copy_parent_code;
} Dart_IsolateFlags;
@@ -294,7 +294,6 @@ bool AotCallSpecializer::IsSupportedIntOperandForStaticDoubleOp(
Value* AotCallSpecializer::PrepareStaticOpInput(Value* input,
intptr_t cid,
Instruction* call) {
ASSERT(I->can_use_strong_mode_types());
ASSERT((cid == kDoubleCid) || (cid == kMintCid));
if (input->Type()->is_nullable()) {
@@ -368,18 +367,13 @@ static void RefineUseTypes(Definition* instr) {
bool AotCallSpecializer::TryOptimizeInstanceCallUsingStaticTypes(
InstanceCallInstr* instr) {
ASSERT(I->can_use_strong_mode_types());
const Token::Kind op_kind = instr->token_kind();
return TryOptimizeIntegerOperation(instr, op_kind) ||
TryOptimizeDoubleOperation(instr, op_kind);
}
bool AotCallSpecializer::TryOptimizeStaticCallUsingStaticTypes(
StaticCallInstr* instr) {
ASSERT(I->can_use_strong_mode_types());
const String& name = String::Handle(Z, instr->function().name());
const Token::Kind op_kind = MethodTokenRecognizer::RecognizeTokenKind(name);
@@ -472,8 +466,6 @@ bool AotCallSpecializer::TryOptimizeIntegerOperation(TemplateDartCall<0>* instr,
return false;
}
ASSERT(I->can_use_strong_mode_types());
Definition* replacement = NULL;
if (instr->ArgumentCount() == 2) {
Value* left_value = instr->ArgumentValueAt(0);
@@ -801,43 +793,8 @@ void AotCallSpecializer::VisitInstanceCall(InstanceCallInstr* instr) {
const CallTargets& targets = instr->Targets();
const intptr_t receiver_idx = instr->FirstArgIndex();
if (I->can_use_strong_mode_types()) {
// In AOT strong mode, we avoid deopting speculation.
// TODO(ajcbik): replace this with actual analysis phase
// that determines if checks are removed later.
} else if (speculative_policy_->IsAllowedForInlining(instr->deopt_id()) &&
!targets.is_empty()) {
if ((op_kind == Token::kINDEX) && TryReplaceWithIndexedOp(instr)) {
return;
}
if ((op_kind == Token::kASSIGN_INDEX) && TryReplaceWithIndexedOp(instr)) {
return;
}
if ((op_kind == Token::kEQ) && TryReplaceWithEqualityOp(instr, op_kind)) {
return;
}
if (Token::IsRelationalOperator(op_kind) &&
TryReplaceWithRelationalOp(instr, op_kind)) {
return;
}
if (Token::IsBinaryOperator(op_kind) &&
TryReplaceWithBinaryOp(instr, op_kind)) {
return;
}
if (Token::IsUnaryOperator(op_kind) &&
TryReplaceWithUnaryOp(instr, op_kind)) {
return;
}
if (TryInlineInstanceMethod(instr)) {
return;
}
}
if (I->can_use_strong_mode_types() &&
TryOptimizeInstanceCallUsingStaticTypes(instr)) {
if (TryOptimizeInstanceCallUsingStaticTypes(instr)) {
return;
}
+3 -3
View File
@@ -424,8 +424,8 @@ FlowGraph::ToCheck FlowGraph::CheckForInstanceCall(
if (function().IsDynamicFunction() && IsReceiver(receiver->definition())) {
// Call receiver is callee receiver: calling "this.g()" in f().
receiver_class = function().Owner();
} else if (isolate()->can_use_strong_mode_types()) {
// In strong mode, get the receiver's compile type. Note that
} else {
// Get the receiver's compile type. Note that
// we allow nullable types, which may result in just generating
// a null check rather than the more elaborate class check
CompileType* type = receiver->Type();
@@ -1381,7 +1381,7 @@ void FlowGraph::RenameRecursive(
captured_parameters_->Add(index);
}
if ((phi != NULL) && isolate()->can_use_strong_mode_types()) {
if (phi != nullptr) {
// Assign type to Phi if it doesn't have a type yet.
// For a Phi to appear in the local variable it either was placed
// there as incoming value by renaming or it was stored there by
@@ -1309,26 +1309,8 @@ bool FlowGraphCompiler::TryIntrinsifyHelper() {
}
return false;
}
case FunctionLayout::kImplicitSetter: {
if (!isolate()->argument_type_checks()) {
Field& field = Field::Handle(function().accessor_field());
ASSERT(!field.IsNull());
#if defined(DEBUG)
// HACK: Clone the field to ignore assertion in Field::guarded_cid().
// The same reasons as above apply, but we only check if it's dynamic.
field = field.CloneFromOriginal();
#endif
if (field.is_instance() && field.guarded_cid() == kDynamicCid) {
SpecialStatsBegin(CombinedCodeStatistics::kTagIntrinsics);
GenerateSetterIntrinsic(compiler::target::Field::OffsetOf(field));
SpecialStatsEnd(CombinedCodeStatistics::kTagIntrinsics);
return true;
}
return false;
}
case FunctionLayout::kImplicitSetter:
break;
}
#if !defined(TARGET_ARCH_IA32)
case FunctionLayout::kMethodExtractor: {
auto& extracted_method = Function::ZoneHandle(
@@ -1094,7 +1094,6 @@ class FlowGraphCompiler : public ValueObject {
intptr_t type_arguments_field_offset);
void GenerateGetterIntrinsic(intptr_t offset);
void GenerateSetterIntrinsic(intptr_t offset);
// Perform a greedy local register allocation. Consider all registers free.
void AllocateRegistersLocally(Instruction* instr);
@@ -871,20 +871,6 @@ void FlowGraphCompiler::GenerateGetterIntrinsic(intptr_t offset) {
__ Ret();
}
void FlowGraphCompiler::GenerateSetterIntrinsic(intptr_t offset) {
// LR: return address.
// SP+1: receiver.
// SP+0: value.
// Sequence node has one store node and one return NULL node.
__ Comment("Intrinsic Setter");
__ ldr(R0,
compiler::Address(SP, 1 * compiler::target::kWordSize)); // Receiver.
__ ldr(R1, compiler::Address(SP, 0 * compiler::target::kWordSize)); // Value.
__ StoreIntoObjectOffset(R0, offset, R1);
__ LoadObject(R0, Object::null_object());
__ Ret();
}
void FlowGraphCompiler::EmitFrameEntry() {
const Function& function = parsed_function().function();
if (CanOptimizeFunction() && function.IsOptimizable() &&
@@ -832,19 +832,6 @@ void FlowGraphCompiler::GenerateGetterIntrinsic(intptr_t offset) {
__ ret();
}
void FlowGraphCompiler::GenerateSetterIntrinsic(intptr_t offset) {
// LR: return address.
// SP+1: receiver.
// SP+0: value.
// Sequence node has one store node and one return NULL node.
__ Comment("Intrinsic Setter");
__ LoadFromOffset(R0, SP, 1 * kWordSize); // Receiver.
__ LoadFromOffset(R1, SP, 0 * kWordSize); // Value.
__ StoreIntoObjectOffset(R0, offset, R1);
__ LoadObject(R0, Object::null_object());
__ ret();
}
void FlowGraphCompiler::EmitFrameEntry() {
const Function& function = parsed_function().function();
if (CanOptimizeFunction() && function.IsOptimizable() &&
@@ -752,21 +752,6 @@ void FlowGraphCompiler::GenerateGetterIntrinsic(intptr_t offset) {
__ ret();
}
void FlowGraphCompiler::GenerateSetterIntrinsic(intptr_t offset) {
// TOS: return address.
// +1 : value
// +2 : receiver.
// Sequence node has one store node and one return NULL node.
__ Comment("Intrinsic Setter");
__ movl(EAX, compiler::Address(ESP, 2 * kWordSize)); // Receiver.
__ movl(EBX, compiler::Address(ESP, 1 * kWordSize)); // Value.
__ StoreIntoObject(EAX, compiler::FieldAddress(EAX, offset), EBX);
const compiler::Immediate& raw_null =
compiler::Immediate(static_cast<intptr_t>(Object::null()));
__ movl(EAX, raw_null);
__ ret();
}
// NOTE: If the entry code shape changes, ReturnAddressLocator in profiler.cc
// needs to be updated to match.
void FlowGraphCompiler::EmitFrameEntry() {
@@ -837,19 +837,6 @@ void FlowGraphCompiler::GenerateGetterIntrinsic(intptr_t offset) {
__ ret();
}
void FlowGraphCompiler::GenerateSetterIntrinsic(intptr_t offset) {
// TOS: return address.
// +1 : value
// +2 : receiver.
// Sequence node has one store node and one return NULL node.
__ Comment("Intrinsic Setter");
__ movq(RAX, compiler::Address(RSP, 2 * kWordSize)); // Receiver.
__ movq(RBX, compiler::Address(RSP, 1 * kWordSize)); // Value.
__ StoreIntoObject(RAX, compiler::FieldAddress(RAX, offset), RBX);
__ LoadObject(RAX, Object::null_object());
__ ret();
}
// NOTE: If the entry code shape changes, ReturnAddressLocator in profiler.cc
// needs to be updated to match.
void FlowGraphCompiler::EmitFrameEntry() {
+1 -2
View File
@@ -2337,8 +2337,7 @@ Definition* CheckedSmiComparisonInstr::Canonicalize(FlowGraph* flow_graph) {
if ((left_type->ToCid() == kSmiCid) && (right_type->ToCid() == kSmiCid)) {
op_cid = kSmiCid;
} else if (Isolate::Current()->can_use_strong_mode_types() &&
FlowGraphCompiler::SupportsUnboxedInt64() &&
} else if (FlowGraphCompiler::SupportsUnboxedInt64() &&
// TODO(dartbug.com/30480): handle nullable types here
left_type->IsNullableInt() && !left_type->is_nullable() &&
right_type->IsNullableInt() && !right_type->is_nullable()) {
+14 -91
View File
@@ -2535,7 +2535,7 @@ static bool InlineSetIndexed(FlowGraph* flow_graph,
}
Instruction* cursor = *entry;
if (flow_graph->isolate()->argument_type_checks() && !is_unchecked_call &&
if (!is_unchecked_call &&
(kind != MethodRecognizer::kObjectArraySetIndexedUnchecked &&
kind != MethodRecognizer::kGrowableArraySetIndexedUnchecked)) {
// Only type check for the value. A type check for the index is not
@@ -2827,60 +2827,6 @@ static bool InlineLoadClassId(FlowGraph* flow_graph,
return true;
}
// Adds an explicit bounds check for a typed getter/setter.
static void PrepareInlineTypedArrayBoundsCheck(FlowGraph* flow_graph,
Instruction* call,
intptr_t array_cid,
intptr_t view_cid,
Definition* array,
Definition** byte_index,
Instruction** cursor) {
ASSERT(array_cid != kDynamicCid);
LoadFieldInstr* length = new (Z) LoadFieldInstr(
new (Z) Value(array), Slot::GetLengthFieldForArrayCid(array_cid),
call->token_pos());
*cursor = flow_graph->AppendTo(*cursor, length, NULL, FlowGraph::kValue);
intptr_t element_size = compiler::target::Instance::ElementSizeFor(array_cid);
ConstantInstr* bytes_per_element =
flow_graph->GetConstant(Smi::Handle(Z, Smi::New(element_size)));
BinarySmiOpInstr* len_in_bytes = new (Z)
BinarySmiOpInstr(Token::kMUL, new (Z) Value(length),
new (Z) Value(bytes_per_element), call->deopt_id());
*cursor = flow_graph->AppendTo(*cursor, len_in_bytes, call->env(),
FlowGraph::kValue);
// adjusted_length = len_in_bytes - (element_size - 1).
Definition* adjusted_length = len_in_bytes;
intptr_t adjustment =
compiler::target::Instance::ElementSizeFor(view_cid) - 1;
if (adjustment > 0) {
ConstantInstr* length_adjustment =
flow_graph->GetConstant(Smi::Handle(Z, Smi::New(adjustment)));
adjusted_length = new (Z)
BinarySmiOpInstr(Token::kSUB, new (Z) Value(len_in_bytes),
new (Z) Value(length_adjustment), call->deopt_id());
*cursor = flow_graph->AppendTo(*cursor, adjusted_length, call->env(),
FlowGraph::kValue);
}
// Check adjusted_length > 0.
// TODO(ajcbik): this is a synthetic check that cannot
// be directly linked to a use, is that a sign of wrong use?
ConstantInstr* zero = flow_graph->GetConstant(Object::smi_zero());
Definition* check =
flow_graph->CreateCheckBound(adjusted_length, zero, call->deopt_id());
*cursor =
flow_graph->AppendTo(*cursor, check, call->env(), FlowGraph::kValue);
// Check 0 <= byte_index < adjusted_length.
*byte_index = flow_graph->CreateCheckBound(adjusted_length, *byte_index,
call->deopt_id());
*cursor = flow_graph->AppendTo(*cursor, *byte_index, call->env(),
FlowGraph::kValue);
}
// Emits preparatory code for a typed getter/setter.
// Handles three cases:
// (1) dynamic: generates load untagged (internal or external)
@@ -2919,12 +2865,12 @@ static bool InlineByteArrayBaseLoad(FlowGraph* flow_graph,
// Dynamic calls are polymorphic due to:
// (A) extra bounds check computations (length stored in receiver),
// (B) external/internal typed data in receiver.
// For Dart2, both issues are resolved in the inlined code.
// Both issues are resolved in the inlined code.
// All getters that go through InlineByteArrayBaseLoad() have explicit
// bounds checks in all their clients in the library, so we can omit yet
// another inlined bounds check.
if (array_cid == kDynamicCid) {
ASSERT(call->IsStaticCall());
if (!flow_graph->isolate()->can_use_strong_mode_types()) {
return false;
}
}
Definition* array = receiver;
@@ -2935,15 +2881,6 @@ static bool InlineByteArrayBaseLoad(FlowGraph* flow_graph,
(*entry)->InheritDeoptTarget(Z, call);
Instruction* cursor = *entry;
// All getters that go through InlineByteArrayBaseLoad() have explicit
// bounds checks in all their clients in the library, so we can omit yet
// another inlined bounds check when compiling for Dart2 (resolves (A)).
const bool needs_bounds_check =
!flow_graph->isolate()->can_use_strong_mode_types();
if (needs_bounds_check) {
PrepareInlineTypedArrayBoundsCheck(flow_graph, call, array_cid, view_cid,
array, &index, &cursor);
}
// Generates a template for the load, either a dynamic conditional
// that dispatches on external and internal storage, or a single
@@ -2999,12 +2936,12 @@ static bool InlineByteArrayBaseStore(FlowGraph* flow_graph,
// Dynamic calls are polymorphic due to:
// (A) extra bounds check computations (length stored in receiver),
// (B) external/internal typed data in receiver.
// For Dart2, both issues are resolved in the inlined code.
// Both issues are resolved in the inlined code.
// All setters that go through InlineByteArrayBaseLoad() have explicit
// bounds checks in all their clients in the library, so we can omit yet
// another inlined bounds check.
if (array_cid == kDynamicCid) {
ASSERT(call->IsStaticCall());
if (!flow_graph->isolate()->can_use_strong_mode_types()) {
return false;
}
}
Definition* array = receiver;
@@ -3015,16 +2952,6 @@ static bool InlineByteArrayBaseStore(FlowGraph* flow_graph,
(*entry)->InheritDeoptTarget(Z, call);
Instruction* cursor = *entry;
// All setters that go through InlineByteArrayBaseLoad() have explicit
// bounds checks in all their clients in the library, so we can omit yet
// another inlined bounds check when compiling for Dart2 (resolves (A)).
const bool needs_bounds_check =
!flow_graph->isolate()->can_use_strong_mode_types();
if (needs_bounds_check) {
PrepareInlineTypedArrayBoundsCheck(flow_graph, call, array_cid, view_cid,
array, &index, &cursor);
}
// Prepare additional checks. In AOT Dart2, we use an explicit null check and
// non-speculative unboxing for most value types.
Cids* value_check = nullptr;
@@ -3037,8 +2964,7 @@ static bool InlineByteArrayBaseStore(FlowGraph* flow_graph,
case kExternalTypedDataUint8ClampedArrayCid:
case kTypedDataInt16ArrayCid:
case kTypedDataUint16ArrayCid: {
if (CompilerState::Current().is_aot() &&
flow_graph->isolate()->can_use_strong_mode_types()) {
if (CompilerState::Current().is_aot()) {
needs_null_check = true;
} else {
// Check that value is always smi.
@@ -3048,8 +2974,7 @@ static bool InlineByteArrayBaseStore(FlowGraph* flow_graph,
}
case kTypedDataInt32ArrayCid:
case kTypedDataUint32ArrayCid:
if (CompilerState::Current().is_aot() &&
flow_graph->isolate()->can_use_strong_mode_types()) {
if (CompilerState::Current().is_aot()) {
needs_null_check = true;
} else {
// On 64-bit platforms assume that stored value is always a smi.
@@ -3061,8 +2986,7 @@ static bool InlineByteArrayBaseStore(FlowGraph* flow_graph,
case kTypedDataFloat32ArrayCid:
case kTypedDataFloat64ArrayCid: {
// Check that value is always double.
if (CompilerState::Current().is_aot() &&
flow_graph->isolate()->can_use_strong_mode_types()) {
if (CompilerState::Current().is_aot()) {
needs_null_check = true;
} else {
value_check = Cids::CreateMonomorphic(Z, kDoubleCid);
@@ -3082,10 +3006,9 @@ static bool InlineByteArrayBaseStore(FlowGraph* flow_graph,
case kTypedDataInt64ArrayCid:
case kTypedDataUint64ArrayCid:
// StoreIndexedInstr takes unboxed int64, so value is
// checked when unboxing. In AOT Dart2, we use an
// checked when unboxing. In AOT, we use an
// explicit null check and non-speculative unboxing.
needs_null_check = CompilerState::Current().is_aot() &&
flow_graph->isolate()->can_use_strong_mode_types();
needs_null_check = CompilerState::Current().is_aot();
break;
default:
// Array cids are already checked in the caller.
+90 -122
View File
@@ -59,15 +59,13 @@ FlowGraphTypePropagator::FlowGraphTypePropagator(FlowGraph* flow_graph)
types_.Add(NULL);
}
if (Isolate::Current()->argument_type_checks()) {
asserts_ = new ZoneGrowableArray<AssertAssignableInstr*>(
flow_graph->current_ssa_temp_index());
for (intptr_t i = 0; i < flow_graph->current_ssa_temp_index(); i++) {
asserts_->Add(NULL);
}
collected_asserts_ = new ZoneGrowableArray<intptr_t>(10);
asserts_ = new ZoneGrowableArray<AssertAssignableInstr*>(
flow_graph->current_ssa_temp_index());
for (intptr_t i = 0; i < flow_graph->current_ssa_temp_index(); i++) {
asserts_->Add(NULL);
}
collected_asserts_ = new ZoneGrowableArray<intptr_t>(10);
}
void FlowGraphTypePropagator::Propagate() {
@@ -121,13 +119,7 @@ void FlowGraphTypePropagator::PropagateRecursive(BlockEntryInstr* block) {
const intptr_t rollback_point = rollback_.length();
// When having assertions enabled or when running in strong-mode the IR graphs
// can contain [AssertAssignableInstr]s and we therefore enable this
// optimization.
Isolate* isolate = Isolate::Current();
if (isolate->argument_type_checks()) {
StrengthenAsserts(block);
}
StrengthenAsserts(block);
block->Accept(this);
@@ -1096,43 +1088,41 @@ CompileType ParameterInstr::ComputeType() const {
const bool is_unchecked_entry_param =
graph_entry->unchecked_entry() == block_;
if (Isolate::Current()->can_use_strong_mode_types()) {
LocalScope* scope = graph_entry->parsed_function().scope();
// Note: in catch-blocks we have ParameterInstr for each local variable
// not only for normal parameters.
const LocalVariable* param = nullptr;
if (scope != nullptr && (index() < scope->num_variables())) {
param = scope->VariableAt(index());
} else if (index() < function.NumParameters()) {
param = graph_entry->parsed_function().RawParameterVariable(index());
LocalScope* scope = graph_entry->parsed_function().scope();
// Note: in catch-blocks we have ParameterInstr for each local variable
// not only for normal parameters.
const LocalVariable* param = nullptr;
if (scope != nullptr && (index() < scope->num_variables())) {
param = scope->VariableAt(index());
} else if (index() < function.NumParameters()) {
param = graph_entry->parsed_function().RawParameterVariable(index());
}
if (param != nullptr) {
CompileType* inferred_type = NULL;
if (!block_->IsCatchBlockEntry()) {
inferred_type = param->parameter_type();
}
if (param != nullptr) {
CompileType* inferred_type = NULL;
if (!block_->IsCatchBlockEntry()) {
inferred_type = param->parameter_type();
}
// Best bet: use inferred type if it is a concrete class or int.
if ((inferred_type != nullptr) &&
((inferred_type->ToNullableCid() != kDynamicCid) ||
inferred_type->IsNullableInt())) {
TraceStrongModeType(this, inferred_type);
return *inferred_type;
}
// If parameter type was checked by caller, then use Dart type annotation,
// plus non-nullability from inferred type if known.
if (param->was_type_checked_by_caller() ||
(is_unchecked_entry_param &&
!param->is_explicit_covariant_parameter())) {
const bool is_nullable =
(inferred_type == NULL) || inferred_type->is_nullable();
TraceStrongModeType(this, param->type());
return CompileType::FromAbstractType(param->type(), is_nullable);
}
// Last resort: use inferred non-nullability.
if (inferred_type != NULL) {
TraceStrongModeType(this, inferred_type);
return *inferred_type;
}
// Best bet: use inferred type if it is a concrete class or int.
if ((inferred_type != nullptr) &&
((inferred_type->ToNullableCid() != kDynamicCid) ||
inferred_type->IsNullableInt())) {
TraceStrongModeType(this, inferred_type);
return *inferred_type;
}
// If parameter type was checked by caller, then use Dart type annotation,
// plus non-nullability from inferred type if known.
if (param->was_type_checked_by_caller() ||
(is_unchecked_entry_param &&
!param->is_explicit_covariant_parameter())) {
const bool is_nullable =
(inferred_type == NULL) || inferred_type->is_nullable();
TraceStrongModeType(this, param->type());
return CompileType::FromAbstractType(param->type(), is_nullable);
}
// Last resort: use inferred non-nullability.
if (inferred_type != NULL) {
TraceStrongModeType(this, inferred_type);
return *inferred_type;
}
}
@@ -1260,25 +1250,23 @@ CompileType InstanceCallBaseInstr::ComputeType() const {
return *inferred_type;
}
if (Isolate::Current()->can_use_strong_mode_types()) {
const Function& target = interface_target();
if (!target.IsNull()) {
const AbstractType& result_type =
AbstractType::ZoneHandle(target.result_type());
// Currently VM doesn't have enough information to instantiate generic
// result types of interface targets:
// 1. receiver type inferred by the front-end is not passed to VM.
// 2. VM collects type arguments through the chain of superclasses but
// not through implemented interfaces.
// So treat non-instantiated generic types as dynamic to avoid pretending
// the type is known.
// TODO(dartbug.com/30480): instantiate generic result_type
if (result_type.IsInstantiated()) {
TraceStrongModeType(this, result_type);
const bool is_nullable =
(inferred_type == NULL) || inferred_type->is_nullable();
return CompileType::FromAbstractType(result_type, is_nullable);
}
const Function& target = interface_target();
if (!target.IsNull()) {
const AbstractType& result_type =
AbstractType::ZoneHandle(target.result_type());
// Currently VM doesn't have enough information to instantiate generic
// result types of interface targets:
// 1. receiver type inferred by the front-end is not passed to VM.
// 2. VM collects type arguments through the chain of superclasses but
// not through implemented interfaces.
// So treat non-instantiated generic types as dynamic to avoid pretending
// the type is known.
// TODO(dartbug.com/30480): instantiate generic result_type
if (result_type.IsInstantiated()) {
TraceStrongModeType(this, result_type);
const bool is_nullable =
(inferred_type == NULL) || inferred_type->is_nullable();
return CompileType::FromAbstractType(result_type, is_nullable);
}
}
@@ -1336,30 +1324,25 @@ CompileType StaticCallInstr::ComputeType() const {
}
}
if (Isolate::Current()->can_use_strong_mode_types()) {
const AbstractType& result_type =
AbstractType::ZoneHandle(function().result_type());
// TODO(dartbug.com/30480): instantiate generic result_type if possible.
// Also, consider fixing AbstractType::IsSubtypeOf to handle
// non-instantiated types properly.
if (result_type.IsInstantiated()) {
TraceStrongModeType(this, result_type);
is_nullable = is_nullable &&
(inferred_type == nullptr || inferred_type->is_nullable());
return CompileType::FromAbstractType(result_type, is_nullable);
}
const AbstractType& result_type =
AbstractType::ZoneHandle(function().result_type());
// TODO(dartbug.com/30480): instantiate generic result_type if possible.
// Also, consider fixing AbstractType::IsSubtypeOf to handle
// non-instantiated types properly.
if (result_type.IsInstantiated()) {
TraceStrongModeType(this, result_type);
is_nullable = is_nullable &&
(inferred_type == nullptr || inferred_type->is_nullable());
return CompileType::FromAbstractType(result_type, is_nullable);
}
return CompileType::Dynamic();
}
CompileType LoadLocalInstr::ComputeType() const {
if (Isolate::Current()->can_use_strong_mode_types()) {
const AbstractType& local_type = local().type();
TraceStrongModeType(this, local_type);
return CompileType::FromAbstractType(local_type);
}
return CompileType::Dynamic();
const AbstractType& local_type = local().type();
TraceStrongModeType(this, local_type);
return CompileType::FromAbstractType(local_type);
}
CompileType DropTempsInstr::ComputeType() const {
@@ -1385,15 +1368,11 @@ CompileType StringInterpolateInstr::ComputeType() const {
}
CompileType LoadStaticFieldInstr::ComputeType() const {
bool is_nullable = CompileType::kNullable;
intptr_t cid = kDynamicCid;
AbstractType* abstract_type = NULL;
const Field& field = this->StaticField();
if (Isolate::Current()->can_use_strong_mode_types()) {
cid = kIllegalCid; // Abstract type is known, calculate cid lazily.
abstract_type = &AbstractType::ZoneHandle(field.type());
TraceStrongModeType(this, *abstract_type);
}
bool is_nullable = CompileType::kNullable;
intptr_t cid = kIllegalCid; // Abstract type is known, calculate cid lazily.
AbstractType* abstract_type = &AbstractType::ZoneHandle(field.type());
TraceStrongModeType(this, *abstract_type);
ASSERT(field.is_static());
const bool is_initialized = IsFieldInitialized() && !FLAG_fields_may_be_reset;
if (field.is_final() && is_initialized) {
@@ -1448,13 +1427,8 @@ CompileType LoadFieldInstr::ComputeType() const {
return compile_type_cid;
}
const Isolate* isolate = Isolate::Current();
const AbstractType* abstract_type = NULL;
if (isolate->can_use_strong_mode_types() ||
(field_type.IsFunctionType() || field_type.HasTypeClass())) {
abstract_type = &field_type;
TraceStrongModeType(this, *abstract_type);
}
const AbstractType* abstract_type = &field_type;
TraceStrongModeType(this, *abstract_type);
if (compile_type_cid.ToNullableCid() != kDynamicCid) {
abstract_type = nullptr;
@@ -1538,20 +1512,17 @@ CompileType UnaryInt64OpInstr::ComputeType() const {
}
CompileType CheckedSmiOpInstr::ComputeType() const {
if (Isolate::Current()->can_use_strong_mode_types()) {
if (left()->Type()->IsNullableInt() && right()->Type()->IsNullableInt()) {
const AbstractType& abstract_type =
AbstractType::ZoneHandle(Type::IntType());
TraceStrongModeType(this, abstract_type);
return CompileType::FromAbstractType(abstract_type,
CompileType::kNonNullable);
} else {
CompileType* type = call()->Type();
TraceStrongModeType(this, type);
return *type;
}
if (left()->Type()->IsNullableInt() && right()->Type()->IsNullableInt()) {
const AbstractType& abstract_type =
AbstractType::ZoneHandle(Type::IntType());
TraceStrongModeType(this, abstract_type);
return CompileType::FromAbstractType(abstract_type,
CompileType::kNonNullable);
} else {
CompileType* type = call()->Type();
TraceStrongModeType(this, type);
return *type;
}
return CompileType::Dynamic();
}
bool CheckedSmiOpInstr::RecomputeType() {
@@ -1559,12 +1530,9 @@ bool CheckedSmiOpInstr::RecomputeType() {
}
CompileType CheckedSmiComparisonInstr::ComputeType() const {
if (Isolate::Current()->can_use_strong_mode_types()) {
CompileType* type = call()->Type();
TraceStrongModeType(this, type);
return *type;
}
return CompileType::Dynamic();
CompileType* type = call()->Type();
TraceStrongModeType(this, type);
return *type;
}
CompileType BoxIntegerInstr::ComputeType() const {
+3 -7
View File
@@ -261,7 +261,6 @@ void CallSpecializer::AddCheckNull(Value* to_check,
intptr_t deopt_id,
Environment* deopt_environment,
Instruction* insert_before) {
ASSERT(I->can_use_strong_mode_types());
if (to_check->Type()->is_nullable()) {
CheckNullInstr* check_null =
new (Z) CheckNullInstr(to_check->CopyWithType(Z), function_name,
@@ -867,7 +866,7 @@ bool CallSpecializer::TryInlineInstanceSetter(InstanceCallInstr* instr) {
// Build an AssertAssignable if necessary.
const AbstractType& dst_type = AbstractType::ZoneHandle(zone(), field.type());
if (I->argument_type_checks() && !dst_type.IsTopTypeForSubtyping()) {
if (!dst_type.IsTopTypeForSubtyping()) {
// Compute if we need to type check the value. Always type check if
// at a dynamic invocation.
bool needs_check = true;
@@ -1204,7 +1203,6 @@ bool CallSpecializer::TryReplaceInstanceOfWithRangeCheck(
bool CallSpecializer::TryOptimizeInstanceOfUsingStaticTypes(
InstanceCallInstr* call,
const AbstractType& type) {
ASSERT(I->can_use_strong_mode_types());
ASSERT(Token::IsTypeTestOperator(call->token_kind()));
if (!type.IsInstantiated()) {
return false;
@@ -1274,8 +1272,7 @@ void CallSpecializer::ReplaceWithInstanceOf(InstanceCallInstr* call) {
type = AbstractType::Cast(call->ArgumentAt(3)->AsConstant()->value()).raw();
}
if (I->can_use_strong_mode_types() &&
TryOptimizeInstanceOfUsingStaticTypes(call, type)) {
if (TryOptimizeInstanceOfUsingStaticTypes(call, type)) {
return;
}
@@ -1408,8 +1405,7 @@ void CallSpecializer::VisitStaticCall(StaticCallInstr* call) {
}
}
if (I->can_use_strong_mode_types() &&
TryOptimizeStaticCallUsingStaticTypes(call)) {
if (TryOptimizeStaticCallUsingStaticTypes(call)) {
return;
}
}
@@ -830,9 +830,6 @@ JoinEntryInstr* BaseFlowGraphBuilder::BuildThrowNoSuchMethod() {
}
Fragment BaseFlowGraphBuilder::AssertBool(TokenPosition position) {
if (!I->should_emit_strong_mode_checks()) {
return Fragment();
}
Value* value = Pop();
AssertBooleanInstr* instr =
new (Z) AssertBooleanInstr(position, value, GetNextDeoptId());
@@ -1111,10 +1108,6 @@ Fragment BaseFlowGraphBuilder::AssertAssignable(
const AbstractType& dst_type,
const String& dst_name,
AssertAssignableInstr::Kind kind) {
if (!I->should_emit_strong_mode_checks()) {
return Drop() + Drop();
}
Value* function_type_args = Pop();
Value* instantiator_type_args = Pop();
Value* value = Pop();
@@ -2105,7 +2105,7 @@ UncheckedEntryPointStyle BytecodeFlowGraphBuilder::ChooseEntryPointStyle(
const KBCInstr* jump_if_unchecked) {
ASSERT(KernelBytecode::IsJumpIfUncheckedOpcode(jump_if_unchecked));
if (!function().MayHaveUncheckedEntryPoint(isolate())) {
if (!function().MayHaveUncheckedEntryPoint()) {
return UncheckedEntryPointStyle::kNone;
}
@@ -912,7 +912,7 @@ intptr_t BytecodeReaderHelper::ReadConstantPool(const Function& function,
// checked at the entry because the parameter is marked covariant,
// neither of those cases require a dynamic invocation forwarder;
// * we assume that all closures are entered in a checked way.
if (!Field::IsGetterName(name) && I->should_emit_strong_mode_checks() &&
if (!Field::IsGetterName(name) &&
(name.raw() != Symbols::EqualOperator().raw()) &&
(name.raw() != Symbols::Call().raw())) {
name = Function::CreateDynamicInvocationForwarderName(name);
@@ -137,8 +137,7 @@ void BytecodeScopeBuilder::BuildScopes() {
if (needs_expr_temp) {
scope_->AddVariable(parsed_function_->EnsureExpressionTemp());
}
if (parsed_function_->function().MayHaveUncheckedEntryPoint(
parsed_function_->isolate())) {
if (parsed_function_->function().MayHaveUncheckedEntryPoint()) {
scope_->AddVariable(parsed_function_->EnsureEntryPointsTemp());
}
parsed_function_->AllocateVariables();
@@ -763,7 +763,7 @@ void StreamingFlowGraphBuilder::CheckArgumentTypesAsNecessary(
Fragment* explicit_checks,
Fragment* implicit_checks,
Fragment* implicit_redefinitions) {
if (!dart_function.NeedsArgumentTypeChecks(I)) return;
if (!dart_function.NeedsArgumentTypeChecks()) return;
// Check if parent function was annotated with no-dynamic-invocations.
const ProcedureAttributesMetadata attrs =
@@ -903,7 +903,7 @@ UncheckedEntryPointStyle StreamingFlowGraphBuilder::ChooseEntryPointStyle(
const Fragment& every_time_prologue,
const Fragment& type_args_handling) {
ASSERT(!dart_function.IsImplicitClosureFunction());
if (!dart_function.MayHaveUncheckedEntryPoint(I) ||
if (!dart_function.MayHaveUncheckedEntryPoint() ||
implicit_type_checks.is_empty()) {
return UncheckedEntryPointStyle::kNone;
}
@@ -2437,7 +2437,7 @@ Fragment StreamingFlowGraphBuilder::BuildPropertySet(TokenPosition* p) {
const String* mangled_name = &setter_name;
const Function* direct_call_target = &direct_call.target_;
if (I->should_emit_strong_mode_checks() && H.IsRoot(itarget_name)) {
if (H.IsRoot(itarget_name)) {
mangled_name = &String::ZoneHandle(
Z, Function::CreateDynamicInvocationForwarderName(setter_name));
if (!direct_call_target->IsNull()) {
@@ -3014,8 +3014,7 @@ Fragment StreamingFlowGraphBuilder::BuildMethodInvocation(TokenPosition* p) {
// those cases require a dynamic invocation forwarder;
// * we assume that all closures are entered in a checked way.
const Function* direct_call_target = &direct_call.target_;
if (I->should_emit_strong_mode_checks() &&
(name.raw() != Symbols::EqualOperator().raw()) &&
if ((name.raw() != Symbols::EqualOperator().raw()) &&
(name.raw() != Symbols::Call().raw()) && H.IsRoot(itarget_name)) {
mangled_name = &String::ZoneHandle(
Z, Function::CreateDynamicInvocationForwarderName(name));
+4 -12
View File
@@ -1534,9 +1534,6 @@ Fragment FlowGraphBuilder::CheckAssignable(const AbstractType& dst_type,
const String& dst_name,
AssertAssignableInstr::Kind kind) {
Fragment instructions;
if (!I->should_emit_strong_mode_checks()) {
return Fragment();
}
if (!dst_type.IsTopTypeForSubtyping()) {
LocalVariable* top_of_stack = MakeTemporary();
instructions += LoadLocal(top_of_stack);
@@ -1552,10 +1549,6 @@ Fragment FlowGraphBuilder::AssertAssignableLoadTypeArguments(
const AbstractType& dst_type,
const String& dst_name,
AssertAssignableInstr::Kind kind) {
if (!I->should_emit_strong_mode_checks()) {
return Fragment();
}
Fragment instructions;
if (!dst_type.IsInstantiated(kCurrentClass)) {
@@ -1599,7 +1592,6 @@ void FlowGraphBuilder::BuildArgumentTypeChecks(
Fragment* explicit_checks,
Fragment* implicit_checks,
Fragment* implicit_redefinitions) {
if (!I->should_emit_strong_mode_checks()) return;
const Function& dart_function = parsed_function_->function();
const Function* forwarding_target = nullptr;
@@ -2033,7 +2025,7 @@ FlowGraph* FlowGraphBuilder::BuildGraphOfNoSuchMethodForwarder(
}
}
if (function.NeedsArgumentTypeChecks(I)) {
if (function.NeedsArgumentTypeChecks()) {
BuildArgumentTypeChecks(TypeChecksToBuild::kCheckAllTypeParameterBounds,
&body, &body, nullptr);
}
@@ -2402,7 +2394,7 @@ FlowGraph* FlowGraphBuilder::BuildGraphOfImplicitClosureFunction(
// We're going to throw away the explicit checks because the target will
// always check them.
Fragment implicit_checks;
if (function.NeedsArgumentTypeChecks(I)) {
if (function.NeedsArgumentTypeChecks()) {
Fragment explicit_checks_unused;
if (target.is_static()) {
// Tearoffs of static methods needs to perform arguments checks since
@@ -2460,7 +2452,7 @@ FlowGraph* FlowGraphBuilder::BuildGraphOfImplicitClosureFunction(
// Setup multiple entrypoints if useful.
FunctionEntryInstr* extra_entry = nullptr;
if (function.MayHaveUncheckedEntryPoint(I)) {
if (function.MayHaveUncheckedEntryPoint()) {
// The prologue for a closure will always have context handling (e.g.
// setting up the receiver variable), but we don't need it on the unchecked
// entry because the only time we reference this is for loading the
@@ -2538,7 +2530,7 @@ FlowGraph* FlowGraphBuilder::BuildGraphOfFieldAccessor(
body += LoadLocal(parsed_function_->ParameterVariable(0));
}
body += LoadLocal(setter_value);
if (I->argument_type_checks() && setter_value->needs_type_check()) {
if (setter_value->needs_type_check()) {
body += CheckAssignable(setter_value->type(), setter_value->name(),
AssertAssignableInstr::kParameterCheck);
}
@@ -430,7 +430,7 @@ ScopeBuildingResult* ScopeBuilder::BuildScopes() {
if (needs_expr_temp_) {
scope_->AddVariable(parsed_function_->EnsureExpressionTemp());
}
if (parsed_function_->function().MayHaveUncheckedEntryPoint(I)) {
if (parsed_function_->function().MayHaveUncheckedEntryPoint()) {
scope_->AddVariable(parsed_function_->EnsureEntryPointsTemp());
}
parsed_function_->AllocateVariables();
-16
View File
@@ -748,14 +748,6 @@ bool GraphIntrinsifier::Build_GrowableArrayGetIndexed(FlowGraph* flow_graph) {
return true;
}
bool GraphIntrinsifier::Build_ObjectArraySetIndexed(FlowGraph* flow_graph) {
if (Isolate::Current()->argument_type_checks()) {
return false;
}
return Build_ObjectArraySetIndexedUnchecked(flow_graph);
}
bool GraphIntrinsifier::Build_ObjectArraySetIndexedUnchecked(
FlowGraph* flow_graph) {
GraphEntryInstr* graph_entry = flow_graph->graph_entry();
@@ -780,14 +772,6 @@ bool GraphIntrinsifier::Build_ObjectArraySetIndexedUnchecked(
return true;
}
bool GraphIntrinsifier::Build_GrowableArraySetIndexed(FlowGraph* flow_graph) {
if (Isolate::Current()->argument_type_checks()) {
return false;
}
return Build_GrowableArraySetIndexedUnchecked(flow_graph);
}
bool GraphIntrinsifier::Build_GrowableArraySetIndexedUnchecked(
FlowGraph* flow_graph) {
GraphEntryInstr* graph_entry = flow_graph->graph_entry();
@@ -17,6 +17,8 @@ namespace dart {
V(Object, Object., ObjectConstructor, 0x8f3ae7ea) \
V(List, ., ListFactory, 0xdf9970a9) \
V(_List, ., ObjectArrayAllocate, 0x03ddbd3a) \
V(_List, []=, ObjectArraySetIndexed, 0x4d5e74cf) \
V(_GrowableList, []=, GrowableArraySetIndexed, 0x4d5e74cf) \
V(_TypedList, _getInt8, ByteArrayBaseGetInt8, 0xa24c2704) \
V(_TypedList, _getUint8, ByteArrayBaseGetUint8, 0xa491df3e) \
V(_TypedList, _getInt16, ByteArrayBaseGetInt16, 0xb65ae1fc) \
@@ -347,7 +349,6 @@ namespace dart {
#define GRAPH_CORE_INTRINSICS_LIST(V) \
V(_List, get:length, ObjectArrayLength, 0x05176aac) \
V(_List, [], ObjectArrayGetIndexed, 0x7e13418e) \
V(_List, []=, ObjectArraySetIndexed, 0x4d5e74cf) \
V(_List, _setIndexed, ObjectArraySetIndexedUnchecked, 0x91b2c203) \
V(_ImmutableList, get:length, ImmutableArrayLength, 0x05176aac) \
V(_ImmutableList, [], ImmutableArrayGetIndexed, 0x7e13418e) \
@@ -356,7 +357,6 @@ namespace dart {
V(_GrowableList, _setData, GrowableArraySetData, 0x9e2350fe) \
V(_GrowableList, _setLength, GrowableArraySetLength, 0x8d94d91d) \
V(_GrowableList, [], GrowableArrayGetIndexed, 0x7e13418e) \
V(_GrowableList, []=, GrowableArraySetIndexed, 0x4d5e74cf) \
V(_GrowableList, _setIndexed, GrowableArraySetIndexedUnchecked, 0x91b2c203) \
V(_StringBase, get:length, StringBaseLength, 0x05176aac) \
V(_OneByteString, codeUnitAt, OneByteStringCodeUnitAt, 0xb0959953) \
-4
View File
@@ -85,9 +85,6 @@ constexpr bool kDartUseBackgroundCompilation = true;
#define FLAG_LIST(P, R, C, D) \
VM_GLOBAL_FLAG_LIST(P, R, C, D) \
DISASSEMBLE_FLAGS(P, R, C, D) \
P(experimental_unsafe_mode_use_at_your_own_risk, bool, false, \
"Omit runtime strong mode type checks and disable optimizations based on " \
"types.") \
P(abort_on_oom, bool, false, \
"Abort if memory allocation fails - use only with --old-gen-heap-size") \
C(async_debugger, false, false, bool, true, \
@@ -233,7 +230,6 @@ constexpr bool kDartUseBackgroundCompilation = true;
"Use class hierarchy analysis even if it can cause deoptimization.") \
P(use_field_guards, bool, true, "Use field guards and track field types") \
C(use_osr, false, true, bool, true, "Use OSR") \
P(use_strong_mode_types, bool, true, "Optimize based on strong mode types.") \
R(verbose_gc, false, bool, false, "Enables verbose GC.") \
R(verbose_gc_hdr, 40, int, 40, "Print verbose GC header interval.") \
R(verify_after_gc, false, bool, false, \
+2 -18
View File
@@ -164,10 +164,7 @@ typedef FixedCache<intptr_t, CatchEntryMovesRefPtr, 16> CatchEntryMovesCache;
V(NONPRODUCT, use_field_guards, UseFieldGuards, use_field_guards, \
FLAG_use_field_guards) \
V(NONPRODUCT, use_osr, UseOsr, use_osr, FLAG_use_osr) \
V(PRECOMPILER, obfuscate, Obfuscate, obfuscate, false_by_default) \
V(PRODUCT, unsafe_trust_strong_mode_types, UnsafeTrustStrongModeTypes, \
unsafe_trust_strong_mode_types, \
FLAG_experimental_unsafe_mode_use_at_your_own_risk)
V(PRECOMPILER, obfuscate, Obfuscate, obfuscate, false_by_default)
// Represents the information used for spawning the first isolate within an
// isolate group.
@@ -1144,10 +1141,6 @@ class Isolate : public BaseIsolate, public IntrusiveDListEntry<Isolate> {
isolate_flags_ = IsKernelIsolateBit::update(value, isolate_flags_);
}
bool can_use_strong_mode_types() const {
return FLAG_use_strong_mode_types && !unsafe_trust_strong_mode_types();
}
// Whether it's possible for unoptimized code to optimize immediately on entry
// (can happen with random or very low optimization counter thresholds)
bool CanOptimizeImmediately() const {
@@ -1230,14 +1223,6 @@ class Isolate : public BaseIsolate, public IntrusiveDListEntry<Isolate> {
isolate_flags_ = NullSafetyBit::update(null_safety, isolate_flags_);
}
// Convenience flag tester indicating whether incoming function arguments
// should be type checked.
bool argument_type_checks() const { return should_emit_strong_mode_checks(); }
bool should_emit_strong_mode_checks() const {
return !unsafe_trust_strong_mode_types();
}
bool has_attempted_stepping() const {
return HasAttemptedSteppingBit::decode(isolate_flags_);
}
@@ -1400,8 +1385,7 @@ class Isolate : public BaseIsolate, public IntrusiveDListEntry<Isolate> {
V(Obfuscate) \
V(ShouldLoadVmService) \
V(NullSafety) \
V(NullSafetySet) \
V(UnsafeTrustStrongModeTypes)
V(NullSafetySet)
// Isolate specific flags.
enum FlagBits {
-1
View File
@@ -86,7 +86,6 @@ class RunKernelTask : public ThreadPool::Task {
Dart_IsolateFlags api_flags;
Isolate::FlagsInitialize(&api_flags);
api_flags.enable_asserts = false;
api_flags.unsafe_trust_strong_mode_types = false;
#if !defined(DART_PRECOMPILER)
api_flags.use_field_guards = true;
#endif
+2 -2
View File
@@ -9180,9 +9180,9 @@ bool Function::NeedsMonomorphicCheckedEntry(Zone* zone) const {
#endif
}
bool Function::MayHaveUncheckedEntryPoint(Isolate* I) const {
bool Function::MayHaveUncheckedEntryPoint() const {
return FLAG_enable_multiple_entrypoints &&
(NeedsArgumentTypeChecks(I) || IsImplicitClosureFunction());
(NeedsArgumentTypeChecks() || IsImplicitClosureFunction());
}
const char* Function::ToCString() const {
+2 -5
View File
@@ -2854,17 +2854,14 @@ class Function : public Object {
}
bool IsInFactoryScope() const;
bool NeedsArgumentTypeChecks(Isolate* I) const {
if (!I->should_emit_strong_mode_checks()) {
return false;
}
bool NeedsArgumentTypeChecks() const {
return IsClosureFunction() ||
!(is_static() || (kind() == FunctionLayout::kConstructor));
}
bool NeedsMonomorphicCheckedEntry(Zone* zone) const;
bool MayHaveUncheckedEntryPoint(Isolate* I) const;
bool MayHaveUncheckedEntryPoint() const;
TokenPosition token_pos() const {
#if defined(DART_PRECOMPILED_RUNTIME)
+1 -4
View File
@@ -151,10 +151,7 @@ class LocalVariable : public ZoneAllocated {
// 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) &&
Isolate::Current()->should_emit_strong_mode_checks();
}
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.