[vm/compiler] Convert index checks in TypedData methods to CheckBound.

Create a _typedDataIndexCheck abstraction for the conditional throwing
of IndexError in TypedData indexing operations. Recognize that
abstraction in the inliner and replace it with the equivalent CheckBound
instruction.

Do the same for byte offset checking in ByteData operations.

TEST=vm/dart/typed_list_index_checkbound

Change-Id: Ia6c4a3b7ae4667af69aa5bf214d4f187557dac06
Cq-Include-Trybots: luci.dart.try:vm-aot-linux-debug-x64-try,vm-aot-linux-release-x64-try,vm-linux-debug-x64-try,vm-eager-optimization-linux-release-x64-try
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/330800
Reviewed-by: Martin Kustermann <kustermann@google.com>
Commit-Queue: Tess Strickland <sstrickl@google.com>
This commit is contained in:
Tess Strickland 2023-11-17 15:35:42 +00:00 committed by Commit Queue
parent 55a2b1cbfb
commit 904354f108
5 changed files with 343 additions and 479 deletions

View file

@ -0,0 +1,122 @@
// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
// Verify that we perform any necessary bounds checking in typed data
// indexing methods using GenericCheckBound instead of branching Dart code.
import 'dart:ffi';
import 'dart:typed_data';
import 'package:ffi/ffi.dart';
import 'package:vm/testing/il_matchers.dart';
@pragma('vm:never-inline')
@pragma('vm:testing:print-flow-graph')
int retrieveFromView(Int8List src, int n) => src[n];
@pragma('vm:never-inline')
@pragma('vm:testing:print-flow-graph')
int retrieveFromBase(Int8List src, int n) => src[n];
@pragma('vm:never-inline')
@pragma('vm:testing:print-flow-graph')
int retrieveFromExternal(Int8List src, int n) => src[n];
void matchIL$retrieveFromView(FlowGraph graph) {
graph.dump();
graph.match([
match.block('Graph'),
match.block('Function', [
'src' << match.Parameter(index: 0),
'n' << match.Parameter(index: 1),
'len' << match.LoadField('src', slot: 'TypedDataBase.length'),
if (is32BitConfiguration) ...[
'boxed_n' << match.BoxInt64('n'),
match.GenericCheckBound('len', 'boxed_n'),
] else ...[
'unboxed_len' << match.UnboxInt64('len'),
match.GenericCheckBound('unboxed_len', 'n'),
],
'typed_data' << match.LoadField('src', slot: 'TypedDataView.typed_data'),
'boxed_offset' <<
match.LoadField('src', slot: 'TypedDataView.offset_in_bytes'),
'offset' << match.UnboxInt64('boxed_offset'),
'index' << match.BinaryInt64Op('offset', 'n', op_kind: '+'),
'data' << match.LoadField('typed_data', slot: 'PointerBase.data'),
if (is32BitConfiguration) ...[
'boxed_index' << match.BoxInt64('index'),
'retval32' << match.LoadIndexed('data', 'boxed_index'),
'retval' << match.IntConverter('retval32', from: 'int32', to: 'int64'),
] else ...[
'retval' << match.LoadIndexed('data', 'index'),
],
match.Return('retval'),
]),
]);
}
void matchIL$retrieveFromBase(FlowGraph graph) {
graph.dump();
graph.match([
match.block('Graph'),
match.block('Function', [
'src' << match.Parameter(index: 0),
'n' << match.Parameter(index: 1),
'len' << match.LoadField('src', slot: 'TypedDataBase.length'),
if (is32BitConfiguration) ...[
'boxed_n' << match.BoxInt64('n'),
match.GenericCheckBound('len', 'boxed_n'),
'retval32' << match.LoadIndexed('src', 'boxed_n'),
'retval' << match.IntConverter('retval32', from: 'int32', to: 'int64'),
] else ...[
'unboxed_len' << match.UnboxInt64('len'),
match.GenericCheckBound('unboxed_len', 'n'),
'retval' << match.LoadIndexed('src', 'n'),
],
match.Return('retval'),
]),
]);
}
void matchIL$retrieveFromExternal(FlowGraph graph) {
graph.dump();
graph.match([
match.block('Graph'),
match.block('Function', [
'src' << match.Parameter(index: 0),
'n' << match.Parameter(index: 1),
'len' << match.LoadField('src', slot: 'TypedDataBase.length'),
if (is32BitConfiguration) ...[
'boxed_n' << match.BoxInt64('n'),
match.GenericCheckBound('len', 'boxed_n'),
] else ...[
'unboxed_len' << match.UnboxInt64('len'),
match.GenericCheckBound('unboxed_len', 'n'),
],
'data' << match.LoadField('src', slot: 'PointerBase.data'),
if (is32BitConfiguration) ...[
'retval32' << match.LoadIndexed('data', 'boxed_n'),
'retval' << match.IntConverter('retval32', from: 'int32', to: 'int64'),
] else ...[
'retval' << match.LoadIndexed('data', 'n'),
],
match.Return('retval'),
]),
]);
}
void main(List<String> args) {
final n = args.isEmpty ? 0 : int.parse(args.first);
final list = Int8List.fromList([1, 2, 3, 4]);
print(retrieveFromBase(list, n));
print(retrieveFromView(Int8List.sublistView(list), n));
if (!isSimulator) {
using((arena) {
final p = arena.allocate<Int8>(list.length);
final external = p.asTypedList(list.length);
external.setRange(0, list.length, list);
print(retrieveFromExternal(external, n));
});
}
}

View file

@ -2745,6 +2745,42 @@ static bool CanUnboxDouble() {
#undef Z
#define Z (flow_graph->zone())
static bool InlineTypedDataIndexCheck(FlowGraph* flow_graph,
Instruction* call,
Definition* receiver,
GraphEntryInstr* graph_entry,
FunctionEntryInstr** entry,
Instruction** last,
Definition** result,
const String& symbol) {
*entry =
new (Z) FunctionEntryInstr(graph_entry, flow_graph->allocate_block_id(),
call->GetBlock()->try_index(), DeoptId::kNone);
(*entry)->InheritDeoptTarget(Z, call);
Instruction* cursor = *entry;
Definition* index = call->ArgumentAt(1);
Definition* length = call->ArgumentAt(2);
if (CompilerState::Current().is_aot()) {
// Add a null-check in case the index argument is known to be compatible
// but possibly nullable. We don't need to do the same for length
// because all callers in typed_data_patch.dart retrieve the length
// from the typed data object.
auto* const null_check =
new (Z) CheckNullInstr(new (Z) Value(index), symbol, call->deopt_id(),
call->source(), CheckNullInstr::kArgumentError);
cursor = flow_graph->AppendTo(cursor, null_check, call->env(),
FlowGraph::kEffect);
}
index = flow_graph->CreateCheckBound(length, index, call->deopt_id());
cursor = flow_graph->AppendTo(cursor, index, call->env(), FlowGraph::kValue);
*last = cursor;
*result = index;
return true;
}
static intptr_t PrepareInlineIndexedOp(FlowGraph* flow_graph,
Instruction* call,
intptr_t array_cid,
@ -4564,6 +4600,13 @@ bool FlowGraphInliner::TryInlineRecognizedMethod(
const MethodRecognizer::Kind kind = target.recognized_kind();
switch (kind) {
case MethodRecognizer::kTypedDataIndexCheck:
return InlineTypedDataIndexCheck(flow_graph, call, receiver, graph_entry,
entry, last, result, Symbols::Index());
case MethodRecognizer::kByteDataByteOffsetCheck:
return InlineTypedDataIndexCheck(flow_graph, call, receiver, graph_entry,
entry, last, result,
Symbols::byteOffset());
// Recognized [] operators.
case MethodRecognizer::kObjectArrayGetIndexed:
case MethodRecognizer::kGrowableArrayGetIndexed:

View file

@ -120,6 +120,8 @@ namespace dart {
V(_TypedListBase, _memMove8, TypedData_memMove8, 0xd1d8e325) \
V(_TypedListBase, _memMove16, TypedData_memMove16, 0x07861cd5) \
V(::, _toClampedUint8, ConvertIntToClampedUint8, 0xd0e522d0) \
V(::, _typedDataIndexCheck, TypedDataIndexCheck, 0xc54f594f) \
V(::, _byteDataByteOffsetCheck, ByteDataByteOffsetCheck, 0x4ae73104) \
V(::, copyRangeFromUint8ListToOneByteString, \
CopyRangeFromUint8ListToOneByteString, 0xcc42cce1) \
V(_StringBase, _interpolate, StringBaseInterpolate, 0x7c662480) \
@ -390,40 +392,40 @@ namespace dart {
V(_IntegerImplementation, <<, Integer_shl, 0x2d253e1b) \
#define GRAPH_TYPED_DATA_INTRINSICS_LIST(V) \
V(_Int8List, [], Int8ArrayGetIndexed, 0xb8ab318e) \
V(_Int8List, []=, Int8ArraySetIndexed, 0xd832152f) \
V(_Uint8List, [], Uint8ArrayGetIndexed, 0x1e23b54e) \
V(_Uint8List, []=, Uint8ArraySetIndexed, 0x24c120f3) \
V(_ExternalUint8Array, [], ExternalUint8ArrayGetIndexed, 0x1e23b54e) \
V(_ExternalUint8Array, []=, ExternalUint8ArraySetIndexed, 0x24c120f3) \
V(_Uint8ClampedList, [], Uint8ClampedArrayGetIndexed, 0x1e23b54e) \
V(_Uint8ClampedList, []=, Uint8ClampedArraySetIndexed, 0x1ac0d693) \
V(_Int8List, [], Int8ArrayGetIndexed, 0x7b31eba4) \
V(_Int8List, []=, Int8ArraySetIndexed, 0x02734e41) \
V(_Uint8List, [], Uint8ArrayGetIndexed, 0xe0aa6f64) \
V(_Uint8List, []=, Uint8ArraySetIndexed, 0x4f025a05) \
V(_ExternalUint8Array, [], ExternalUint8ArrayGetIndexed, 0xe0aa6f64) \
V(_ExternalUint8Array, []=, ExternalUint8ArraySetIndexed, 0x4f025a05) \
V(_Uint8ClampedList, [], Uint8ClampedArrayGetIndexed, 0xe0aa6f64) \
V(_Uint8ClampedList, []=, Uint8ClampedArraySetIndexed, 0x45020fa5) \
V(_ExternalUint8ClampedArray, [], ExternalUint8ClampedArrayGetIndexed, \
0x1e23b54e) \
0xe0aa6f64) \
V(_ExternalUint8ClampedArray, []=, ExternalUint8ClampedArraySetIndexed, \
0x1ac0d693) \
V(_Int16List, [], Int16ArrayGetIndexed, 0x98a990ee) \
V(_Int16List, []=, Int16ArraySetIndexed, 0x68b1d8ba) \
V(_Uint16List, [], Uint16ArrayGetIndexed, 0x7c826cee) \
V(_Uint16List, []=, Uint16ArraySetIndexed, 0x50f48031) \
V(_Int32List, [], Int32ArrayGetIndexed, 0x9a47368d) \
V(_Int32List, []=, Int32ArraySetIndexed, 0x46e65559) \
V(_Uint32List, [], Uint32ArrayGetIndexed, 0xe9999c2d) \
V(_Uint32List, []=, Uint32ArraySetIndexed, 0x3289c9d9) \
V(_Int64List, [], Int64ArrayGetIndexed, 0xd30f90cd) \
V(_Int64List, []=, Int64ArraySetIndexed, 0xa79b0f2f) \
V(_Uint64List, [], Uint64ArrayGetIndexed, 0x0fd3f64d) \
V(_Uint64List, []=, Uint64ArraySetIndexed, 0x72fc8067) \
V(_Float64List, [], Float64ArrayGetIndexed, 0x0ccc6114) \
V(_Float64List, []=, Float64ArraySetIndexed, 0xa6594c8d) \
V(_Float32List, [], Float32ArrayGetIndexed, 0xe2ca6234) \
V(_Float32List, []=, Float32ArraySetIndexed, 0x01a7a9bb) \
V(_Float32x4List, [], Float32x4ArrayGetIndexed, 0xe48a60cd) \
V(_Float32x4List, []=, Float32x4ArraySetIndexed, 0x85f79568) \
V(_Int32x4List, [], Int32x4ArrayGetIndexed, 0xfa032945) \
V(_Int32x4List, []=, Int32x4ArraySetIndexed, 0x6f45f1f8) \
V(_Float64x2List, [], Float64x2ArrayGetIndexed, 0x2e4ca3f7) \
V(_Float64x2List, []=, Float64x2ArraySetIndexed, 0x75cd3b10) \
0x45020fa5) \
V(_Int16List, [], Int16ArrayGetIndexed, 0x5b304b04) \
V(_Int16List, []=, Int16ArraySetIndexed, 0x92f311cc) \
V(_Uint16List, [], Uint16ArrayGetIndexed, 0x3f092704) \
V(_Uint16List, []=, Uint16ArraySetIndexed, 0x7b35b943) \
V(_Int32List, [], Int32ArrayGetIndexed, 0x5ccdf0a3) \
V(_Int32List, []=, Int32ArraySetIndexed, 0x71278e6b) \
V(_Uint32List, [], Uint32ArrayGetIndexed, 0xac205643) \
V(_Uint32List, []=, Uint32ArraySetIndexed, 0x5ccb02eb) \
V(_Int64List, [], Int64ArrayGetIndexed, 0x95964ae3) \
V(_Int64List, []=, Int64ArraySetIndexed, 0xf550bf5d) \
V(_Uint64List, [], Uint64ArrayGetIndexed, 0xd25ab063) \
V(_Uint64List, []=, Uint64ArraySetIndexed, 0xc0b23095) \
V(_Float64List, [], Float64ArrayGetIndexed, 0x85ff7e1e) \
V(_Float64List, []=, Float64ArraySetIndexed, 0xb12e72af) \
V(_Float32List, [], Float32ArrayGetIndexed, 0x5bfd7f3e) \
V(_Float32List, []=, Float32ArraySetIndexed, 0x0c7ccfdd) \
V(_Float32x4List, [], Float32x4ArrayGetIndexed, 0xbbf0c685) \
V(_Float32x4List, []=, Float32x4ArraySetIndexed, 0x2f5090b8) \
V(_Int32x4List, [], Int32x4ArrayGetIndexed, 0x11605fcd) \
V(_Int32x4List, []=, Int32x4ArraySetIndexed, 0x3624ca18) \
V(_Float64x2List, [], Float64x2ArrayGetIndexed, 0x2ff8c69b) \
V(_Float64x2List, []=, Float64x2ArraySetIndexed, 0xcef0684c) \
V(_TypedListBase, get:length, TypedListBaseLength, 0x5842648b) \
V(_ByteDataView, get:length, ByteDataViewLength, 0x5842648b) \
V(_Float32x4, get:x, Float32x4GetX, 0x3a2af950) \

View file

@ -480,6 +480,7 @@ class ObjectPointerVisitor;
V(add, "add") \
V(addStream, "addStream") \
V(asyncStarBody, "asyncStarBody") \
V(byteOffset, "byteOffset") \
V(c_result, ":result") \
V(call, "call") \
V(callback, "callback") \

File diff suppressed because it is too large Load diff