Chromium Code Reviews| Index: runtime/vm/flow_graph_optimizer.cc |
| diff --git a/runtime/vm/flow_graph_optimizer.cc b/runtime/vm/flow_graph_optimizer.cc |
| index 1160b9f9bc53148aa4af538e99925cfb3224466e..85e8429630c7f012ae61cb739482d8e1e8b05648 100644 |
| --- a/runtime/vm/flow_graph_optimizer.cc |
| +++ b/runtime/vm/flow_graph_optimizer.cc |
| @@ -44,6 +44,8 @@ DEFINE_FLAG(bool, trace_range_analysis, false, "Trace range analysis progress"); |
| DEFINE_FLAG(bool, truncating_left_shift, true, |
| "Optimize left shift to truncate if possible"); |
| DEFINE_FLAG(bool, use_cha, true, "Use class hierarchy analysis."); |
| +DEFINE_FLAG(bool, trace_integer_ir_selection, false, |
| + "Print integer IR selection optimization pass."); |
| DECLARE_FLAG(bool, enable_type_checks); |
| DECLARE_FLAG(bool, source_lines); |
| DECLARE_FLAG(bool, trace_type_check_elimination); |
| @@ -608,10 +610,12 @@ void FlowGraphOptimizer::InsertConversion(Representation from, |
| const intptr_t deopt_id = (deopt_target != NULL) ? |
| deopt_target->DeoptimizationTarget() : Isolate::kNoDeoptId; |
| converted = new(I) UnboxIntegerInstr(use->CopyWithType(), deopt_id); |
| - |
| } else if ((from == kUnboxedMint) && (to == kTagged)) { |
| converted = new(I) BoxIntegerInstr(use->CopyWithType()); |
| - |
| + } else if ((from == kUnboxedMint) && (to == kUnboxedUint32)) { |
| + converted = new(I) UnboxedIntConverterInstr(from, to, use->CopyWithType()); |
| + } else if ((from == kUnboxedUint32) && (to == kUnboxedMint)) { |
| + converted = new(I) UnboxedIntConverterInstr(from, to, use->CopyWithType()); |
| } else if (from == kUnboxedMint && to == kUnboxedDouble) { |
| ASSERT(CanUnboxDouble()); |
| // Convert by boxing/unboxing. |
| @@ -5147,6 +5151,320 @@ void FlowGraphOptimizer::InferIntRanges() { |
| } |
| +// Replaces Mint IL instructions with Uint32 IL instructions |
| +// when possible. Uses output of RangeAnalysis. |
| +class IntegerInstructionSelector : public ValueObject { |
| + public: |
| + explicit IntegerInstructionSelector(FlowGraph* flow_graph) |
| + : flow_graph_(flow_graph), |
| + isolate_(NULL), |
| + changed_(true) { |
| + ASSERT(flow_graph_ != NULL); |
| + isolate_ = flow_graph_->isolate(); |
| + ASSERT(isolate_ != NULL); |
| + selected_uint32_defs_ = |
| + new(I) BitVector(flow_graph_->current_ssa_temp_index()); |
| + } |
| + |
| + void Select(); |
| + |
| + private: |
| + bool IsPotentialUint32Definition(Definition* def); |
| + void FindPotentialUint32Definitions(); |
| + bool IsInitialUint32Candidate(Definition* def); |
| + void SelectInitialUint32Definitions(); |
| + bool IsUint32Candidate(Definition* def); |
| + void Iterate(); |
| + Definition* ConstructReplacementFor(Definition* def); |
| + void ReplaceInstructions(); |
| + |
| + Isolate* isolate() const { return isolate_; } |
| + |
| + GrowableArray<Definition*> potential_uint32_defs_; |
| + BitVector* selected_uint32_defs_; |
| + |
| + FlowGraph* flow_graph_; |
| + Isolate* isolate_; |
| + bool changed_; |
| +}; |
| + |
| + |
| +void IntegerInstructionSelector::Select() { |
| + if (FLAG_trace_integer_ir_selection) { |
| + OS::Print("---- starting integer ir selection -------\n"); |
| + } |
| + FindPotentialUint32Definitions(); |
| + SelectInitialUint32Definitions(); |
|
Vyacheslav Egorov (Google)
2014/07/07 16:16:03
"InitialDefinition" is a confusing name. I would c
Vyacheslav Egorov (Google)
2014/07/07 16:16:03
"InitialDefinition" is a confusing name. I would c
|
| + Iterate(); |
| + ReplaceInstructions(); |
| + if (FLAG_trace_integer_ir_selection) { |
| + OS::Print("---- after integer ir selection -------\n"); |
| + FlowGraphPrinter printer(*flow_graph_); |
| + printer.PrintBlocks(); |
| + } |
| +} |
| + |
| + |
| +bool IntegerInstructionSelector::IsPotentialUint32Definition(Definition* def) { |
| + if (!def->IsMintDefinition()) { |
| + // Not a mint op. |
| + return false; |
| + } |
| + if (def->IsLoadField()) { |
| + // A load of a mint field. |
| + return false; |
| + } |
| + return true; |
| +} |
| + |
| + |
| +void IntegerInstructionSelector::FindPotentialUint32Definitions() { |
| + if (FLAG_trace_integer_ir_selection) { |
| + OS::Print("++++ Finding potential Uint32 definitions:\n"); |
| + } |
| + const GrowableArray<Definition*>& initial = |
| + *flow_graph_->graph_entry()->initial_definitions(); |
| + for (intptr_t i = 0; i < initial.length(); ++i) { |
| + Definition* current = initial[i]; |
| + if (IsPotentialUint32Definition(current)) { |
| + if (FLAG_trace_integer_ir_selection) { |
| + OS::Print("Adding %s\n", current->ToCString()); |
| + } |
| + potential_uint32_defs_.Add(current); |
| + } |
| + } |
| + |
| + for (BlockIterator block_it = flow_graph_->reverse_postorder_iterator(); |
| + !block_it.Done(); |
| + block_it.Advance()) { |
| + BlockEntryInstr* block = block_it.Current(); |
| + |
| + |
|
Vyacheslav Egorov (Google)
2014/07/07 16:16:03
remove empty line
Cutch
2014/07/07 22:34:28
Done.
|
| + if (block->IsGraphEntry() || block->IsCatchBlockEntry()) { |
| + const GrowableArray<Definition*>& initial = block->IsGraphEntry() |
| + ? *block->AsGraphEntry()->initial_definitions() |
|
Vyacheslav Egorov (Google)
2014/07/07 16:16:03
Did not we already iterate over graph entry above?
Cutch
2014/07/07 22:34:28
Done.
|
| + : *block->AsCatchBlockEntry()->initial_definitions(); |
| + for (intptr_t i = 0; i < initial.length(); ++i) { |
| + Definition* current = initial[i]; |
| + if (IsPotentialUint32Definition(current)) { |
| + if (FLAG_trace_integer_ir_selection) { |
| + OS::Print("Adding %s\n", current->ToCString()); |
| + } |
| + potential_uint32_defs_.Add(current); |
| + } |
| + } |
| + } |
| + |
| + for (ForwardInstructionIterator instr_it(block); |
| + !instr_it.Done(); |
| + instr_it.Advance()) { |
| + Instruction* current = instr_it.Current(); |
| + Definition* defn = current->AsDefinition(); |
| + if ((defn != NULL) && (defn->ssa_temp_index() != -1)) { |
| + if (IsPotentialUint32Definition(defn)) { |
| + if (FLAG_trace_integer_ir_selection) { |
| + OS::Print("Adding %s\n", current->ToCString()); |
| + } |
| + potential_uint32_defs_.Add(defn); |
| + } |
| + } |
| + } |
| + } |
| +} |
| + |
| + |
| +// BinaryMintOp masks and stores into unsigned typed arrays that truncate the |
| +// value into a Uint32 range. |
| +bool IntegerInstructionSelector::IsInitialUint32Candidate(Definition* def) { |
| + if (def->IsBinaryMintOp()) { |
| + BinaryMintOpInstr* op = def->AsBinaryMintOp(); |
| + // Must be a mask operation. |
| + if (op->op_kind() != Token::kBIT_AND) { |
| + return false; |
| + } |
| + Range* range = op->range(); |
| + if ((range == NULL) || |
| + !range->IsWithin(0, static_cast<int64_t>(kMaxUint32))) { |
| + return false; |
| + } |
| + return true; |
| + } |
| + // TODO(johnmccutchan): Add typed array stores. |
| + return false; |
| +} |
| + |
| + |
| +void IntegerInstructionSelector::SelectInitialUint32Definitions() { |
| + ASSERT(selected_uint32_defs_ != NULL); |
| + if (FLAG_trace_integer_ir_selection) { |
| + OS::Print("++++ Selecting Uint32 definitions:\n"); |
| + OS::Print("++++ Initial set:\n"); |
| + } |
| + for (intptr_t i = 0; i < potential_uint32_defs_.length(); i++) { |
| + Definition* defn = potential_uint32_defs_[i]; |
| + if (IsInitialUint32Candidate(defn)) { |
| + if (FLAG_trace_integer_ir_selection) { |
| + OS::Print("Adding %s\n", defn->ToCString()); |
| + } |
| + selected_uint32_defs_->Add(defn->ssa_temp_index()); |
| + } |
| + } |
| +} |
| + |
| + |
| +bool IntegerInstructionSelector::IsUint32Candidate(Definition* def) { |
| + ASSERT(def->IsMintDefinition()); |
| + if (def->IsBoxInteger()) { |
| + // If a BoxInteger's input is a candidate, the box is a candidate. |
| + BoxIntegerInstr* box = def->AsBoxInteger(); |
| + Definition* box_input = box->value()->definition(); |
| + return selected_uint32_defs_->Contains(box_input->ssa_temp_index()); |
| + } |
| + // A right shift with a range outside Uint32 cannot be converted because we |
| + // may need the high bits. |
| + if (def->IsShiftMintOp()) { |
| + ShiftMintOpInstr* op = def->AsShiftMintOp(); |
| + if (op->op_kind() == Token::kSHR) { |
| + Range* range = op->range(); |
| + if ((range == NULL) || |
| + !range->IsWithin(0, static_cast<int64_t>(kMaxUint32))) { |
| + return false; |
| + } |
| + } |
| + } |
| + if (!def->HasUses()) { |
| + // No uses, skip. |
|
Vyacheslav Egorov (Google)
2014/07/07 16:16:03
[sidenote: we should DCE those instructions :)]
|
| + return false; |
| + } |
| + // All uses are candidates. |
| + Value* use = def->input_use_list(); |
|
Vyacheslav Egorov (Google)
2014/07/07 16:16:03
What about environment uses? It is not safe to ign
|
| + bool only_candidate_uses = true; |
| + while ((use != NULL) && only_candidate_uses) { |
| + Instruction* instr = use->instruction(); |
| + if (!instr->IsDefinition()) { |
| + // Not a definition. |
| + only_candidate_uses = false; |
|
Vyacheslav Egorov (Google)
2014/07/07 16:16:03
return false;
Cutch
2014/07/07 22:34:28
Done.
|
| + break; |
| + } |
| + Definition* defn = instr->AsDefinition(); |
| + if (!defn->IsMintDefinition()) { |
| + // Not a mint definition. |
| + only_candidate_uses = false; |
|
Vyacheslav Egorov (Google)
2014/07/07 16:16:03
return false;
Cutch
2014/07/07 22:34:28
Done.
|
| + break; |
| + } |
| + only_candidate_uses = only_candidate_uses && |
|
Vyacheslav Egorov (Google)
2014/07/07 16:16:03
only_candidate_uses are true at this point no need
Cutch
2014/07/07 22:34:28
Done.
|
| + selected_uint32_defs_->Contains(defn->ssa_temp_index()); |
|
Vyacheslav Egorov (Google)
2014/07/07 16:16:03
You can just do:
for (Value* use = def->input_use
Cutch
2014/07/07 22:34:28
Done.
|
| + use = use->next_use(); |
| + } |
| + return only_candidate_uses; |
|
Vyacheslav Egorov (Google)
2014/07/07 16:16:03
return true;
|
| +} |
| + |
| + |
| +void IntegerInstructionSelector::Iterate() { |
|
Vyacheslav Egorov (Google)
2014/07/07 16:16:03
I would call it Propagate().
|
| + ASSERT(selected_uint32_defs_ != NULL); |
| + ASSERT(changed_); |
| + intptr_t iteration = 0; |
| + while (changed_) { |
| + if (FLAG_trace_integer_ir_selection) { |
| + OS::Print("+++ Iteration: %" Pd "\n", iteration++); |
| + } |
| + changed_ = false; |
| + for (intptr_t i = 0; i < potential_uint32_defs_.length(); i++) { |
| + Definition* defn = potential_uint32_defs_[i]; |
| + if (selected_uint32_defs_->Contains(defn->ssa_temp_index())) { |
| + // Already marked as a candidate, skip. |
| + continue; |
| + } |
| + if (defn->IsConstant()) { |
| + // Skip constants. |
| + continue; |
| + } |
| + if (IsUint32Candidate(defn)) { |
| + if (FLAG_trace_integer_ir_selection) { |
| + OS::Print("Adding %s\n", defn->ToCString()); |
| + } |
| + // Found a new candidate. |
| + selected_uint32_defs_->Add(defn->ssa_temp_index()); |
| + // Haven't reached fixed point yet. |
| + changed_ = true; |
| + } |
| + } |
| + } |
| + if (FLAG_trace_integer_ir_selection) { |
| + OS::Print("Reached fixed point\n"); |
| + } |
| +} |
| + |
| + |
| +Definition* IntegerInstructionSelector::ConstructReplacementFor( |
| + Definition* def) { |
| + // Should only see mint definitions. |
| + ASSERT(def->IsMintDefinition()); |
| + // Should not see constant instructions. |
| + ASSERT(!def->IsConstant()); |
| + if (def->IsBinaryMintOp()) { |
| + BinaryMintOpInstr* op = def->AsBinaryMintOp(); |
| + Token::Kind op_kind = op->op_kind(); |
| + Value* left = op->left()->CopyWithType(); |
| + Value* right = op->right()->CopyWithType(); |
| + intptr_t deopt_id = op->DeoptimizationTarget(); |
| + return new(I) BinaryUint32OpInstr(op_kind, left, right, deopt_id); |
| + } else if (def->IsBoxInteger()) { |
| + BoxIntegerInstr* box = def->AsBoxInteger(); |
| + Value* value = box->value()->CopyWithType(); |
| + return new(I) BoxUint32Instr(value); |
| + } else if (def->IsUnboxInteger()) { |
| + UnboxIntegerInstr* unbox = def->AsUnboxInteger(); |
| + Value* value = unbox->value()->CopyWithType(); |
| + intptr_t deopt_id = unbox->deopt_id(); |
| + return new(I) UnboxUint32Instr(value, deopt_id); |
| + } else if (def->IsUnaryMintOp()) { |
| + UnaryMintOpInstr* op = def->AsUnaryMintOp(); |
| + Token::Kind op_kind = op->op_kind(); |
| + Value* value = op->value()->CopyWithType(); |
| + intptr_t deopt_id = op->DeoptimizationTarget(); |
| + return new(I) UnaryUint32OpInstr(op_kind, value, deopt_id); |
| + } else if (def->IsShiftMintOp()) { |
| + ShiftMintOpInstr* op = def->AsShiftMintOp(); |
| + Token::Kind op_kind = op->op_kind(); |
| + Value* left = op->left()->CopyWithType(); |
| + Value* right = op->right()->CopyWithType(); |
| + intptr_t deopt_id = op->DeoptimizationTarget(); |
| + return new(I) ShiftUint32OpInstr(op_kind, left, right, deopt_id); |
| + } |
| + printf("%s\n", def->ToCString()); |
| + UNREACHABLE(); |
| + return NULL; |
| +} |
| + |
| + |
| +void IntegerInstructionSelector::ReplaceInstructions() { |
| + if (FLAG_trace_integer_ir_selection) { |
| + OS::Print("++++ Replacing instructions:\n"); |
| + } |
| + for (intptr_t i = 0; i < potential_uint32_defs_.length(); i++) { |
| + Definition* defn = potential_uint32_defs_[i]; |
| + if (!selected_uint32_defs_->Contains(defn->ssa_temp_index())) { |
| + // Not a candidate. |
| + continue; |
| + } |
| + Definition* replacement = ConstructReplacementFor(defn); |
| + ASSERT(replacement != NULL); |
| + if (FLAG_trace_integer_ir_selection) { |
| + OS::Print("Replacing %s with %s\n", defn->ToCString(), |
| + replacement->ToCString()); |
| + } |
| + defn->ReplaceWith(replacement, NULL); |
| + ASSERT(flow_graph_->VerifyUseLists()); |
| + } |
| +} |
| + |
| +void FlowGraphOptimizer::SelectIntegerInstructions() { |
| + IntegerInstructionSelector iis(flow_graph_); |
| + iis.Select(); |
| +} |
| + |
| + |
| void TryCatchAnalyzer::Optimize(FlowGraph* flow_graph) { |
| // For every catch-block: Iterate over all call instructions inside the |
| // corresponding try-block and figure out for each environment value if it |
| @@ -8912,6 +9230,40 @@ void ConstantPropagator::VisitBoxInt32x4(BoxInt32x4Instr* instr) { |
| } |
| +void ConstantPropagator::VisitBoxUint32(BoxUint32Instr* instr) { |
| + // TODO(kmillikin): Handle box operation. |
| + SetValue(instr, non_constant_); |
| +} |
| + |
| + |
| +void ConstantPropagator::VisitUnboxUint32(UnboxUint32Instr* instr) { |
| + // TODO(kmillikin): Handle unbox operation. |
| + SetValue(instr, non_constant_); |
| +} |
| + |
| + |
| +void ConstantPropagator::VisitUnboxedIntConverter( |
| + UnboxedIntConverterInstr* instr) { |
| + SetValue(instr, non_constant_); |
| +} |
| + |
| + |
| +void ConstantPropagator::VisitBinaryUint32Op(BinaryUint32OpInstr* instr) { |
| + HandleBinaryOp(instr, instr->op_kind(), *instr->left(), *instr->right()); |
|
Vyacheslav Egorov (Google)
2014/07/07 16:16:03
Should result of HandleBinaryOp be truncated to ma
|
| +} |
| + |
| + |
| +void ConstantPropagator::VisitShiftUint32Op(ShiftUint32OpInstr* instr) { |
| + HandleBinaryOp(instr, instr->op_kind(), *instr->left(), *instr->right()); |
| +} |
| + |
| + |
| +void ConstantPropagator::VisitUnaryUint32Op(UnaryUint32OpInstr* instr) { |
| + // TODO(kmillikin): Handle unary operations. |
| + SetValue(instr, non_constant_); |
| +} |
| + |
| + |
| void ConstantPropagator::Analyze() { |
| GraphEntryInstr* entry = graph_->graph_entry(); |
| reachable_->Add(entry->preorder_number()); |