mirror of
https://github.com/dart-lang/sdk
synced 2024-10-14 18:46:06 +00:00
[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:
parent
55a2b1cbfb
commit
904354f108
122
runtime/tests/vm/dart/typed_list_index_checkbound_il_test.dart
Normal file
122
runtime/tests/vm/dart/typed_list_index_checkbound_il_test.dart
Normal 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));
|
||||
});
|
||||
}
|
||||
}
|
|
@ -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:
|
||||
|
|
|
@ -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) \
|
||||
|
|
|
@ -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
Loading…
Reference in a new issue