[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:
committed by
commit-bot@chromium.org
parent
485ad3ba4c
commit
dd2d040ccb
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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()) {
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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) \
|
||||
|
||||
@@ -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
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user