Skip to content

Commit 4d1bdaa

Browse files
sstricklCommit Queue
authored and
Commit Queue
committed
Reland "[vm/compiler] Change MemoryCopy to also take untagged addresses."
This is a reland of commit 06d7a23 This version fixes an issue when a phi node has multiple inputs with different unboxed integer representations. The original CL made a change where only the representations were considered, not the range of values for the Phi calculated by range analysis. The reland goes back to the old behavior for this case. Also fixes the new tests on 32-bit architectures. Original change's description: > [vm/compiler] Change MemoryCopy to also take untagged addresses. > > This CL adds the ability to pass the payload address of the source > and destination directly to the MemoryCopy instruction as an untagged > value. > > The new translation of the _TypedListBase._memMoveN methods use the new > MemoryCopy constructor, retrieving the untagged value of the data field > of both the source and destination. This way, if inlining exposes the > allocation of the object from which the data field is being retrieved, > then allocation sinking can remove the intermediate allocation if there > are no escaping uses of the object. > > Since Pointer.asTypedList allocates such ExternalTypedData objects, > this CL makes that method inlined if at all possible, which removes > the intermediate allocation if the only use of the TypedData object > is to call setRange for memory copying purposes. > > This CL also separates unboxed native slots into two groups: those > that contain untagged addresses and those that do not. The former > group now have the kUntagged representation, which mimics the old > use of LoadUntagged for the PointerBase data field and also ensures > that any arithmetic operations on untagged addresses must first be > explicitly converted to an unboxed integer and then explicitly converted > back to untagged before being stored in a slot that contains untagged > addresses. > > When a unboxed native slot that contains untagged addresses is defined, > the definition also includes a boolean which represents whether > addresses that may be moved by the GC can be stored in this slot or not. > The redundancy eliminator uses this to decide whether it is safe to > eliminate a duplicate load, replace a load with the value originally > stored in the slot, or lift a load out of a loop. > > In particular, the PointerBase data field may contain GC-moveable > addresses, but only for internal TypedData objects and views, not > for external TypedData objects or Pointers. To allow load optimizations > involving the latter, the LoadField and StoreField instructions now > take boolean flags for whether loads or stores from the slot are > guaranteed to not be GC-moveable, to override the information from > the slot argument. > > Notable benchmark changes on x64 (similar for other archs unless noted): > > JIT: > * FfiMemory.PointerPointer: 250.7% > * FfiStructCopy.Copy1Bytes: -26.73% (only x64) > * FfiStructCopy.Copy32Bytes: -25.18% (only x64) > * MemoryCopy.64.setRange.Pointer.Uint8: 19.36% > * MemoryCopy.64.setRange.Pointer.Double: 18.96% > * MemoryCopy.8.setRange.Pointer.Double: 17.59% > * MemoryCopy.8.setRange.Pointer.Uint8: 19.46% > > AOT: > * FfiMemory.PointerPointer: 323.5% > * FfiStruct.FieldLoadStore: 483.3% > * FileIO_readwrite_64kb: 15.39% > * FileIO_readwrite_512kb (Intel Xeon): 46.22% > * MemoryCopy.512.setRange.Pointer.Uint8: 35.20% > * MemoryCopy.64.setRange.Pointer.Uint8: 55.40% > * MemoryCopy.512.setRange.Pointer.Double: 29.45% > * MemoryCopy.64.setRange.Pointer.Double: 60.37% > * MemoryCopy.8.setRange.Pointer.Double: 59.54% > * MemoryCopy.8.setRange.Pointer.Uint8: 55.40% > * FfiStructCopy.Copy32Bytes: 398.3% > * FfiStructCopy.Copy1Bytes: 1233% > > TEST=vm/dart/address_local_pointer, vm/dart/pointer_as_typed_list > > Issue: #42072 > Fixes: #53124 > > Cq-Include-Trybots: luci.dart.try:vm-ffi-qemu-linux-release-arm-try,vm-eager-optimization-linux-release-x64-try,vm-linux-release-x64-try,vm-linux-debug-x64-try,vm-aot-linux-release-x64-try,vm-aot-linux-debug-x64-try > Change-Id: I563e0bfac5b1ac6cf1111649934067c12891b631 > Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/324820 > Reviewed-by: Alexander Markov <[email protected]> > Commit-Queue: Tess Strickland <[email protected]> > Reviewed-by: Martin Kustermann <[email protected]> TEST=vm/dart/address_local_pointer, vm/dart/pointer_as_typed_list Issue: #42072 Fixes: #53124 Change-Id: Iabb0e910f12636d0ff51e711c8c9c98ad40e5811 Cq-Include-Trybots: luci.dart.try:vm-ffi-qemu-linux-release-arm-try,vm-eager-optimization-linux-release-x64-try,vm-linux-release-x64-try,vm-linux-debug-x64-try,vm-aot-linux-release-x64-try,vm-aot-linux-debug-x64-try,vm-aot-linux-release-simarm_x64-try,vm-aot-linux-debug-simarm_x64-try Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/330600 Reviewed-by: Alexander Markov <[email protected]> Commit-Queue: Tess Strickland <[email protected]> Reviewed-by: Martin Kustermann <[email protected]>
1 parent 6489835 commit 4d1bdaa

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+1144
-751
lines changed

pkg/vm/lib/testing/il_matchers.dart

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,10 @@ class Env {
183183
void bind(String name, Map<String, dynamic> instrOrBlock) {
184184
final id = instrOrBlock['v'] ?? instrOrBlock['b'];
185185

186+
if (id == null) {
187+
throw 'Instruction is not a definition or a block: ${instrOrBlock['o']}';
188+
}
189+
186190
if (nameToId.containsKey(name)) {
187191
if (nameToId[name] != id) {
188192
throw 'Binding mismatch for $name: got ${nameToId[name]} and $id';
@@ -561,6 +565,14 @@ final dynamic match = Matchers();
561565
/// set this field.
562566
late String Function(Symbol) getName;
563567

568+
final bool isSimulator = (() {
569+
final configuration = Platform.environment['DART_CONFIGURATION'];
570+
if (configuration == null) {
571+
throw 'Expected DART_CONFIGURATION to be defined';
572+
}
573+
return configuration.contains('SIM');
574+
})();
575+
564576
final bool is32BitConfiguration = (() {
565577
final configuration = Platform.environment['DART_CONFIGURATION'];
566578
if (configuration == null) {

runtime/lib/typed_data.cc

Lines changed: 0 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -29,24 +29,6 @@ static void RangeCheck(intptr_t offset_in_bytes,
2929
}
3030
}
3131

32-
static void AlignmentCheck(intptr_t offset_in_bytes, intptr_t element_size) {
33-
if ((offset_in_bytes % element_size) != 0) {
34-
const auto& error = String::Handle(String::NewFormatted(
35-
"Offset in bytes (%" Pd ") must be a multiple of %" Pd "",
36-
offset_in_bytes, element_size));
37-
Exceptions::ThrowArgumentError(error);
38-
}
39-
}
40-
41-
// Checks to see if a length will not result in an OOM error.
42-
static void LengthCheck(intptr_t len, intptr_t max) {
43-
if (len < 0 || len > max) {
44-
const String& error = String::Handle(String::NewFormatted(
45-
"Length (%" Pd ") of object must be in range [0..%" Pd "]", len, max));
46-
Exceptions::ThrowArgumentError(error);
47-
}
48-
}
49-
5032
DEFINE_NATIVE_ENTRY(TypedDataBase_length, 0, 1) {
5133
GET_NON_NULL_NATIVE_ARGUMENT(TypedDataBase, array, arguments->NativeArgAt(0));
5234
return Smi::New(array.Length());
@@ -148,67 +130,6 @@ DEFINE_NATIVE_ENTRY(TypedDataBase_setClampedRange, 0, 5) {
148130
return Object::null();
149131
}
150132

151-
// Native methods for typed data allocation are recognized and implemented
152-
// in FlowGraphBuilder::BuildGraphOfRecognizedMethod.
153-
// These bodies exist only to assert that they are not used.
154-
#define TYPED_DATA_NEW(name) \
155-
DEFINE_NATIVE_ENTRY(TypedData_##name##_new, 0, 2) { \
156-
UNREACHABLE(); \
157-
return Object::null(); \
158-
}
159-
160-
#define TYPED_DATA_NEW_NATIVE(name) TYPED_DATA_NEW(name)
161-
162-
CLASS_LIST_TYPED_DATA(TYPED_DATA_NEW_NATIVE)
163-
#undef TYPED_DATA_NEW_NATIVE
164-
#undef TYPED_DATA_NEW
165-
166-
// We check the length parameter against a possible maximum length for the
167-
// array based on available physical addressable memory on the system.
168-
//
169-
// More specifically
170-
//
171-
// TypedData::MaxElements(cid) is equal to (kSmiMax / ElementSizeInBytes(cid))
172-
//
173-
// which ensures that the number of bytes the array holds is guaranteed to fit
174-
// into a _Smi.
175-
//
176-
// Argument 0 is type arguments and is ignored.
177-
static InstancePtr NewTypedDataView(intptr_t cid,
178-
intptr_t element_size,
179-
Zone* zone,
180-
NativeArguments* arguments) {
181-
GET_NON_NULL_NATIVE_ARGUMENT(TypedDataBase, typed_data,
182-
arguments->NativeArgAt(1));
183-
GET_NON_NULL_NATIVE_ARGUMENT(Smi, offset, arguments->NativeArgAt(2));
184-
GET_NON_NULL_NATIVE_ARGUMENT(Smi, len, arguments->NativeArgAt(3));
185-
const intptr_t backing_length = typed_data.LengthInBytes();
186-
const intptr_t offset_in_bytes = offset.Value();
187-
const intptr_t length = len.Value();
188-
AlignmentCheck(offset_in_bytes, element_size);
189-
LengthCheck(offset_in_bytes + length * element_size, backing_length);
190-
return TypedDataView::New(cid, typed_data, offset_in_bytes, length);
191-
}
192-
193-
#define TYPED_DATA_VIEW_NEW(native_name, cid) \
194-
DEFINE_NATIVE_ENTRY(native_name, 0, 4) { \
195-
return NewTypedDataView(cid, TypedDataBase::ElementSizeInBytes(cid), zone, \
196-
arguments); \
197-
}
198-
199-
#define TYPED_DATA_NEW_NATIVE(name) \
200-
TYPED_DATA_VIEW_NEW(TypedDataView_##name##View_new, \
201-
kTypedData##name##ViewCid) \
202-
TYPED_DATA_VIEW_NEW(TypedDataView_Unmodifiable##name##View_new, \
203-
kUnmodifiableTypedData##name##ViewCid)
204-
205-
CLASS_LIST_TYPED_DATA(TYPED_DATA_NEW_NATIVE)
206-
TYPED_DATA_VIEW_NEW(TypedDataView_ByteDataView_new, kByteDataViewCid)
207-
TYPED_DATA_VIEW_NEW(TypedDataView_UnmodifiableByteDataView_new,
208-
kUnmodifiableByteDataViewCid)
209-
#undef TYPED_DATA_NEW_NATIVE
210-
#undef TYPED_DATA_VIEW_NEW
211-
212133
#define TYPED_DATA_GETTER(getter, object, ctor, access_size) \
213134
DEFINE_NATIVE_ENTRY(TypedData_##getter, 0, 2) { \
214135
GET_NON_NULL_NATIVE_ARGUMENT(TypedDataBase, array, \
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
// Verify that returning the address of a locally created Pointer that doesn't
6+
// escape just returns the address used to create the Pointer without actually
7+
// creating it. (See https://github.com/dart-lang/sdk/issues/53124.)
8+
9+
import 'dart:ffi';
10+
11+
import 'package:expect/expect.dart';
12+
import 'package:ffi/ffi.dart';
13+
import 'package:vm/testing/il_matchers.dart';
14+
15+
@pragma('vm:never-inline')
16+
@pragma('vm:testing:print-flow-graph')
17+
int identity(int address) => Pointer<Void>.fromAddress(address).address;
18+
19+
void matchIL$identity(FlowGraph graph) {
20+
graph.dump();
21+
if (is32BitConfiguration) {
22+
// The Dart int address is truncated before being returned.
23+
graph.match([
24+
match.block('Graph'),
25+
match.block('Function', [
26+
'address' << match.Parameter(index: 0),
27+
'int32' <<
28+
match.IntConverter('address',
29+
from: 'int64', to: 'int32', is_truncating: true),
30+
'uint32' <<
31+
match.IntConverter('int32',
32+
from: 'int32', to: 'uint32', is_truncating: true),
33+
'retval' << match.IntConverter('uint32', from: 'uint32', to: 'int64'),
34+
match.Return('retval'),
35+
]),
36+
]);
37+
} else {
38+
graph.match([
39+
match.block('Graph'),
40+
match.block('Function', [
41+
'address' << match.Parameter(index: 0),
42+
match.Return('address'),
43+
]),
44+
]);
45+
}
46+
}
47+
48+
void main(List<String> args) {
49+
final n = args.isEmpty ? 100 : int.parse(args.first);
50+
Expect.equals(n, identity(n));
51+
}
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
// Verify that we don't generate intermediate external TypedData views when
6+
// using setRange to copy between Pointers.
7+
8+
import 'dart:ffi';
9+
10+
import 'package:expect/expect.dart';
11+
import 'package:ffi/ffi.dart';
12+
import 'package:vm/testing/il_matchers.dart';
13+
14+
@pragma('vm:never-inline')
15+
@pragma('vm:testing:print-flow-graph')
16+
void copyPointerContents(Pointer<Uint8>? dest, Pointer<Uint8>? src, int n) {
17+
if (dest == null || src == null) return;
18+
dest.asTypedList(n).setRange(0, n, src.asTypedList(n));
19+
}
20+
21+
void matchIL$copyPointerContents(FlowGraph graph) {
22+
graph.dump();
23+
// Since we only call it with n == 100, the third argument will get optimized
24+
// away. The element_size starts as 1, but canonicalization will turn it into
25+
// 4, the length to 100 / 4 == 25, and the starting offsets to 0 / 4 == 0.
26+
//
27+
// We could change the definition of n in main to:
28+
//
29+
// final n = args.isEmpty ? 100 : int.parse(args.first);
30+
//
31+
// but then we'd have to wade through the generated bounds checks here.
32+
graph.match([
33+
match.block('Graph', [
34+
'cnull' << match.Constant(value: null),
35+
'c0' << match.Constant(value: 0),
36+
'c25' << match.Constant(value: 25),
37+
]),
38+
match.block('Function', [
39+
'dest' << match.Parameter(index: 0),
40+
'src' << match.Parameter(index: 1),
41+
match.Branch(match.StrictCompare('dest', 'cnull'),
42+
ifTrue: 'B4', ifFalse: 'B96'),
43+
]),
44+
'B4' <<
45+
match.block('Target', [
46+
match.Return('cnull'),
47+
]),
48+
'B96' <<
49+
match.block('Target', [
50+
'dest.data' << match.LoadField('dest', slot: 'PointerBase.data'),
51+
'src.data' << match.LoadField('src', slot: 'PointerBase.data'),
52+
match.MemoryCopy('src.data', 'dest.data', 'c0', 'c0', 'c25',
53+
element_size: 4),
54+
match.Return('cnull'),
55+
]),
56+
]);
57+
}
58+
59+
void main(List<String> args) {
60+
final n = 100;
61+
if (isSimulator) {
62+
// malloc/calloc aren't defined for simulators, so pass null instead.
63+
copyPointerContents(null, null, n);
64+
} else {
65+
final src = malloc<Uint8>(n);
66+
for (int i = 0; i < n; i++) {
67+
src[i] = n - i;
68+
}
69+
final dest = calloc<Uint8>(n);
70+
copyPointerContents(dest, src, n);
71+
Expect.listEquals(src.asTypedList(n), dest.asTypedList(n));
72+
}
73+
}

runtime/tests/vm/dart/typed_data_aot_regress43534_il_test.dart

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,10 @@ void matchIL$main_foo(FlowGraph graph) {
2626
graph.match([
2727
match.block('Graph'),
2828
match.block('Function', [
29-
match.LoadField(),
29+
'list' << match.Parameter(index: 1),
30+
match.LoadField('list', slot: 'TypedDataBase.length'),
3031
match.GenericCheckBound(),
31-
match.LoadUntagged(),
32+
match.LoadField('list', slot: 'PointerBase.data'),
3233
match.LoadIndexed(),
3334
]),
3435
]);

runtime/tests/vm/dart_2/typed_data_aot_regress43534_il_test.dart

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,10 @@ void matchIL$main_foo(FlowGraph graph) {
2626
graph.match([
2727
match.block('Graph'),
2828
match.block('Function', [
29-
match.LoadField(),
29+
'list' << match.Parameter(index: 1),
30+
match.LoadField('list', slot: 'TypedDataBase.length'),
3031
match.GenericCheckBound(),
31-
match.LoadUntagged(),
32+
match.LoadField('list', slot: 'PointerBase.data'),
3233
match.LoadIndexed(),
3334
]),
3435
]);

runtime/tools/ffi/sdk_lib_ffi_generator.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,7 @@ void generatePatchExtension(
258258
? ""
259259
: """
260260
@patch
261+
@pragma("vm:prefer-inline")
261262
$typedListType asTypedList(
262263
int length, {
263264
Pointer<NativeFinalizerFunction>? finalizer,

runtime/vm/bootstrap_natives.h

Lines changed: 0 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -158,20 +158,6 @@ namespace dart {
158158
V(Timeline_getTraceClock, 0) \
159159
V(Timeline_isDartStreamEnabled, 0) \
160160
V(Timeline_reportTaskEvent, 5) \
161-
V(TypedData_Int8Array_new, 2) \
162-
V(TypedData_Uint8Array_new, 2) \
163-
V(TypedData_Uint8ClampedArray_new, 2) \
164-
V(TypedData_Int16Array_new, 2) \
165-
V(TypedData_Uint16Array_new, 2) \
166-
V(TypedData_Int32Array_new, 2) \
167-
V(TypedData_Uint32Array_new, 2) \
168-
V(TypedData_Int64Array_new, 2) \
169-
V(TypedData_Uint64Array_new, 2) \
170-
V(TypedData_Float32Array_new, 2) \
171-
V(TypedData_Float64Array_new, 2) \
172-
V(TypedData_Float32x4Array_new, 2) \
173-
V(TypedData_Int32x4Array_new, 2) \
174-
V(TypedData_Float64x2Array_new, 2) \
175161
V(TypedDataBase_length, 1) \
176162
V(TypedDataBase_setClampedRange, 5) \
177163
V(TypedData_GetInt8, 2) \
@@ -200,38 +186,8 @@ namespace dart {
200186
V(TypedData_SetInt32x4, 3) \
201187
V(TypedData_GetFloat64x2, 2) \
202188
V(TypedData_SetFloat64x2, 3) \
203-
V(TypedDataView_ByteDataView_new, 4) \
204-
V(TypedDataView_Int8ArrayView_new, 4) \
205-
V(TypedDataView_Uint8ArrayView_new, 4) \
206-
V(TypedDataView_Uint8ClampedArrayView_new, 4) \
207-
V(TypedDataView_Int16ArrayView_new, 4) \
208-
V(TypedDataView_Uint16ArrayView_new, 4) \
209-
V(TypedDataView_Int32ArrayView_new, 4) \
210-
V(TypedDataView_Uint32ArrayView_new, 4) \
211-
V(TypedDataView_Int64ArrayView_new, 4) \
212-
V(TypedDataView_Uint64ArrayView_new, 4) \
213-
V(TypedDataView_Float32ArrayView_new, 4) \
214-
V(TypedDataView_Float64ArrayView_new, 4) \
215-
V(TypedDataView_Float32x4ArrayView_new, 4) \
216-
V(TypedDataView_Int32x4ArrayView_new, 4) \
217-
V(TypedDataView_Float64x2ArrayView_new, 4) \
218189
V(TypedDataView_offsetInBytes, 1) \
219190
V(TypedDataView_typedData, 1) \
220-
V(TypedDataView_UnmodifiableByteDataView_new, 4) \
221-
V(TypedDataView_UnmodifiableInt8ArrayView_new, 4) \
222-
V(TypedDataView_UnmodifiableUint8ArrayView_new, 4) \
223-
V(TypedDataView_UnmodifiableUint8ClampedArrayView_new, 4) \
224-
V(TypedDataView_UnmodifiableInt16ArrayView_new, 4) \
225-
V(TypedDataView_UnmodifiableUint16ArrayView_new, 4) \
226-
V(TypedDataView_UnmodifiableInt32ArrayView_new, 4) \
227-
V(TypedDataView_UnmodifiableUint32ArrayView_new, 4) \
228-
V(TypedDataView_UnmodifiableInt64ArrayView_new, 4) \
229-
V(TypedDataView_UnmodifiableUint64ArrayView_new, 4) \
230-
V(TypedDataView_UnmodifiableFloat32ArrayView_new, 4) \
231-
V(TypedDataView_UnmodifiableFloat64ArrayView_new, 4) \
232-
V(TypedDataView_UnmodifiableFloat32x4ArrayView_new, 4) \
233-
V(TypedDataView_UnmodifiableInt32x4ArrayView_new, 4) \
234-
V(TypedDataView_UnmodifiableFloat64x2ArrayView_new, 4) \
235191
V(Float32x4_fromDoubles, 4) \
236192
V(Float32x4_splat, 1) \
237193
V(Float32x4_fromInt32x4Bits, 2) \

0 commit comments

Comments
 (0)