Reland "[VM] Inline ClassID.getID() eagerly, extend pattern matching logic to recognize it, use it to special case ascii decoding"

This change extends/fixes the exiting "pattern recognition" which tries
to recognize the pattern

  v2 <- LoadClassIdInstr(v1)
  BranchIf v2 == IntegerConstant(cid)

Furthermore we start inlining the recognized `ClassID.getID` method very
early in the pipeline.  This allows the VM to recognize the above
pattern and insert redefinitions before the actual inlining pass.

Furthermore we special-case two very hot methods in utf8 decoding by
manually having two loops, one of which is guarded by a class-id check
against the _Uint8ArrayView class, which is most common.  (In the future
we would like to unify the typed data layouts so we no longer need to
use `ClassId.getID`, thereby also allowing non core library code to use
this).

This improves dart-aot by
  * 31%+ for a protobuf decoding benchmark we care about

Issue https://github.com/dart-lang/sdk/issues/31954

Change-Id: I7181bbf096aabe303634fd3b2bff9cc96d69719c
Reviewed-on: https://dart-review.googlesource.com/c/85443
Reviewed-by: Vyacheslav Egorov <vegorov@google.com>
Commit-Queue: Martin Kustermann <kustermann@google.com>
This commit is contained in:
Martin Kustermann 2018-11-28 10:43:32 +00:00 committed by commit-bot@chromium.org
parent c9a8589ba6
commit f5d1229081
12 changed files with 113 additions and 18 deletions

View file

@ -495,3 +495,13 @@ class Utf8Decoder {
return null;
}();
}
@patch
int _scanOneByteCharacters(List<int> units, int from, int endIndex) {
final to = endIndex;
for (var i = from; i < to; i++) {
final unit = units[i];
if ((unit & _ONE_BYTE_LIMIT) != unit) return i - from;
}
return to - from;
}

View file

@ -342,9 +342,7 @@ template("dart_io") {
"..:dart_config",
"..:dart_os_config",
] + extra_configs
public_configs = [
"..:dart_public_config",
]
public_configs = [ "..:dart_public_config" ]
if (is_fuchsia) {
configs -= [ "//build/config:symbol_visibility_hidden" ]
}

View file

@ -615,9 +615,9 @@ class ProcessStarter {
char err_msg[FDIO_SPAWN_ERR_MSG_MAX_LENGTH];
uint32_t flags = FDIO_SPAWN_CLONE_JOB | FDIO_SPAWN_CLONE_LDSVC |
FDIO_SPAWN_CLONE_NAMESPACE;
status = fdio_spawn_vmo(ZX_HANDLE_INVALID, flags, vmo, program_arguments_,
program_environment_, 4, actions, &process,
err_msg);
status =
fdio_spawn_vmo(ZX_HANDLE_INVALID, flags, vmo, program_arguments_,
program_environment_, 4, actions, &process, err_msg);
if (status != ZX_OK) {
LOG_ERR("ProcessStarter: Start() fdio_spawn_vmo failed\n");

View file

@ -4,7 +4,9 @@
// part of "internal_patch.dart";
@pragma("vm:entry-point")
class ClassID {
@pragma("vm:entry-point")
@pragma("vm:exact-result-type", "dart:core#_Smi")
static int getID(Object value) native "ClassID_getID";

View file

@ -4,14 +4,24 @@
// part of "internal_patch.dart";
@pragma("vm:entry-point")
class ClassID {
@pragma("vm:entry-point")
@pragma("vm:exact-result-type", "dart:core#_Smi")
static int getID(Object value) native "ClassID_getID";
@pragma("vm:entry-point")
static final int cidArray = 0;
@pragma("vm:entry-point")
static final int cidExternalOneByteString = 0;
@pragma("vm:entry-point")
static final int cidGrowableObjectArray = 0;
@pragma("vm:entry-point")
static final int cidImmutableArray = 0;
@pragma("vm:entry-point")
static final int cidOneByteString = 0;
@pragma("vm:entry-point")
static final int cidTwoByteString = 0;
@pragma("vm:entry-point")
static final int cidUint8ArrayView = 0;
}

View file

@ -7,7 +7,7 @@
/// used by patches of that library. We plan to change this when we have a
/// shared front end and simply use parts.
import "dart:_internal" show POWERS_OF_TEN, patch;
import "dart:_internal" show POWERS_OF_TEN, patch, ClassID;
import "dart:typed_data" show Uint8List, Uint16List;
@ -1816,3 +1816,27 @@ class _JsonUtf8DecoderSink extends ByteConversionSinkBase {
_sink.close();
}
}
@patch
int _scanOneByteCharacters(List<int> units, int from, int endIndex) {
final to = endIndex;
// Special case for _Uint8ArrayView.
final cid = ClassID.getID(units);
if (identical(cid, ClassID.cidUint8ArrayView)) {
if (from >= 0 && to >= 0 && to <= units.length) {
for (int i = from; i < to; i++) {
final unit = units[i];
if ((unit & _ONE_BYTE_LIMIT) != unit) return i - from;
}
return to - from;
}
}
// Fall through to normal case.
for (var i = from; i < to; i++) {
final unit = units[i];
if ((unit & _ONE_BYTE_LIMIT) != unit) return i - from;
}
return to - from;
}

View file

@ -222,6 +222,19 @@ abstract class _StringBase implements String {
static String _createOneByteString(List<int> charCodes, int start, int len) {
// It's always faster to do this in Dart than to call into the runtime.
var s = _OneByteString._allocate(len);
// Special case for _Uint8ArrayView.
final cid = ClassID.getID(charCodes);
if (identical(cid, ClassID.cidUint8ArrayView)) {
if (start >= 0 && len >= 0) {
for (int i = 0; i < len; i++) {
s._setAt(i, charCodes[start + i]);
}
return s;
}
}
// Fall through to normal case.
for (int i = 0; i < len; i++) {
s._setAt(i, charCodes[start + i]);
}

View file

@ -2692,6 +2692,22 @@ static bool InlineGrowableArraySetter(FlowGraph* flow_graph,
return true;
}
static bool InlineLoadClassId(FlowGraph* flow_graph,
Instruction* call,
GraphEntryInstr* graph_entry,
FunctionEntryInstr** entry,
Instruction** last) {
*entry =
new (Z) FunctionEntryInstr(graph_entry, flow_graph->allocate_block_id(),
call->GetBlock()->try_index(), DeoptId::kNone);
(*entry)->InheritDeoptTarget(Z, call);
auto load_cid = new (Z)
LoadClassIdInstr(call->PushArgumentAt(0)->value()->CopyWithType(Z));
flow_graph->InsertBefore(call, load_cid, nullptr, FlowGraph::kValue);
*last = load_cid;
return true;
}
// Adds an explicit bounds check for a typed getter/setter.
static void PrepareInlineTypedArrayBoundsCheck(FlowGraph* flow_graph,
Instruction* call,
@ -3605,6 +3621,8 @@ bool FlowGraphInliner::TryInlineRecognizedMethod(
}
return InlineGetIndexed(flow_graph, kind, call, receiver, graph_entry,
entry, last, can_speculate);
case MethodRecognizer::kClassIDgetID:
return InlineLoadClassId(flow_graph, call, graph_entry, entry, last);
default:
break;
}

View file

@ -3707,6 +3707,18 @@ void Class::InjectCIDFields() const {
CLASS_LIST_WITH_NULL(ADD_SET_FIELD)
#undef ADD_SET_FIELD
#define ADD_SET_FIELD(clazz) \
field_name = Symbols::New(thread, "cid" #clazz "View"); \
field = Field::New(field_name, true, false, true, false, *this, \
Type::Handle(Type::IntType()), TokenPosition::kMinSource, \
TokenPosition::kMinSource); \
value = Smi::New(kTypedData##clazz##ViewCid); \
field.SetStaticValue(value, true); \
AddField(field);
CLASS_LIST_TYPED_DATA(ADD_SET_FIELD)
#undef ADD_SET_FIELD
#undef CLASS_LIST_WITH_NULL
}

View file

@ -412,6 +412,7 @@ class ObjectPointerVisitor;
V(_UserTag, "_UserTag") \
V(Default, "Default") \
V(ClassID, "ClassID") \
V(getID, "getID") \
V(DartIsVM, "dart.isVM") \
V(stack, ":stack") \
V(stack_pointer, ":stack_pointer") \

View file

@ -496,3 +496,13 @@ class Utf8Decoder {
return null;
}
}
@patch
int _scanOneByteCharacters(List<int> units, int from, int endIndex) {
final to = endIndex;
for (var i = from; i < to; i++) {
final unit = units[i];
if ((unit & _ONE_BYTE_LIMIT) != unit) return i - from;
}
return to - from;
}

View file

@ -412,16 +412,6 @@ class _Utf8Decoder {
_expectedUnits = 0;
_extraUnits = 0;
int scanOneByteCharacters(List<int> units, int from) {
final to = endIndex;
final mask = _ONE_BYTE_LIMIT;
for (var i = from; i < to; i++) {
final unit = units[i];
if ((unit & mask) != unit) return i - from;
}
return to - from;
}
void addSingleBytes(int from, int to) {
assert(from >= startIndex && from <= endIndex);
assert(to >= startIndex && to <= endIndex);
@ -484,7 +474,7 @@ class _Utf8Decoder {
}
while (i < endIndex) {
var oneBytes = scanOneByteCharacters(codeUnits, i);
var oneBytes = _scanOneByteCharacters(codeUnits, i, endIndex);
if (oneBytes > 0) {
_isFirstCharacter = false;
addSingleBytes(i, i + oneBytes);
@ -545,3 +535,10 @@ class _Utf8Decoder {
}
}
}
// Returns the number of bytes in [units] starting at offset [from] which have
// the leftmost bit set to 0.
//
// To increase performance of this critical method we have a special variant of
// it implemented in the VM's patch files, which is why we make it external.
external int _scanOneByteCharacters(List<int> units, int from, int endIndex);