| Index: runtime/vm/flow_graph_optimizer.cc
|
| diff --git a/runtime/vm/flow_graph_optimizer.cc b/runtime/vm/flow_graph_optimizer.cc
|
| index b1996d6a32c90a325371a263382d94ce3d3ffe47..fa5db4ded883864c43848e90aa74406a86314450 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.
|
| @@ -4512,7 +4516,290 @@ bool FlowGraphOptimizer::TryInlineInstanceSetter(InstanceCallInstr* instr,
|
| }
|
|
|
|
|
| -// Range analysis for smi values.
|
| +// 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) {
|
| + 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 IsUint32NarrowingDefinition(Definition* def);
|
| + void FindUint32NarrowingDefinitions();
|
| + bool AllUsesAreUint32Narrowing(Value* list_head);
|
| + bool CanBecomeUint32(Definition* def);
|
| + void Propagate();
|
| + 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_;
|
| +};
|
| +
|
| +
|
| +void IntegerInstructionSelector::Select() {
|
| + if (FLAG_trace_integer_ir_selection) {
|
| + OS::Print("---- starting integer ir selection -------\n");
|
| + }
|
| + FindPotentialUint32Definitions();
|
| + FindUint32NarrowingDefinitions();
|
| + Propagate();
|
| + 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) {
|
| + // TODO(johnmccutchan): Consider Smi operations, to avoid unnecessary tagging
|
| + // & untagged of intermediate results.
|
| + // TODO(johnmccutchan): Consider phis.
|
| + return def->IsBoxInteger() || // BoxMint.
|
| + def->IsUnboxInteger() || // UnboxMint.
|
| + def->IsBinaryMintOp() ||
|
| + def->IsShiftMintOp() ||
|
| + def->IsUnaryMintOp();
|
| +}
|
| +
|
| +
|
| +void IntegerInstructionSelector::FindPotentialUint32Definitions() {
|
| + if (FLAG_trace_integer_ir_selection) {
|
| + OS::Print("++++ Finding potential Uint32 definitions:\n");
|
| + }
|
| +
|
| + for (BlockIterator block_it = flow_graph_->reverse_postorder_iterator();
|
| + !block_it.Done();
|
| + block_it.Advance()) {
|
| + BlockEntryInstr* block = block_it.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::IsUint32NarrowingDefinition(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::FindUint32NarrowingDefinitions() {
|
| + 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 (IsUint32NarrowingDefinition(defn)) {
|
| + if (FLAG_trace_integer_ir_selection) {
|
| + OS::Print("Adding %s\n", defn->ToCString());
|
| + }
|
| + selected_uint32_defs_->Add(defn->ssa_temp_index());
|
| + }
|
| + }
|
| +}
|
| +
|
| +
|
| +bool IntegerInstructionSelector::AllUsesAreUint32Narrowing(Value* list_head) {
|
| + for (Value::Iterator it(list_head);
|
| + !it.Done();
|
| + it.Advance()) {
|
| + Value* use = it.Current();
|
| + Definition* defn = use->instruction()->AsDefinition();
|
| + if ((defn == NULL) ||
|
| + (defn->ssa_temp_index() == -1) ||
|
| + !selected_uint32_defs_->Contains(defn->ssa_temp_index())) {
|
| + return false;
|
| + }
|
| + }
|
| + return true;
|
| +}
|
| +
|
| +
|
| +bool IntegerInstructionSelector::CanBecomeUint32(Definition* def) {
|
| + ASSERT(IsPotentialUint32Definition(def));
|
| + 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 an input outside of Uint32 range cannot be converted
|
| + // because we need the high bits.
|
| + if (def->IsShiftMintOp()) {
|
| + ShiftMintOpInstr* op = def->AsShiftMintOp();
|
| + if (op->op_kind() == Token::kSHR) {
|
| + Definition* shift_input = op->left()->definition();
|
| + ASSERT(shift_input != NULL);
|
| + Range* range = shift_input->range();
|
| + if ((range == NULL) ||
|
| + !range->IsWithin(0, static_cast<int64_t>(kMaxUint32))) {
|
| + return false;
|
| + }
|
| + }
|
| + }
|
| + if (!def->HasUses()) {
|
| + // No uses, skip.
|
| + return false;
|
| + }
|
| + return AllUsesAreUint32Narrowing(def->input_use_list()) &&
|
| + AllUsesAreUint32Narrowing(def->env_use_list());
|
| +}
|
| +
|
| +
|
| +void IntegerInstructionSelector::Propagate() {
|
| + ASSERT(selected_uint32_defs_ != NULL);
|
| + bool changed = true;
|
| + 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 (CanBecomeUint32(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(IsPotentialUint32Definition(def));
|
| + // 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);
|
| + }
|
| + 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();
|
| +}
|
| +
|
| +
|
| +// Range analysis for integer values.
|
| class RangeAnalysis : public ValueObject {
|
| public:
|
| explicit RangeAnalysis(FlowGraph* flow_graph)
|
| @@ -4621,6 +4908,8 @@ void RangeAnalysis::Analyze() {
|
| CollectValues();
|
| InsertConstraints();
|
| InferRanges();
|
| + IntegerInstructionSelector iis(flow_graph_);
|
| + iis.Select();
|
| RemoveConstraints();
|
| }
|
|
|
| @@ -8484,6 +8773,24 @@ void ConstantPropagator::HandleBinaryOp(Definition* instr,
|
| }
|
|
|
|
|
| +void ConstantPropagator::TruncateInteger(Definition* defn, int64_t mask) {
|
| + const Object& value = defn->constant_value();
|
| + if (IsNonConstant(value)) {
|
| + return;
|
| + }
|
| + ASSERT(IsConstant(value));
|
| + if (!value.IsInteger()) {
|
| + return;
|
| + }
|
| + const Integer& value_int = Integer::Cast(value);
|
| + int64_t truncated = value_int.AsInt64Value() & mask;
|
| + Instance& result = Integer::ZoneHandle(I, Integer::New(truncated));
|
| + result = result.CheckAndCanonicalize(NULL);
|
| + ASSERT(!result.IsNull());
|
| + SetValue(defn, result);
|
| +}
|
| +
|
| +
|
| void ConstantPropagator::VisitBinarySmiOp(BinarySmiOpInstr* instr) {
|
| HandleBinaryOp(instr, instr->op_kind(), *instr->left(), *instr->right());
|
| }
|
| @@ -8916,6 +9223,42 @@ 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());
|
| + TruncateInteger(instr, static_cast<int64_t>(0xFFFFFFFF));
|
| +}
|
| +
|
| +
|
| +void ConstantPropagator::VisitShiftUint32Op(ShiftUint32OpInstr* instr) {
|
| + HandleBinaryOp(instr, instr->op_kind(), *instr->left(), *instr->right());
|
| + TruncateInteger(instr, static_cast<int64_t>(0xFFFFFFFF));
|
| +}
|
| +
|
| +
|
| +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());
|
|
|