| Index: runtime/vm/flow_graph_optimizer.cc
|
| diff --git a/runtime/vm/flow_graph_optimizer.cc b/runtime/vm/flow_graph_optimizer.cc
|
| index f2d9c90464324fabb0e08c2ee7d3ab8abcefd9f5..2082659b051630055b1c2c4d7852907dd195e586 100644
|
| --- a/runtime/vm/flow_graph_optimizer.cc
|
| +++ b/runtime/vm/flow_graph_optimizer.cc
|
| @@ -812,6 +812,12 @@ static intptr_t MethodKindToCid(MethodRecognizer::Kind kind) {
|
| case MethodRecognizer::kFloat32x4ArraySetIndexed:
|
| return kTypedDataFloat32x4ArrayCid;
|
|
|
| + case MethodRecognizer::kUint8ArrayGetUint8:
|
| + return kTypedDataUint8ArrayCid;
|
| +
|
| + case MethodRecognizer::kExternalUint8ArrayGetUint8:
|
| + return kExternalTypedDataUint8ArrayCid;
|
| +
|
| default:
|
| break;
|
| }
|
| @@ -1074,6 +1080,37 @@ bool FlowGraphOptimizer::TryInlineRecognizedMethod(const Function& target,
|
| value_check = ic_data.AsUnaryClassChecksForArgNr(2);
|
| return InlineSetIndexed(kind, target, call, token_pos,
|
| &ic_data, value_check, entry, last);
|
| + case MethodRecognizer::kByteArrayBaseGetInt8:
|
| + return InlineByteArrayViewLoad(kind, call, kTypedDataInt8ArrayCid,
|
| + ic_data, entry, last);
|
| + case MethodRecognizer::kUint8ArrayGetUint8:
|
| + case MethodRecognizer::kExternalUint8ArrayGetUint8:
|
| + case MethodRecognizer::kByteArrayBaseGetUint8:
|
| + return InlineByteArrayViewLoad(kind, call, kTypedDataUint8ArrayCid,
|
| + ic_data, entry, last);
|
| + case MethodRecognizer::kByteArrayBaseGetInt16:
|
| + return InlineByteArrayViewLoad(kind, call, kTypedDataInt16ArrayCid,
|
| + ic_data, entry, last);
|
| + case MethodRecognizer::kByteArrayBaseGetUint16:
|
| + return InlineByteArrayViewLoad(kind, call, kTypedDataUint16ArrayCid,
|
| + ic_data, entry, last);
|
| + case MethodRecognizer::kByteArrayBaseGetInt32:
|
| + if (!CanUnboxInt32()) return false;
|
| + return InlineByteArrayViewLoad(kind, call, kTypedDataInt32ArrayCid,
|
| + ic_data, entry, last);
|
| + case MethodRecognizer::kByteArrayBaseGetUint32:
|
| + if (!CanUnboxInt32()) return false;
|
| + return InlineByteArrayViewLoad(kind, call, kTypedDataUint32ArrayCid,
|
| + ic_data, entry, last);
|
| + case MethodRecognizer::kByteArrayBaseGetFloat32:
|
| + return InlineByteArrayViewLoad(kind, call, kTypedDataFloat32ArrayCid,
|
| + ic_data, entry, last);
|
| + case MethodRecognizer::kByteArrayBaseGetFloat64:
|
| + return InlineByteArrayViewLoad(kind, call, kTypedDataFloat64ArrayCid,
|
| + ic_data, entry, last);
|
| + case MethodRecognizer::kByteArrayBaseGetFloat32x4:
|
| + return InlineByteArrayViewLoad(kind, call, kTypedDataFloat32x4ArrayCid,
|
| + ic_data, entry, last);
|
| default:
|
| return false;
|
| }
|
| @@ -2025,33 +2062,35 @@ bool FlowGraphOptimizer::TryInlineInstanceMethod(InstanceCallInstr* call) {
|
|
|
| switch (recognized_kind) {
|
| // ByteArray getters.
|
| + case MethodRecognizer::kUint8ArrayGetUint8:
|
| + case MethodRecognizer::kExternalUint8ArrayGetUint8:
|
| case MethodRecognizer::kByteArrayBaseGetInt8:
|
| return BuildByteArrayViewLoad(
|
| - call, class_ids[0], kTypedDataInt8ArrayCid);
|
| + recognized_kind, call, class_ids[0], kTypedDataInt8ArrayCid);
|
| case MethodRecognizer::kByteArrayBaseGetUint8:
|
| return BuildByteArrayViewLoad(
|
| - call, class_ids[0], kTypedDataUint8ArrayCid);
|
| + recognized_kind, call, class_ids[0], kTypedDataUint8ArrayCid);
|
| case MethodRecognizer::kByteArrayBaseGetInt16:
|
| return BuildByteArrayViewLoad(
|
| - call, class_ids[0], kTypedDataInt16ArrayCid);
|
| + recognized_kind, call, class_ids[0], kTypedDataInt16ArrayCid);
|
| case MethodRecognizer::kByteArrayBaseGetUint16:
|
| return BuildByteArrayViewLoad(
|
| - call, class_ids[0], kTypedDataUint16ArrayCid);
|
| + recognized_kind, call, class_ids[0], kTypedDataUint16ArrayCid);
|
| case MethodRecognizer::kByteArrayBaseGetInt32:
|
| return BuildByteArrayViewLoad(
|
| - call, class_ids[0], kTypedDataInt32ArrayCid);
|
| + recognized_kind, call, class_ids[0], kTypedDataInt32ArrayCid);
|
| case MethodRecognizer::kByteArrayBaseGetUint32:
|
| return BuildByteArrayViewLoad(
|
| - call, class_ids[0], kTypedDataUint32ArrayCid);
|
| + recognized_kind, call, class_ids[0], kTypedDataUint32ArrayCid);
|
| case MethodRecognizer::kByteArrayBaseGetFloat32:
|
| return BuildByteArrayViewLoad(
|
| - call, class_ids[0], kTypedDataFloat32ArrayCid);
|
| + recognized_kind, call, class_ids[0], kTypedDataFloat32ArrayCid);
|
| case MethodRecognizer::kByteArrayBaseGetFloat64:
|
| return BuildByteArrayViewLoad(
|
| - call, class_ids[0], kTypedDataFloat64ArrayCid);
|
| + recognized_kind, call, class_ids[0], kTypedDataFloat64ArrayCid);
|
| case MethodRecognizer::kByteArrayBaseGetFloat32x4:
|
| return BuildByteArrayViewLoad(
|
| - call, class_ids[0], kTypedDataFloat32x4ArrayCid);
|
| + recognized_kind, call, class_ids[0], kTypedDataFloat32x4ArrayCid);
|
|
|
| // ByteArray setters.
|
| case MethodRecognizer::kByteArrayBaseSetInt8:
|
| @@ -2449,33 +2488,185 @@ bool FlowGraphOptimizer::TryInlineUint32x4Method(
|
| }
|
|
|
|
|
| -bool FlowGraphOptimizer::BuildByteArrayViewLoad(
|
| - InstanceCallInstr* call,
|
| - intptr_t receiver_cid,
|
| - intptr_t view_cid) {
|
| - if ((view_cid == kTypedDataFloat32x4ArrayCid) && !ShouldInlineSimd()) {
|
| - return false;
|
| +bool FlowGraphOptimizer::InlineByteArrayViewLoad(MethodRecognizer::Kind kind,
|
| + Instruction* call,
|
| + intptr_t view_cid,
|
| + const ICData& ic_data,
|
| + TargetEntryInstr** entry,
|
| + Definition** last) {
|
| + intptr_t array_cid = MethodKindToCid(kind);
|
| + if (array_cid == kIllegalCid) {
|
| + // XXX.
|
| + Function& target = Function::Handle();
|
| + GrowableArray<intptr_t> class_ids;
|
| + ic_data.GetCheckAt(0, &class_ids, &target);
|
| + array_cid = class_ids[0];
|
| }
|
| + ASSERT(array_cid != kIllegalCid);
|
|
|
| Definition* array = call->ArgumentAt(0);
|
| - PrepareByteArrayViewOp(call, receiver_cid, view_cid, &array);
|
| + Definition* index = call->ArgumentAt(1);
|
| + *entry = new TargetEntryInstr(flow_graph()->allocate_block_id(),
|
| + call->GetBlock()->try_index());
|
| + (*entry)->InheritDeoptTarget(call);
|
| + Instruction* cursor = *entry;
|
| +
|
| + array_cid = PrepareInlineByteArrayViewOp(call,
|
| + array_cid,
|
| + view_cid,
|
| + &array,
|
| + index,
|
| + &cursor);
|
|
|
| - // Optimistically build a smi-checked load for Int32 and Uint32
|
| - // loads on ia32 like we do for normal array loads, and only revert to
|
| - // mint case after deoptimizing here.
|
| intptr_t deopt_id = Isolate::kNoDeoptId;
|
| - if ((view_cid == kTypedDataInt32ArrayCid ||
|
| - view_cid == kTypedDataUint32ArrayCid) &&
|
| - call->ic_data()->deopt_reason() == kDeoptUnknown) {
|
| - deopt_id = call->deopt_id();
|
| + if ((array_cid == kTypedDataInt32ArrayCid) ||
|
| + (array_cid == kTypedDataUint32ArrayCid)) {
|
| + // Set deopt_id if we can optimistically assume that the result is Smi.
|
| + // Assume mixed Mint/Smi if this instruction caused deoptimization once.
|
| + deopt_id = (ic_data.deopt_reason() == kDeoptUnknown) ?
|
| + call->deopt_id() : Isolate::kNoDeoptId;
|
| }
|
| - Definition* byte_index = call->ArgumentAt(1);
|
| - LoadIndexedInstr* array_op = new LoadIndexedInstr(new Value(array),
|
| - new Value(byte_index),
|
| - 1, // Index scale.
|
| - view_cid,
|
| - deopt_id);
|
| - ReplaceCall(call, array_op);
|
| +
|
| + *last = new LoadIndexedInstr(new Value(array),
|
| + new Value(index),
|
| + 1,
|
| + array_cid,
|
| + deopt_id);
|
| + flow_graph()->AppendTo(cursor,
|
| + *last,
|
| + deopt_id != Isolate::kNoDeoptId ? call->env() : NULL,
|
| + Definition::kValue);
|
| + return true;
|
| +}
|
| +
|
| +
|
| +intptr_t FlowGraphOptimizer::PrepareInlineByteArrayViewOp(
|
| + Instruction* call,
|
| + intptr_t array_cid,
|
| + intptr_t view_cid,
|
| + Definition** array,
|
| + Definition* byte_index,
|
| + Instruction** cursor) {
|
| + // Insert byte_index smi check.
|
| + *cursor = flow_graph()->AppendTo(*cursor,
|
| + new CheckSmiInstr(new Value(byte_index),
|
| + call->deopt_id()),
|
| + call->env(),
|
| + Definition::kEffect);
|
| +
|
| + const bool is_immutable = true;
|
| + LoadFieldInstr* length =
|
| + new LoadFieldInstr(new Value(*array),
|
| + CheckArrayBoundInstr::LengthOffsetFor(array_cid),
|
| + Type::ZoneHandle(Type::SmiType()),
|
| + is_immutable);
|
| + length->set_result_cid(kSmiCid);
|
| + length->set_recognized_kind(
|
| + LoadFieldInstr::RecognizedKindFromArrayCid(array_cid));
|
| + *cursor = flow_graph()->AppendTo(*cursor,
|
| + length,
|
| + NULL,
|
| + Definition::kValue);
|
| +
|
| + intptr_t element_size = FlowGraphCompiler::ElementSizeFor(array_cid);
|
| + ConstantInstr* bytes_per_element =
|
| + flow_graph()->GetConstant(Smi::Handle(Smi::New(element_size)));
|
| + BinarySmiOpInstr* len_in_bytes =
|
| + new BinarySmiOpInstr(Token::kMUL,
|
| + new Value(length),
|
| + new Value(bytes_per_element),
|
| + call->deopt_id());
|
| + *cursor = flow_graph()->AppendTo(*cursor, len_in_bytes, call->env(),
|
| + Definition::kValue);
|
| +
|
| + ConstantInstr* length_adjustment =
|
| + flow_graph()->GetConstant(Smi::Handle(Smi::New(
|
| + FlowGraphCompiler::ElementSizeFor(view_cid) - 1)));
|
| + // adjusted_length = len_in_bytes - (element_size - 1).
|
| + BinarySmiOpInstr* adjusted_length =
|
| + new BinarySmiOpInstr(Token::kSUB,
|
| + new Value(len_in_bytes),
|
| + new Value(length_adjustment),
|
| + call->deopt_id());
|
| + *cursor = flow_graph()->AppendTo(*cursor, adjusted_length, call->env(),
|
| + Definition::kValue);
|
| +
|
| + // Check adjusted_length > 0.
|
| + ConstantInstr* zero = flow_graph()->GetConstant(Smi::Handle(Smi::New(0)));
|
| + *cursor = flow_graph()->AppendTo(*cursor,
|
| + new CheckArrayBoundInstr(
|
| + new Value(adjusted_length),
|
| + new Value(zero),
|
| + call->deopt_id()),
|
| + call->env(),
|
| + Definition::kEffect);
|
| + // Check 0 <= byte_index < adjusted_length.
|
| + *cursor = flow_graph()->AppendTo(*cursor,
|
| + new CheckArrayBoundInstr(
|
| + new Value(adjusted_length),
|
| + new Value(byte_index),
|
| + call->deopt_id()),
|
| + call->env(),
|
| + Definition::kEffect);
|
| +
|
| + if (RawObject::IsExternalTypedDataClassId(array_cid)) {
|
| + LoadUntaggedInstr* elements =
|
| + new LoadUntaggedInstr(new Value(*array),
|
| + ExternalTypedData::data_offset());
|
| + *cursor = flow_graph()->AppendTo(*cursor,
|
| + elements,
|
| + NULL,
|
| + Definition::kValue);
|
| + *array = elements;
|
| + }
|
| + return array_cid;
|
| +}
|
| +
|
| +
|
| +bool FlowGraphOptimizer::BuildByteArrayViewLoad(MethodRecognizer::Kind kind,
|
| + InstanceCallInstr* call,
|
| + intptr_t receiver_cid,
|
| + intptr_t view_cid) {
|
| + if ((view_cid == kTypedDataFloat32x4ArrayCid) && !ShouldInlineSimd()) {
|
| + return false;
|
| + }
|
| +
|
| + // Check for monomorphic IC data.
|
| + if (!call->HasICData()) return false;
|
| + const ICData& ic_data = ICData::Handle(call->ic_data()->AsUnaryClassChecks());
|
| + if (ic_data.NumberOfChecks() != 1) return false;
|
| + ASSERT(ic_data.HasOneTarget());
|
| + const Function& target = Function::Handle(ic_data.GetTargetAt(0));
|
| + TargetEntryInstr* entry;
|
| + Definition* last;
|
| + if (!TryInlineRecognizedMethod(target,
|
| + call,
|
| + call->token_pos(),
|
| + *call->ic_data(),
|
| + &entry, &last)) {
|
| + return false;
|
| + }
|
| +
|
| + // Insert receiver class check.
|
| + AddReceiverCheck(call);
|
| + // 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.
|
| + call->ReplaceUsesWith(last);
|
| + // Finally insert the sequence other definition in place of this one in the
|
| + // graph.
|
| + call->previous()->LinkTo(entry->next());
|
| + entry->UnuseAllInputs(); // Entry block is not in the graph.
|
| + last->LinkTo(call);
|
| + // Remove through the iterator.
|
| + ASSERT(current_iterator()->Current() == call);
|
| + current_iterator()->RemoveCurrentFromGraph();
|
| + call->set_previous(NULL);
|
| + call->set_next(NULL);
|
| return true;
|
| }
|
|
|
|
|