Chromium Code Reviews| Index: runtime/vm/aot_optimizer.cc |
| diff --git a/runtime/vm/aot_optimizer.cc b/runtime/vm/aot_optimizer.cc |
| index 2dc0be48ad8acfaa668b5c703f871ef0bbf3de88..9b39040428ae8ae28171bc70f161c2898df0f3c4 100644 |
| --- a/runtime/vm/aot_optimizer.cc |
| +++ b/runtime/vm/aot_optimizer.cc |
| @@ -37,6 +37,8 @@ DEFINE_FLAG(int, max_exhaustive_polymorphic_checks, 5, |
| #define I (isolate()) |
| #define Z (zone()) |
| +#ifdef DART_PRECOMPILER |
| + |
| static bool ShouldInlineSimd() { |
| return FlowGraphCompiler::SupportsUnboxedSimd128(); |
| } |
| @@ -414,6 +416,52 @@ void AotOptimizer::ReplaceCall(Definition* call, |
| } |
| +void AotOptimizer::ReplaceCallWithSubgraph(Definition* call, |
| + TargetEntryInstr* entry, |
| + Definition* last) { |
| + // We are splitting block A into a subgraph starting at A and ending at B. |
| + // Give the original block id to B to maintain the order of phi inputs at its |
| + // successors consistent with block ids. |
| + BlockEntryInstr* a = call->GetBlock(); |
| + BlockEntryInstr* b = last->GetBlock(); |
| + intptr_t block_id_temp = a->block_id(); |
| + a->set_block_id(b->block_id()); |
| + b->set_block_id(block_id_temp); |
| + |
| + // Remove the original push arguments. |
| + for (intptr_t i = 0; i < call->ArgumentCount(); ++i) { |
| + PushArgumentInstr* push = call->PushArgumentAt(i); |
| + push->ReplaceUsesWith(push->value()->definition()); |
| + push->RemoveFromGraph(); |
| + } |
| + // Replace all uses of this definition with the result. |
| + if (call->HasUses()) { |
| + call->ReplaceUsesWith(last); |
| + } |
| + // Finally insert the sequence other definition in place of this one in the |
| + // graph. |
| + if (entry->next() != NULL) { |
| + call->previous()->LinkTo(entry->next()); |
| + } |
| + entry->UnuseAllInputs(); // Entry block is not in the graph. |
| + if (last != NULL) { |
| + if (last->IsPhi()) { |
| + last->AsPhi()->block()->LinkTo(call); |
| + } else { |
| + last->LinkTo(call); |
| + } |
| + } |
| + call->RemoveFromGraph(); |
| + call->set_previous(NULL); |
| + call->set_next(NULL); |
| + |
| + // Discover new predecessors and recompute dominators. |
| + flow_graph()->DiscoverBlocks(); |
| + GrowableArray<BitVector*> dominance_frontier; |
| + flow_graph()->ComputeDominators(&dominance_frontier); |
| +} |
| + |
| + |
| void AotOptimizer::AddCheckSmi(Definition* to_check, |
| intptr_t deopt_id, |
| Environment* deopt_environment, |
| @@ -1540,6 +1588,14 @@ void AotOptimizer::ReplaceWithInstanceOf(InstanceCallInstr* call) { |
| return; |
| } |
| + TypeRangeCache* cache = thread()->type_range_cache(); |
| + intptr_t lower_limit, upper_limit; |
| + if (cache != NULL && |
| + cache->InstanceOfHasClassRange(type, &lower_limit, &upper_limit)) { |
| + ReplaceWithClassRangeCheck(call, left, negate, lower_limit, upper_limit); |
| + return; |
| + } |
| + |
| const ICData& unary_checks = |
| ICData::ZoneHandle(Z, call->ic_data()->AsUnaryClassChecks()); |
| if ((unary_checks.NumberOfChecks() > 0) && |
| @@ -1576,6 +1632,119 @@ void AotOptimizer::ReplaceWithInstanceOf(InstanceCallInstr* call) { |
| } |
| +void AotOptimizer::ReplaceWithClassRangeCheck(InstanceCallInstr* call, |
|
Florian Schneider
2016/09/07 16:35:57
I wonder if it would be easier to replace the call
rmacnak
2016/09/07 22:21:27
Yes, I like that better.
|
| + Definition* left, |
| + bool negate, |
| + intptr_t lower_limit, |
| + intptr_t upper_limit) { |
| + // left.instanceof(type) => |
| + // t = left.cid, |
| + // t < lower_limit ? negate : (t > upper_limit ? negate, !negate) |
| + TargetEntryInstr* entry = |
| + new(Z) TargetEntryInstr(flow_graph()->allocate_block_id(), |
| + call->GetBlock()->try_index()); |
| + entry->InheritDeoptTarget(Z, call); |
| + TargetEntryInstr* check_upper = |
| + new(Z) TargetEntryInstr(flow_graph()->allocate_block_id(), |
| + call->GetBlock()->try_index()); |
| + check_upper->InheritDeoptTarget(Z, call); |
| + TargetEntryInstr* too_low = |
| + new(Z) TargetEntryInstr(flow_graph()->allocate_block_id(), |
| + call->GetBlock()->try_index()); |
| + too_low->InheritDeoptTarget(Z, call); |
| + TargetEntryInstr* too_high = |
| + new(Z) TargetEntryInstr(flow_graph()->allocate_block_id(), |
| + call->GetBlock()->try_index()); |
| + too_high->InheritDeoptTarget(Z, call); |
| + TargetEntryInstr* match = |
| + new(Z) TargetEntryInstr(flow_graph()->allocate_block_id(), |
| + call->GetBlock()->try_index()); |
| + match->InheritDeoptTarget(Z, call); |
| + JoinEntryInstr* result = |
| + new(Z) JoinEntryInstr(flow_graph()->allocate_block_id(), |
| + call->GetBlock()->try_index()); |
| + result->InheritDeoptTargetAfter(flow_graph(), call, NULL); |
| + |
| + LoadClassIdInstr* left_cid = new(Z) LoadClassIdInstr(new(Z) Value(left)); |
| + ConstantInstr* lower_cid = |
| + flow_graph()->GetConstant(Smi::Handle(Z, Smi::New(lower_limit))); |
| + RelationalOpInstr* compare_lower = |
| + new(Z) RelationalOpInstr(call->token_pos(), |
| + Token::kLT, |
| + new(Z) Value(left_cid), |
| + new(Z) Value(lower_cid), |
| + kSmiCid, |
| + call->deopt_id()); |
| + BranchInstr* branch_lower = new(Z) BranchInstr(compare_lower); |
| + flow_graph()->AppendTo(entry, |
| + left_cid, |
| + call->env(), |
| + FlowGraph::kValue); |
| + flow_graph()->AppendTo(left_cid, |
| + branch_lower, |
| + call->env(), |
| + FlowGraph::kEffect); |
| + |
| + ConstantInstr* upper_cid = |
| + flow_graph()->GetConstant(Smi::Handle(Z, Smi::New(upper_limit))); |
| + RelationalOpInstr* compare_upper = |
| + new(Z) RelationalOpInstr(call->token_pos(), |
| + Token::kGT, |
| + new(Z) Value(left_cid), |
| + new(Z) Value(upper_cid), |
| + kSmiCid, |
| + call->deopt_id()); |
| + BranchInstr* branch_upper = new(Z) BranchInstr(compare_upper); |
| + flow_graph()->AppendTo(check_upper, |
| + branch_upper, |
| + call->env(), |
| + FlowGraph::kEffect); |
| + |
| + *branch_lower->true_successor_address() = too_low; |
| + *branch_lower->false_successor_address() = check_upper; |
| + |
| + *branch_upper->true_successor_address() = too_high; |
| + *branch_upper->false_successor_address() = match; |
| + |
| + flow_graph()->AppendTo(too_low, |
| + new(Z) GotoInstr(result), |
| + call->env(), |
| + FlowGraph::kEffect); |
| + flow_graph()->AppendTo(too_high, |
| + new(Z) GotoInstr(result), |
| + call->env(), |
| + FlowGraph::kEffect); |
| + flow_graph()->AppendTo(match, |
| + new(Z) GotoInstr(result), |
| + call->env(), |
| + FlowGraph::kEffect); |
| + |
| + PhiInstr* result_phi = new(Z) PhiInstr(result, 3); |
| + flow_graph()->AllocateSSAIndexes(result_phi); |
| + result_phi->mark_alive(); |
| + |
| + // Discovers predecessors for the 'result' block. |
| + ReplaceCallWithSubgraph(call, entry, result_phi); |
| + |
| + Value* v; |
| + v = new(Z) Value(flow_graph()->GetConstant(Bool::Get(negate))); |
| + v->definition()->AddInputUse(v); |
| + result_phi->SetInputAt(result->IndexOfPredecessor(too_low), v); |
| + |
| + v = new(Z) Value(flow_graph()->GetConstant(Bool::Get(negate))); |
| + v->definition()->AddInputUse(v); |
| + result_phi->SetInputAt(result->IndexOfPredecessor(too_high), v); |
| + |
| + v = new(Z) Value(flow_graph()->GetConstant(Bool::Get(!negate))); |
| + v->definition()->AddInputUse(v); |
| + result_phi->SetInputAt(result->IndexOfPredecessor(match), v); |
| + |
| + result->InsertPhi(result_phi); |
| + |
| + flow_graph()->VerifyUseLists(); |
| +} |
| + |
| + |
| // TODO(srdjan): Apply optimizations as in ReplaceWithInstanceOf (TestCids). |
| void AotOptimizer::ReplaceWithTypeCast(InstanceCallInstr* call) { |
| ASSERT(Token::IsTypeCastOperator(call->token_kind())); |
| @@ -2138,5 +2307,6 @@ void AotOptimizer::ReplaceArrayBoundChecks() { |
| } |
| } |
| +#endif // DART_PRECOMPILER |
| } // namespace dart |