VM: Optimized code for all of [External]{One|Two}ByteString::codeUnitAt.
Added support for external string using flow graph based intrinsics
which helps with precompiled code, but also polymorphic calls in jitted code.
I also added support for the missing cases in the flow graph optimizer.
BUG=
R=vegorov@google.com
Review URL: https://codereview.chromium.org/1961393002 .
diff --git a/runtime/lib/string_patch.dart b/runtime/lib/string_patch.dart
index f7aa83c..4352245 100644
--- a/runtime/lib/string_patch.dart
+++ b/runtime/lib/string_patch.dart
@@ -230,7 +230,7 @@
String operator [](int index) native "String_charAt";
- int codeUnitAt(int index) native "String_codeUnitAt";
+ int codeUnitAt(int index); // Implemented in the subclasses.
int get length native "String_getLength";
@@ -937,6 +937,8 @@
int get hashCode native "String_getHashCode";
+ int codeUnitAt(int index) native "String_codeUnitAt";
+
bool _isWhitespace(int codeUnit) {
return _StringBase._isOneByteWhitespace(codeUnit);
}
@@ -1240,6 +1242,8 @@
return _StringBase._isTwoByteWhitespace(codeUnit);
}
+ int codeUnitAt(int index) native "String_codeUnitAt";
+
bool operator ==(Object other) {
return super == other;
}
@@ -1256,6 +1260,8 @@
return _StringBase._isOneByteWhitespace(codeUnit);
}
+ int codeUnitAt(int index) native "String_codeUnitAt";
+
bool operator ==(Object other) {
return super == other;
}
@@ -1274,6 +1280,8 @@
return _StringBase._isTwoByteWhitespace(codeUnit);
}
+ int codeUnitAt(int index) native "String_codeUnitAt";
+
bool operator ==(Object other) {
return super == other;
}
diff --git a/runtime/vm/aot_optimizer.cc b/runtime/vm/aot_optimizer.cc
index bc3bf8b..0318786 100644
--- a/runtime/vm/aot_optimizer.cc
+++ b/runtime/vm/aot_optimizer.cc
@@ -789,7 +789,7 @@
return false;
}
} else {
- return d->IsStringFromCharCode();
+ return d->IsOneByteStringFromCharCode();
}
}
@@ -823,9 +823,10 @@
ConstantInstr* char_code_left = flow_graph()->GetConstant(
Smi::ZoneHandle(Z, Smi::New(static_cast<intptr_t>(str.CharAt(0)))));
left_val = new(Z) Value(char_code_left);
- } else if (left->IsStringFromCharCode()) {
+ } else if (left->IsOneByteStringFromCharCode()) {
// Use input of string-from-charcode as left value.
- StringFromCharCodeInstr* instr = left->AsStringFromCharCode();
+ OneByteStringFromCharCodeInstr* instr =
+ left->AsOneByteStringFromCharCode();
left_val = new(Z) Value(instr->char_code()->definition());
to_remove_left = instr;
} else {
@@ -835,9 +836,10 @@
Definition* to_remove_right = NULL;
Value* right_val = NULL;
- if (right->IsStringFromCharCode()) {
+ if (right->IsOneByteStringFromCharCode()) {
// Skip string-from-char-code, and use its input as right value.
- StringFromCharCodeInstr* right_instr = right->AsStringFromCharCode();
+ OneByteStringFromCharCodeInstr* right_instr =
+ right->AsOneByteStringFromCharCode();
right_val = new(Z) Value(right_instr->char_code()->definition());
to_remove_right = right_instr;
} else {
@@ -1787,11 +1789,24 @@
return true;
}
- if (((recognized_kind == MethodRecognizer::kStringBaseCodeUnitAt) ||
- (recognized_kind == MethodRecognizer::kStringBaseCharAt)) &&
- (ic_data.NumberOfChecks() == 1) &&
- ((class_ids[0] == kOneByteStringCid) ||
- (class_ids[0] == kTwoByteStringCid))) {
+ if ((recognized_kind == MethodRecognizer::kOneByteStringCodeUnitAt) ||
+ (recognized_kind == MethodRecognizer::kTwoByteStringCodeUnitAt) ||
+ (recognized_kind == MethodRecognizer::kExternalOneByteStringCodeUnitAt) ||
+ (recognized_kind == MethodRecognizer::kExternalTwoByteStringCodeUnitAt)) {
+ ASSERT(ic_data.NumberOfChecks() == 1);
+ ASSERT((class_ids[0] == kOneByteStringCid) ||
+ (class_ids[0] == kTwoByteStringCid) ||
+ (class_ids[0] == kExternalOneByteStringCid) ||
+ (class_ids[0] == kExternalTwoByteStringCid));
+ return TryReplaceInstanceCallWithInline(call);
+ }
+
+ if ((recognized_kind == MethodRecognizer::kStringBaseCharAt) &&
+ (ic_data.NumberOfChecks() == 1)) {
+ ASSERT((class_ids[0] == kOneByteStringCid) ||
+ (class_ids[0] == kTwoByteStringCid) ||
+ (class_ids[0] == kExternalOneByteStringCid) ||
+ (class_ids[0] == kExternalTwoByteStringCid));
return TryReplaceInstanceCallWithInline(call);
}
diff --git a/runtime/vm/constant_propagator.cc b/runtime/vm/constant_propagator.cc
index 4176af4..4409f3f 100644
--- a/runtime/vm/constant_propagator.cc
+++ b/runtime/vm/constant_propagator.cc
@@ -604,8 +604,8 @@
}
-void ConstantPropagator::VisitStringFromCharCode(
- StringFromCharCodeInstr* instr) {
+void ConstantPropagator::VisitOneByteStringFromCharCode(
+ OneByteStringFromCharCodeInstr* instr) {
const Object& o = instr->char_code()->definition()->constant_value();
if (o.IsNull() || IsNonConstant(o)) {
SetValue(instr, non_constant_);
diff --git a/runtime/vm/flow_graph_inliner.cc b/runtime/vm/flow_graph_inliner.cc
index 471c511..c360c98 100644
--- a/runtime/vm/flow_graph_inliner.cc
+++ b/runtime/vm/flow_graph_inliner.cc
@@ -2803,6 +2803,25 @@
call->env(),
FlowGraph::kEffect);
+ // For external strings: Load backing store.
+ if (cid == kExternalOneByteStringCid) {
+ str = new LoadUntaggedInstr(new Value(str),
+ ExternalOneByteString::external_data_offset());
+ cursor = flow_graph->AppendTo(cursor, str, NULL, FlowGraph::kValue);
+ str = new LoadUntaggedInstr(
+ new Value(str),
+ RawExternalOneByteString::ExternalData::data_offset());
+ cursor = flow_graph->AppendTo(cursor, str, NULL, FlowGraph::kValue);
+ } else if (cid == kExternalTwoByteStringCid) {
+ str = new LoadUntaggedInstr(new Value(str),
+ ExternalTwoByteString::external_data_offset());
+ cursor = flow_graph->AppendTo(cursor, str, NULL, FlowGraph::kValue);
+ str = new LoadUntaggedInstr(
+ new Value(str),
+ RawExternalTwoByteString::ExternalData::data_offset());
+ cursor = flow_graph->AppendTo(cursor, str, NULL, FlowGraph::kValue);
+ }
+
LoadIndexedInstr* load_indexed = new(Z) LoadIndexedInstr(
new(Z) Value(str),
new(Z) Value(index),
@@ -2823,8 +2842,7 @@
intptr_t cid,
TargetEntryInstr** entry,
Definition** last) {
- // TODO(johnmccutchan): Handle external strings in PrepareInlineStringIndexOp.
- if (RawObject::IsExternalStringClassId(cid) || cid != kOneByteStringCid) {
+ if ((cid != kOneByteStringCid) && (cid != kExternalOneByteStringCid)) {
return false;
}
Definition* str = call->ArgumentAt(0);
@@ -2836,8 +2854,8 @@
*last = PrepareInlineStringIndexOp(flow_graph, call, cid, str, index, *entry);
- StringFromCharCodeInstr* char_at = new(Z) StringFromCharCodeInstr(
- new(Z) Value(*last), cid);
+ OneByteStringFromCharCodeInstr* char_at =
+ new(Z) OneByteStringFromCharCodeInstr(new(Z) Value(*last));
flow_graph->AppendTo(*last, char_at, NULL, FlowGraph::kValue);
*last = char_at;
@@ -2852,11 +2870,6 @@
intptr_t cid,
TargetEntryInstr** entry,
Definition** last) {
- // TODO(johnmccutchan): Handle external strings in PrepareInlineStringIndexOp.
- if (RawObject::IsExternalStringClassId(cid)) {
- return false;
- }
-
Definition* str = call->ArgumentAt(0);
Definition* index = call->ArgumentAt(1);
@@ -3097,7 +3110,10 @@
receiver_cid,
kTypedDataInt32x4ArrayCid,
entry, last);
- case MethodRecognizer::kStringBaseCodeUnitAt:
+ case MethodRecognizer::kOneByteStringCodeUnitAt:
+ case MethodRecognizer::kTwoByteStringCodeUnitAt:
+ case MethodRecognizer::kExternalOneByteStringCodeUnitAt:
+ case MethodRecognizer::kExternalTwoByteStringCodeUnitAt:
return InlineStringCodeUnitAt(
flow_graph, call, receiver_cid, entry, last);
case MethodRecognizer::kStringBaseCharAt:
diff --git a/runtime/vm/flow_graph_type_propagator.cc b/runtime/vm/flow_graph_type_propagator.cc
index f95e6b2..d0391db 100644
--- a/runtime/vm/flow_graph_type_propagator.cc
+++ b/runtime/vm/flow_graph_type_propagator.cc
@@ -921,8 +921,8 @@
}
-CompileType StringFromCharCodeInstr::ComputeType() const {
- return CompileType::FromCid(cid_);
+CompileType OneByteStringFromCharCodeInstr::ComputeType() const {
+ return CompileType::FromCid(kOneByteStringCid);
}
diff --git a/runtime/vm/intermediate_language.h b/runtime/vm/intermediate_language.h
index 594355f..a7f522d 100644
--- a/runtime/vm/intermediate_language.h
+++ b/runtime/vm/intermediate_language.h
@@ -514,7 +514,7 @@
M(CheckArrayBound) \
M(Constraint) \
M(StringToCharCode) \
- M(StringFromCharCode) \
+ M(OneByteStringFromCharCode) \
M(StringInterpolate) \
M(InvokeMathCFunction) \
M(MergedMath) \
@@ -3929,17 +3929,14 @@
};
-class StringFromCharCodeInstr : public TemplateDefinition<1, NoThrow, Pure> {
+class OneByteStringFromCharCodeInstr
+ : public TemplateDefinition<1, NoThrow, Pure> {
public:
- StringFromCharCodeInstr(Value* char_code, intptr_t cid) : cid_(cid) {
- ASSERT(char_code != NULL);
- ASSERT(char_code->definition()->IsLoadIndexed());
- ASSERT(char_code->definition()->AsLoadIndexed()->class_id() ==
- kOneByteStringCid);
+ explicit OneByteStringFromCharCodeInstr(Value* char_code) {
SetInputAt(0, char_code);
}
- DECLARE_INSTRUCTION(StringFromCharCode)
+ DECLARE_INSTRUCTION(OneByteStringFromCharCode)
virtual CompileType ComputeType() const;
Value* char_code() const { return inputs_[0]; }
@@ -3947,13 +3944,11 @@
virtual bool CanDeoptimize() const { return false; }
virtual bool AttributesEqual(Instruction* other) const {
- return other->AsStringFromCharCode()->cid_ == cid_;
+ return true;
}
private:
- const intptr_t cid_;
-
- DISALLOW_COPY_AND_ASSIGN(StringFromCharCodeInstr);
+ DISALLOW_COPY_AND_ASSIGN(OneByteStringFromCharCodeInstr);
};
diff --git a/runtime/vm/intermediate_language_arm.cc b/runtime/vm/intermediate_language_arm.cc
index 2c7d44e..4123a2a 100644
--- a/runtime/vm/intermediate_language_arm.cc
+++ b/runtime/vm/intermediate_language_arm.cc
@@ -968,8 +968,8 @@
}
-LocationSummary* StringFromCharCodeInstr::MakeLocationSummary(Zone* zone,
- bool opt) const {
+LocationSummary* OneByteStringFromCharCodeInstr::MakeLocationSummary(
+ Zone* zone, bool opt) const {
const intptr_t kNumInputs = 1;
// TODO(fschneider): Allow immediate operands for the char code.
return LocationSummary::Make(zone,
@@ -979,7 +979,8 @@
}
-void StringFromCharCodeInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
+void OneByteStringFromCharCodeInstr::EmitNativeCode(
+ FlowGraphCompiler* compiler) {
ASSERT(compiler->is_optimizing());
const Register char_code = locs()->in(0).reg();
const Register result = locs()->out(0).reg();
@@ -1111,6 +1112,8 @@
case kTypedDataUint16ArrayCid:
case kOneByteStringCid:
case kTwoByteStringCid:
+ case kExternalOneByteStringCid:
+ case kExternalTwoByteStringCid:
return CompileType::FromCid(kSmiCid);
case kTypedDataInt32ArrayCid:
@@ -1137,6 +1140,8 @@
case kTypedDataUint16ArrayCid:
case kOneByteStringCid:
case kTwoByteStringCid:
+ case kExternalOneByteStringCid:
+ case kExternalTwoByteStringCid:
return kTagged;
case kTypedDataInt32ArrayCid:
return kUnboxedInt32;
@@ -1314,6 +1319,7 @@
case kExternalTypedDataUint8ArrayCid:
case kExternalTypedDataUint8ClampedArrayCid:
case kOneByteStringCid:
+ case kExternalOneByteStringCid:
ASSERT(index_scale() == 1);
__ ldrb(result, element_address);
__ SmiTag(result);
@@ -1324,6 +1330,7 @@
break;
case kTypedDataUint16ArrayCid:
case kTwoByteStringCid:
+ case kExternalTwoByteStringCid:
__ ldrh(result, element_address);
__ SmiTag(result);
break;
diff --git a/runtime/vm/intermediate_language_arm64.cc b/runtime/vm/intermediate_language_arm64.cc
index f627e6e..29eae7d 100644
--- a/runtime/vm/intermediate_language_arm64.cc
+++ b/runtime/vm/intermediate_language_arm64.cc
@@ -822,8 +822,8 @@
}
-LocationSummary* StringFromCharCodeInstr::MakeLocationSummary(Zone* zone,
- bool opt) const {
+LocationSummary* OneByteStringFromCharCodeInstr::MakeLocationSummary(
+ Zone* zone, bool opt) const {
const intptr_t kNumInputs = 1;
// TODO(fschneider): Allow immediate operands for the char code.
return LocationSummary::Make(zone,
@@ -833,7 +833,8 @@
}
-void StringFromCharCodeInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
+void OneByteStringFromCharCodeInstr::EmitNativeCode(
+ FlowGraphCompiler* compiler) {
ASSERT(compiler->is_optimizing());
const Register char_code = locs()->in(0).reg();
const Register result = locs()->out(0).reg();
@@ -968,6 +969,8 @@
case kTypedDataUint16ArrayCid:
case kOneByteStringCid:
case kTwoByteStringCid:
+ case kExternalOneByteStringCid:
+ case kExternalTwoByteStringCid:
case kTypedDataInt32ArrayCid:
case kTypedDataUint32ArrayCid:
return CompileType::FromCid(kSmiCid);
@@ -992,6 +995,8 @@
case kTypedDataUint16ArrayCid:
case kOneByteStringCid:
case kTwoByteStringCid:
+ case kExternalOneByteStringCid:
+ case kExternalTwoByteStringCid:
return kTagged;
case kTypedDataInt32ArrayCid:
return kUnboxedInt32;
@@ -1125,6 +1130,7 @@
case kExternalTypedDataUint8ArrayCid:
case kExternalTypedDataUint8ClampedArrayCid:
case kOneByteStringCid:
+ case kExternalOneByteStringCid:
ASSERT(index_scale() == 1);
__ ldr(result, element_address, kUnsignedByte);
__ SmiTag(result);
@@ -1135,6 +1141,7 @@
break;
case kTypedDataUint16ArrayCid:
case kTwoByteStringCid:
+ case kExternalTwoByteStringCid:
__ ldr(result, element_address, kUnsignedHalfword);
__ SmiTag(result);
break;
diff --git a/runtime/vm/intermediate_language_ia32.cc b/runtime/vm/intermediate_language_ia32.cc
index 44d16ed..5a7d5ce 100644
--- a/runtime/vm/intermediate_language_ia32.cc
+++ b/runtime/vm/intermediate_language_ia32.cc
@@ -840,8 +840,8 @@
}
-LocationSummary* StringFromCharCodeInstr::MakeLocationSummary(Zone* zone,
- bool opt) const {
+LocationSummary* OneByteStringFromCharCodeInstr::MakeLocationSummary(
+ Zone* zone, bool opt) const {
const intptr_t kNumInputs = 1;
// TODO(fschneider): Allow immediate operands for the char code.
return LocationSummary::Make(zone,
@@ -851,7 +851,8 @@
}
-void StringFromCharCodeInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
+void OneByteStringFromCharCodeInstr::EmitNativeCode(
+ FlowGraphCompiler* compiler) {
Register char_code = locs()->in(0).reg();
Register result = locs()->out(0).reg();
__ movl(result,
@@ -1000,6 +1001,8 @@
case kTypedDataUint16ArrayCid:
case kOneByteStringCid:
case kTwoByteStringCid:
+ case kExternalOneByteStringCid:
+ case kExternalTwoByteStringCid:
return CompileType::FromCid(kSmiCid);
case kTypedDataInt32ArrayCid:
@@ -1026,6 +1029,8 @@
case kTypedDataUint16ArrayCid:
case kOneByteStringCid:
case kTwoByteStringCid:
+ case kExternalOneByteStringCid:
+ case kExternalTwoByteStringCid:
return kTagged;
case kTypedDataInt32ArrayCid:
return kUnboxedInt32;
@@ -1159,6 +1164,7 @@
case kExternalTypedDataUint8ArrayCid:
case kExternalTypedDataUint8ClampedArrayCid:
case kOneByteStringCid:
+ case kExternalOneByteStringCid:
ASSERT(index_scale() == 1);
__ movzxb(result, element_address);
__ SmiTag(result);
@@ -1169,6 +1175,7 @@
break;
case kTypedDataUint16ArrayCid:
case kTwoByteStringCid:
+ case kExternalTwoByteStringCid:
__ movzxw(result, element_address);
__ SmiTag(result);
break;
diff --git a/runtime/vm/intermediate_language_mips.cc b/runtime/vm/intermediate_language_mips.cc
index 7bbfb55..b1b078f 100644
--- a/runtime/vm/intermediate_language_mips.cc
+++ b/runtime/vm/intermediate_language_mips.cc
@@ -1019,8 +1019,8 @@
}
-LocationSummary* StringFromCharCodeInstr::MakeLocationSummary(Zone* zone,
- bool opt) const {
+LocationSummary* OneByteStringFromCharCodeInstr::MakeLocationSummary(
+ Zone* zone, bool opt) const {
const intptr_t kNumInputs = 1;
// TODO(fschneider): Allow immediate operands for the char code.
return LocationSummary::Make(zone,
@@ -1030,13 +1030,12 @@
}
-void StringFromCharCodeInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
+void OneByteStringFromCharCodeInstr::EmitNativeCode(
+ FlowGraphCompiler* compiler) {
ASSERT(compiler->is_optimizing());
Register char_code = locs()->in(0).reg();
Register result = locs()->out(0).reg();
- __ Comment("StringFromCharCodeInstr");
-
__ lw(result, Address(THR, Thread::predefined_symbols_address_offset()));
__ AddImmediate(result, Symbols::kNullCharCodeSymbolOffset * kWordSize);
__ sll(TMP, char_code, 1); // Char code is a smi.
@@ -1169,6 +1168,8 @@
case kTypedDataUint16ArrayCid:
case kOneByteStringCid:
case kTwoByteStringCid:
+ case kExternalOneByteStringCid:
+ case kExternalTwoByteStringCid:
return CompileType::FromCid(kSmiCid);
case kTypedDataInt32ArrayCid:
@@ -1195,6 +1196,8 @@
case kTypedDataUint16ArrayCid:
case kOneByteStringCid:
case kTwoByteStringCid:
+ case kExternalOneByteStringCid:
+ case kExternalTwoByteStringCid:
return kTagged;
case kTypedDataInt32ArrayCid:
return kUnboxedInt32;
@@ -1321,6 +1324,7 @@
case kExternalTypedDataUint8ArrayCid:
case kExternalTypedDataUint8ClampedArrayCid:
case kOneByteStringCid:
+ case kExternalOneByteStringCid:
ASSERT(index_scale() == 1);
__ lbu(result, element_address);
__ SmiTag(result);
@@ -1331,6 +1335,7 @@
break;
case kTypedDataUint16ArrayCid:
case kTwoByteStringCid:
+ case kExternalTwoByteStringCid:
__ lhu(result, element_address);
__ SmiTag(result);
break;
diff --git a/runtime/vm/intermediate_language_x64.cc b/runtime/vm/intermediate_language_x64.cc
index 87b1f2e..28159ed 100644
--- a/runtime/vm/intermediate_language_x64.cc
+++ b/runtime/vm/intermediate_language_x64.cc
@@ -812,8 +812,8 @@
}
-LocationSummary* StringFromCharCodeInstr::MakeLocationSummary(Zone* zone,
- bool opt) const {
+LocationSummary* OneByteStringFromCharCodeInstr::MakeLocationSummary(
+ Zone* zone, bool opt) const {
const intptr_t kNumInputs = 1;
// TODO(fschneider): Allow immediate operands for the char code.
return LocationSummary::Make(zone,
@@ -823,7 +823,8 @@
}
-void StringFromCharCodeInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
+void OneByteStringFromCharCodeInstr::EmitNativeCode(
+ FlowGraphCompiler* compiler) {
ASSERT(compiler->is_optimizing());
Register char_code = locs()->in(0).reg();
Register result = locs()->out(0).reg();
@@ -973,6 +974,8 @@
case kTypedDataUint16ArrayCid:
case kOneByteStringCid:
case kTwoByteStringCid:
+ case kExternalOneByteStringCid:
+ case kExternalTwoByteStringCid:
case kTypedDataInt32ArrayCid:
case kTypedDataUint32ArrayCid:
return CompileType::FromCid(kSmiCid);
@@ -1000,6 +1003,8 @@
case kTypedDataUint16ArrayCid:
case kOneByteStringCid:
case kTwoByteStringCid:
+ case kExternalOneByteStringCid:
+ case kExternalTwoByteStringCid:
return kTagged;
case kTypedDataInt32ArrayCid:
return kUnboxedInt32;
@@ -1135,6 +1140,7 @@
case kExternalTypedDataUint8ArrayCid:
case kExternalTypedDataUint8ClampedArrayCid:
case kOneByteStringCid:
+ case kExternalOneByteStringCid:
__ movzxb(result, element_address);
__ SmiTag(result);
break;
@@ -1144,6 +1150,7 @@
break;
case kTypedDataUint16ArrayCid:
case kTwoByteStringCid:
+ case kExternalTwoByteStringCid:
__ movzxw(result, element_address);
__ SmiTag(result);
break;
diff --git a/runtime/vm/intrinsifier.cc b/runtime/vm/intrinsifier.cc
index 5615e58..27631b3 100644
--- a/runtime/vm/intrinsifier.cc
+++ b/runtime/vm/intrinsifier.cc
@@ -659,6 +659,68 @@
}
+static bool BuildCodeUnitAt(FlowGraph* flow_graph, intptr_t cid) {
+ GraphEntryInstr* graph_entry = flow_graph->graph_entry();
+ TargetEntryInstr* normal_entry = graph_entry->normal_entry();
+ BlockBuilder builder(flow_graph, normal_entry);
+
+ Definition* index = builder.AddParameter(1);
+ Definition* str = builder.AddParameter(2);
+ PrepareIndexedOp(&builder, str, index, String::length_offset());
+
+ // For external strings: Load external data.
+ if (cid == kExternalOneByteStringCid) {
+ str = builder.AddDefinition(
+ new LoadUntaggedInstr(new Value(str),
+ ExternalOneByteString::external_data_offset()));
+ str = builder.AddDefinition(
+ new LoadUntaggedInstr(
+ new Value(str),
+ RawExternalOneByteString::ExternalData::data_offset()));
+ } else if (cid == kExternalTwoByteStringCid) {
+ str = builder.AddDefinition(
+ new LoadUntaggedInstr(new Value(str),
+ ExternalTwoByteString::external_data_offset()));
+ str = builder.AddDefinition(
+ new LoadUntaggedInstr(
+ new Value(str),
+ RawExternalTwoByteString::ExternalData::data_offset()));
+ }
+
+ Definition* result = builder.AddDefinition(
+ new LoadIndexedInstr(new Value(str),
+ new Value(index),
+ Instance::ElementSizeFor(cid),
+ cid,
+ Thread::kNoDeoptId,
+ builder.TokenPos()));
+ builder.AddIntrinsicReturn(new Value(result));
+ return true;
+}
+
+
+bool Intrinsifier::Build_OneByteStringCodeUnitAt(FlowGraph* flow_graph) {
+ return BuildCodeUnitAt(flow_graph, kOneByteStringCid);
+}
+
+
+bool Intrinsifier::Build_TwoByteStringCodeUnitAt(FlowGraph* flow_graph) {
+ return BuildCodeUnitAt(flow_graph, kTwoByteStringCid);
+}
+
+
+bool Intrinsifier::Build_ExternalOneByteStringCodeUnitAt(
+ FlowGraph* flow_graph) {
+ return BuildCodeUnitAt(flow_graph, kExternalOneByteStringCid);
+}
+
+
+bool Intrinsifier::Build_ExternalTwoByteStringCodeUnitAt(
+ FlowGraph* flow_graph) {
+ return BuildCodeUnitAt(flow_graph, kExternalTwoByteStringCid);
+}
+
+
static bool BuildBinaryFloat32x4Op(FlowGraph* flow_graph, Token::Kind kind) {
if (!FlowGraphCompiler::SupportsUnboxedSimd128()) return false;
diff --git a/runtime/vm/intrinsifier_arm.cc b/runtime/vm/intrinsifier_arm.cc
index 78ab5db..e878d03 100644
--- a/runtime/vm/intrinsifier_arm.cc
+++ b/runtime/vm/intrinsifier_arm.cc
@@ -1574,38 +1574,6 @@
}
-void Intrinsifier::StringBaseCodeUnitAt(Assembler* assembler) {
- Label fall_through, try_two_byte_string;
-
- __ ldr(R1, Address(SP, 0 * kWordSize)); // Index.
- __ ldr(R0, Address(SP, 1 * kWordSize)); // String.
- __ tst(R1, Operand(kSmiTagMask));
- __ b(&fall_through, NE); // Index is not a Smi.
- // Range check.
- __ ldr(R2, FieldAddress(R0, String::length_offset()));
- __ cmp(R1, Operand(R2));
- __ b(&fall_through, CS); // Runtime throws exception.
- __ CompareClassId(R0, kOneByteStringCid, R3);
- __ b(&try_two_byte_string, NE);
- __ SmiUntag(R1);
- __ AddImmediate(R0, OneByteString::data_offset() - kHeapObjectTag);
- __ ldrb(R0, Address(R0, R1));
- __ SmiTag(R0);
- __ Ret();
-
- __ Bind(&try_two_byte_string);
- __ CompareClassId(R0, kTwoByteStringCid, R3);
- __ b(&fall_through, NE);
- ASSERT(kSmiTagShift == 1);
- __ AddImmediate(R0, TwoByteString::data_offset() - kHeapObjectTag);
- __ ldrh(R0, Address(R0, R1));
- __ SmiTag(R0);
- __ Ret();
-
- __ Bind(&fall_through);
-}
-
-
void GenerateSubstringMatchesSpecialization(Assembler* assembler,
intptr_t receiver_cid,
intptr_t other_cid,
diff --git a/runtime/vm/intrinsifier_arm64.cc b/runtime/vm/intrinsifier_arm64.cc
index ba2a6f1..88512e8 100644
--- a/runtime/vm/intrinsifier_arm64.cc
+++ b/runtime/vm/intrinsifier_arm64.cc
@@ -1655,38 +1655,6 @@
}
-void Intrinsifier::StringBaseCodeUnitAt(Assembler* assembler) {
- Label fall_through, try_two_byte_string;
-
- __ ldr(R1, Address(SP, 0 * kWordSize)); // Index.
- __ ldr(R0, Address(SP, 1 * kWordSize)); // String.
- __ tsti(R1, Immediate(kSmiTagMask));
- __ b(&fall_through, NE); // Index is not a Smi.
- // Range check.
- __ ldr(R2, FieldAddress(R0, String::length_offset()));
- __ cmp(R1, Operand(R2));
- __ b(&fall_through, CS); // Runtime throws exception.
- __ CompareClassId(R0, kOneByteStringCid);
- __ b(&try_two_byte_string, NE);
- __ SmiUntag(R1);
- __ AddImmediate(R0, R0, OneByteString::data_offset() - kHeapObjectTag);
- __ ldr(R0, Address(R0, R1), kUnsignedByte);
- __ SmiTag(R0);
- __ ret();
-
- __ Bind(&try_two_byte_string);
- __ CompareClassId(R0, kTwoByteStringCid);
- __ b(&fall_through, NE);
- ASSERT(kSmiTagShift == 1);
- __ AddImmediate(R0, R0, TwoByteString::data_offset() - kHeapObjectTag);
- __ ldr(R0, Address(R0, R1), kUnsignedHalfword);
- __ SmiTag(R0);
- __ ret();
-
- __ Bind(&fall_through);
-}
-
-
void GenerateSubstringMatchesSpecialization(Assembler* assembler,
intptr_t receiver_cid,
intptr_t other_cid,
diff --git a/runtime/vm/intrinsifier_ia32.cc b/runtime/vm/intrinsifier_ia32.cc
index fd2bc9f..8dc9e99 100644
--- a/runtime/vm/intrinsifier_ia32.cc
+++ b/runtime/vm/intrinsifier_ia32.cc
@@ -1718,35 +1718,6 @@
}
-void Intrinsifier::StringBaseCodeUnitAt(Assembler* assembler) {
- Label fall_through, try_two_byte_string;
- __ movl(EBX, Address(ESP, + 1 * kWordSize)); // Index.
- __ movl(EAX, Address(ESP, + 2 * kWordSize)); // String.
- __ testl(EBX, Immediate(kSmiTagMask));
- __ j(NOT_ZERO, &fall_through, Assembler::kNearJump); // Non-smi index.
- // Range check.
- __ cmpl(EBX, FieldAddress(EAX, String::length_offset()));
- // Runtime throws exception.
- __ j(ABOVE_EQUAL, &fall_through, Assembler::kNearJump);
- __ CompareClassId(EAX, kOneByteStringCid, EDI);
- __ j(NOT_EQUAL, &try_two_byte_string, Assembler::kNearJump);
- __ SmiUntag(EBX);
- __ movzxb(EAX, FieldAddress(EAX, EBX, TIMES_1, OneByteString::data_offset()));
- __ SmiTag(EAX);
- __ ret();
-
- __ Bind(&try_two_byte_string);
- __ CompareClassId(EAX, kTwoByteStringCid, EDI);
- __ j(NOT_EQUAL, &fall_through, Assembler::kNearJump);
- ASSERT(kSmiTagShift == 1);
- __ movzxw(EAX, FieldAddress(EAX, EBX, TIMES_1, TwoByteString::data_offset()));
- __ SmiTag(EAX);
- __ ret();
-
- __ Bind(&fall_through);
-}
-
-
// bool _substringMatches(int start, String other)
void Intrinsifier::StringBaseSubstringMatches(Assembler* assembler) {
// For precompilation, not implemented on IA32.
diff --git a/runtime/vm/intrinsifier_mips.cc b/runtime/vm/intrinsifier_mips.cc
index e98fc1d..2398a7b 100644
--- a/runtime/vm/intrinsifier_mips.cc
+++ b/runtime/vm/intrinsifier_mips.cc
@@ -1682,41 +1682,6 @@
}
-void Intrinsifier::StringBaseCodeUnitAt(Assembler* assembler) {
- Label fall_through, try_two_byte_string;
-
- __ lw(T1, Address(SP, 0 * kWordSize)); // Index.
- __ lw(T0, Address(SP, 1 * kWordSize)); // String.
-
- // Checks.
- __ andi(CMPRES1, T1, Immediate(kSmiTagMask));
- __ bne(CMPRES1, ZR, &fall_through); // Index is not a Smi.
- __ lw(T2, FieldAddress(T0, String::length_offset())); // Range check.
- // Runtime throws exception.
- __ BranchUnsignedGreaterEqual(T1, T2, &fall_through);
- __ LoadClassId(CMPRES1, T0); // Class ID check.
- __ BranchNotEqual(
- CMPRES1, Immediate(kOneByteStringCid), &try_two_byte_string);
-
- // Grab byte and return.
- __ SmiUntag(T1);
- __ addu(T2, T0, T1);
- __ lbu(V0, FieldAddress(T2, OneByteString::data_offset()));
- __ Ret();
- __ delay_slot()->SmiTag(V0);
-
- __ Bind(&try_two_byte_string);
- __ BranchNotEqual(CMPRES1, Immediate(kTwoByteStringCid), &fall_through);
- ASSERT(kSmiTagShift == 1);
- __ addu(T2, T0, T1);
- __ lhu(V0, FieldAddress(T2, TwoByteString::data_offset()));
- __ Ret();
- __ delay_slot()->SmiTag(V0);
-
- __ Bind(&fall_through);
-}
-
-
void GenerateSubstringMatchesSpecialization(Assembler* assembler,
intptr_t receiver_cid,
intptr_t other_cid,
diff --git a/runtime/vm/intrinsifier_x64.cc b/runtime/vm/intrinsifier_x64.cc
index ab2fa94..402613e 100644
--- a/runtime/vm/intrinsifier_x64.cc
+++ b/runtime/vm/intrinsifier_x64.cc
@@ -1573,35 +1573,6 @@
}
-void Intrinsifier::StringBaseCodeUnitAt(Assembler* assembler) {
- Label fall_through, try_two_byte_string;
- __ movq(RCX, Address(RSP, + 1 * kWordSize)); // Index.
- __ movq(RAX, Address(RSP, + 2 * kWordSize)); // String.
- __ testq(RCX, Immediate(kSmiTagMask));
- __ j(NOT_ZERO, &fall_through, Assembler::kNearJump); // Non-smi index.
- // Range check.
- __ cmpq(RCX, FieldAddress(RAX, String::length_offset()));
- // Runtime throws exception.
- __ j(ABOVE_EQUAL, &fall_through, Assembler::kNearJump);
- __ CompareClassId(RAX, kOneByteStringCid);
- __ j(NOT_EQUAL, &try_two_byte_string, Assembler::kNearJump);
- __ SmiUntag(RCX);
- __ movzxb(RAX, FieldAddress(RAX, RCX, TIMES_1, OneByteString::data_offset()));
- __ SmiTag(RAX);
- __ ret();
-
- __ Bind(&try_two_byte_string);
- __ CompareClassId(RAX, kTwoByteStringCid);
- __ j(NOT_EQUAL, &fall_through, Assembler::kNearJump);
- ASSERT(kSmiTagShift == 1);
- __ movzxw(RAX, FieldAddress(RAX, RCX, TIMES_1, OneByteString::data_offset()));
- __ SmiTag(RAX);
- __ ret();
-
- __ Bind(&fall_through);
-}
-
-
void GenerateSubstringMatchesSpecialization(Assembler* assembler,
intptr_t receiver_cid,
intptr_t other_cid,
diff --git a/runtime/vm/jit_optimizer.cc b/runtime/vm/jit_optimizer.cc
index b0be614..f4d8757 100644
--- a/runtime/vm/jit_optimizer.cc
+++ b/runtime/vm/jit_optimizer.cc
@@ -755,7 +755,7 @@
return false;
}
} else {
- return d->IsStringFromCharCode();
+ return d->IsOneByteStringFromCharCode();
}
}
@@ -789,9 +789,10 @@
ConstantInstr* char_code_left = flow_graph()->GetConstant(
Smi::ZoneHandle(Z, Smi::New(static_cast<intptr_t>(str.CharAt(0)))));
left_val = new(Z) Value(char_code_left);
- } else if (left->IsStringFromCharCode()) {
+ } else if (left->IsOneByteStringFromCharCode()) {
// Use input of string-from-charcode as left value.
- StringFromCharCodeInstr* instr = left->AsStringFromCharCode();
+ OneByteStringFromCharCodeInstr* instr =
+ left->AsOneByteStringFromCharCode();
left_val = new(Z) Value(instr->char_code()->definition());
to_remove_left = instr;
} else {
@@ -801,9 +802,10 @@
Definition* to_remove_right = NULL;
Value* right_val = NULL;
- if (right->IsStringFromCharCode()) {
+ if (right->IsOneByteStringFromCharCode()) {
// Skip string-from-char-code, and use its input as right value.
- StringFromCharCodeInstr* right_instr = right->AsStringFromCharCode();
+ OneByteStringFromCharCodeInstr* right_instr =
+ right->AsOneByteStringFromCharCode();
right_val = new(Z) Value(right_instr->char_code()->definition());
to_remove_right = right_instr;
} else {
@@ -1764,11 +1766,24 @@
return true;
}
- if (((recognized_kind == MethodRecognizer::kStringBaseCodeUnitAt) ||
- (recognized_kind == MethodRecognizer::kStringBaseCharAt)) &&
- (ic_data.NumberOfChecks() == 1) &&
- ((class_ids[0] == kOneByteStringCid) ||
- (class_ids[0] == kTwoByteStringCid))) {
+ if ((recognized_kind == MethodRecognizer::kOneByteStringCodeUnitAt) ||
+ (recognized_kind == MethodRecognizer::kTwoByteStringCodeUnitAt) ||
+ (recognized_kind == MethodRecognizer::kExternalOneByteStringCodeUnitAt) ||
+ (recognized_kind == MethodRecognizer::kExternalTwoByteStringCodeUnitAt)) {
+ ASSERT(ic_data.NumberOfChecks() == 1);
+ ASSERT((class_ids[0] == kOneByteStringCid) ||
+ (class_ids[0] == kTwoByteStringCid) ||
+ (class_ids[0] == kExternalOneByteStringCid) ||
+ (class_ids[0] == kExternalTwoByteStringCid));
+ return TryReplaceInstanceCallWithInline(call);
+ }
+
+ if ((recognized_kind == MethodRecognizer::kStringBaseCharAt) &&
+ (ic_data.NumberOfChecks() == 1)) {
+ ASSERT((class_ids[0] == kOneByteStringCid) ||
+ (class_ids[0] == kTwoByteStringCid) ||
+ (class_ids[0] == kExternalOneByteStringCid) ||
+ (class_ids[0] == kExternalTwoByteStringCid));
return TryReplaceInstanceCallWithInline(call);
}
diff --git a/runtime/vm/method_recognizer.h b/runtime/vm/method_recognizer.h
index 5e63b27..9ed5848 100644
--- a/runtime/vm/method_recognizer.h
+++ b/runtime/vm/method_recognizer.h
@@ -178,7 +178,6 @@
V(Object, get:runtimeType, ObjectRuntimeType, 15188587) \
V(_StringBase, get:hashCode, String_getHashCode, 2026040200) \
V(_StringBase, get:isEmpty, StringBaseIsEmpty, 1958879178) \
- V(_StringBase, codeUnitAt, StringBaseCodeUnitAt, 1436590579) \
V(_StringBase, _substringMatches, StringBaseSubstringMatches, 797253099) \
V(_StringBase, [], StringBaseCharAt, 754527301) \
V(_OneByteString, get:hashCode, OneByteString_getHashCode, 2026040200) \
@@ -285,6 +284,12 @@
V(_GrowableList, [], GrowableArrayGetIndexed, 1957529650) \
V(_GrowableList, []=, GrowableArraySetIndexed, 225246870) \
V(_StringBase, get:length, StringBaseLength, 707533587) \
+ V(_OneByteString, codeUnitAt, OneByteStringCodeUnitAt, 1436590579) \
+ V(_TwoByteString, codeUnitAt, TwoByteStringCodeUnitAt, 1436590579) \
+ V(_ExternalOneByteString, codeUnitAt, ExternalOneByteStringCodeUnitAt, \
+ 1436590579) \
+ V(_ExternalTwoByteString, codeUnitAt, ExternalTwoByteStringCodeUnitAt, \
+ 1436590579) \
V(_Double, unary-, DoubleFlipSignBit, 1783281169) \
V(_Double, truncateToDouble, DoubleTruncate, 791143891) \
V(_Double, roundToDouble, DoubleRound, 797558034) \
@@ -452,7 +457,6 @@
// A list of core functions that internally dispatch based on received id.
#define POLYMORPHIC_TARGET_LIST(V) \
V(_StringBase, [], StringBaseCharAt, 754527301) \
- V(_StringBase, codeUnitAt, StringBaseCodeUnitAt, 1436590579) \
V(_TypedList, _getInt8, ByteArrayBaseGetInt8, 1508321565) \
V(_TypedList, _getUint8, ByteArrayBaseGetUint8, 953411007) \
V(_TypedList, _getInt16, ByteArrayBaseGetInt16, 433971756) \
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc
index 60e35500..46320b4 100644
--- a/runtime/vm/object.cc
+++ b/runtime/vm/object.cc
@@ -15534,7 +15534,8 @@
intptr_t Instance::DataOffsetFor(intptr_t cid) {
- if (RawObject::IsExternalTypedDataClassId(cid)) {
+ if (RawObject::IsExternalTypedDataClassId(cid) ||
+ RawObject::IsExternalStringClassId(cid)) {
// Elements start at offset 0 of the external data.
return 0;
}