Skip to content

Commit d23c824

Browse files
dcharkescommit-bot@chromium.org
authored andcommitted
Reland "[vm/ffi] Optimize Pointer operations for statically known types"
Original CL in patchset 1. Fix for simdbc in patchset 4. Fix for arm32 precompiled in: https://dart-review.googlesource.com/c/sdk/+/120660/ This CL optimizes Pointer operations in hot loops for Pointer<NativeInteger/NativeDouble/Pointer> (not for structs). Design: go/dart-ffi-pointers-il It provides roughly a 100x speedup for the FfiMemory benchmark. The next 5x speedup is to get rid of allocations due to `load` and `store` not being inlined. FFI API is changed to enable optimizations: * Disable dynamic invocations of Pointer.load / Pointer.store. * Disallow implicit downcast of argument passed to Pointer.store. * Stop zeroing out Pointer.address on Pointer.free(). Issue: #38172 Related issues: Closes: #35902 (Disallowing dynamic invocations of Pointer ops.) Closes: #37385 (Function variance checking) Change-Id: I3921a595fd05026d6ca565ace496771d7c1d877b Cq-Include-Trybots: luci.dart.try:vm-ffi-android-debug-arm-try,vm-ffi-android-debug-arm64-try,app-kernel-linux-debug-x64-try,vm-kernel-linux-debug-ia32-try,vm-dartkb-linux-debug-simarm64-try,vm-kernel-win-debug-x64-try,vm-kernel-win-debug-ia32-try,vm-dartkb-linux-debug-x64-try,vm-kernel-precomp-linux-debug-x64-try,vm-dartkb-linux-release-x64-abi-try,vm-kernel-precomp-android-release-arm64-try,vm-kernel-asan-linux-release-x64-try,vm-kernel-linux-release-simarm-try,vm-kernel-linux-release-simarm64-try,vm-kernel-mac-debug-simdbc64-try,vm-kernel-precomp-android-release-arm_x64-try,vm-kernel-reload-mac-release-simdbc64-try,vm-kernel-precomp-obfuscate-linux-release-x64-try,vm-kernel-reload-rollback-linux-debug-x64-try,vm-kernel-precomp-mac-release-simarm_x64-try Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/120661 Reviewed-by: Martin Kustermann <[email protected]> Commit-Queue: Daco Harkes <[email protected]>
1 parent 15e8c12 commit d23c824

30 files changed

+1094
-327
lines changed

pkg/vm/lib/transformations/ffi.dart

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,22 @@ const nonSizeAlignment = <Abi, Map<NativeType, int>>{
154154
Abi.wordSize32Align64: {},
155155
};
156156

157+
/// Load, store, and elementAt are rewired to their static type for these types.
158+
const List<NativeType> optimizedTypes = [
159+
NativeType.kInt8,
160+
NativeType.kInt16,
161+
NativeType.kInt32,
162+
NativeType.kInt64,
163+
NativeType.kUint8,
164+
NativeType.kUint16,
165+
NativeType.kUint32,
166+
NativeType.kUnit64,
167+
NativeType.kIntptr,
168+
NativeType.kFloat,
169+
NativeType.kDouble,
170+
NativeType.kPointer,
171+
];
172+
157173
/// [FfiTransformer] contains logic which is shared between
158174
/// _FfiUseSiteTransformer and _FfiDefinitionTransformer.
159175
class FfiTransformer extends Transformer {
@@ -178,6 +194,7 @@ class FfiTransformer extends Transformer {
178194
final Procedure loadMethod;
179195
final Procedure storeMethod;
180196
final Procedure offsetByMethod;
197+
final Procedure elementAtMethod;
181198
final Procedure asFunctionMethod;
182199
final Procedure asFunctionInternal;
183200
final Procedure lookupFunctionMethod;
@@ -188,6 +205,10 @@ class FfiTransformer extends Transformer {
188205
final Procedure abiMethod;
189206
final Procedure pointerFromFunctionProcedure;
190207
final Procedure nativeCallbackFunctionProcedure;
208+
final Map<NativeType, Procedure> loadMethods;
209+
final Map<NativeType, Procedure> storeMethods;
210+
final Map<NativeType, Procedure> elementAtMethods;
211+
final Procedure loadStructMethod;
191212

192213
/// Classes corresponding to [NativeType], indexed by [NativeType].
193214
final List<Class> nativeTypesClasses;
@@ -209,6 +230,7 @@ class FfiTransformer extends Transformer {
209230
loadMethod = index.getMember('dart:ffi', 'Pointer', 'load'),
210231
storeMethod = index.getMember('dart:ffi', 'Pointer', 'store'),
211232
offsetByMethod = index.getMember('dart:ffi', 'Pointer', 'offsetBy'),
233+
elementAtMethod = index.getMember('dart:ffi', 'Pointer', 'elementAt'),
212234
addressOfField = index.getMember('dart:ffi', 'Struct', 'addressOf'),
213235
structFromPointer =
214236
index.getMember('dart:ffi', 'Struct', 'fromPointer'),
@@ -228,7 +250,20 @@ class FfiTransformer extends Transformer {
228250
index.getTopLevelMember('dart:ffi', '_nativeCallbackFunction'),
229251
nativeTypesClasses = nativeTypeClassNames
230252
.map((name) => index.getClass('dart:ffi', name))
231-
.toList();
253+
.toList(),
254+
loadMethods = Map.fromIterable(optimizedTypes, value: (t) {
255+
final name = nativeTypeClassNames[t.index];
256+
return index.getTopLevelMember('dart:ffi', "_load$name");
257+
}),
258+
storeMethods = Map.fromIterable(optimizedTypes, value: (t) {
259+
final name = nativeTypeClassNames[t.index];
260+
return index.getTopLevelMember('dart:ffi', "_store$name");
261+
}),
262+
elementAtMethods = Map.fromIterable(optimizedTypes, value: (t) {
263+
final name = nativeTypeClassNames[t.index];
264+
return index.getTopLevelMember('dart:ffi', "_elementAt$name");
265+
}),
266+
loadStructMethod = index.getTopLevelMember('dart:ffi', '_loadStruct');
232267

233268
/// Computes the Dart type corresponding to a ffi.[NativeType], returns null
234269
/// if it is not a valid NativeType.

pkg/vm/lib/transformations/ffi_use_sites.dart

Lines changed: 42 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@ import 'ffi.dart'
3030
NativeType,
3131
kNativeTypeIntStart,
3232
kNativeTypeIntEnd,
33-
FfiTransformer;
33+
FfiTransformer,
34+
optimizedTypes;
3435

3536
/// Checks and replaces calls to dart:ffi struct fields and methods.
3637
void transformLibraries(
@@ -316,8 +317,6 @@ class _FfiUseSiteTransformer extends FfiTransformer {
316317
return StaticInvocation(asFunctionInternal,
317318
Arguments([node.receiver], types: [dartType, nativeSignature]));
318319
} else if (target == loadMethod) {
319-
// TODO(dacoharkes): should load and store be generic?
320-
// https://github.com/dart-lang/sdk/issues/35902
321320
final DartType dartType = node.arguments.types[0];
322321
final DartType pointerType = node.receiver.getStaticType(env);
323322
final DartType nativeType = _pointerTypeGetTypeArg(pointerType);
@@ -327,11 +326,20 @@ class _FfiUseSiteTransformer extends FfiTransformer {
327326
_ensureNativeTypeSized(nativeType, node, target.name);
328327
_ensureNativeTypeToDartType(nativeType, dartType, node,
329328
allowStructs: true);
329+
330+
// TODO(37773): When moving to extension methods we can get rid of
331+
// this rewiring.
332+
final Class nativeClass = (nativeType as InterfaceType).classNode;
333+
final NativeType nt = getType(nativeClass);
334+
final typeArguments = [
335+
if (nt == NativeType.kPointer) _pointerTypeGetTypeArg(nativeType)
336+
];
337+
return StaticInvocation(
338+
optimizedTypes.contains(nt) ? loadMethods[nt] : loadStructMethod,
339+
Arguments([node.receiver], types: typeArguments));
330340
} else if (target == storeMethod) {
331-
// TODO(dacoharkes): should load and store permitted to be generic?
332-
// https://github.com/dart-lang/sdk/issues/35902
333-
final DartType dartType =
334-
node.arguments.positional[0].getStaticType(env);
341+
final Expression storeValue = node.arguments.positional.single;
342+
final DartType dartType = storeValue.getStaticType(env);
335343
final DartType pointerType = node.receiver.getStaticType(env);
336344
final DartType nativeType = _pointerTypeGetTypeArg(pointerType);
337345

@@ -341,6 +349,32 @@ class _FfiUseSiteTransformer extends FfiTransformer {
341349
_ensureNativeTypeValid(nativeType, node);
342350
_ensureNativeTypeSized(nativeType, node, target.name);
343351
_ensureNativeTypeToDartType(nativeType, dartType, node);
352+
353+
// TODO(37773): When moving to extension methods we can get rid of
354+
// this rewiring.
355+
final Class nativeClass = (nativeType as InterfaceType).classNode;
356+
final NativeType nt = getType(nativeClass);
357+
final typeArguments = [
358+
if (nt == NativeType.kPointer) _pointerTypeGetTypeArg(nativeType)
359+
];
360+
return StaticInvocation(storeMethods[nt],
361+
Arguments([node.receiver, storeValue], types: typeArguments));
362+
} else if (target == elementAtMethod) {
363+
// TODO(37773): When moving to extension methods we can get rid of
364+
// this rewiring.
365+
final DartType pointerType = node.receiver.getStaticType(env);
366+
final DartType nativeType = _pointerTypeGetTypeArg(pointerType);
367+
final Class nativeClass = (nativeType as InterfaceType).classNode;
368+
final NativeType nt = getType(nativeClass);
369+
if (optimizedTypes.contains(nt)) {
370+
final typeArguments = [
371+
if (nt == NativeType.kPointer) _pointerTypeGetTypeArg(nativeType)
372+
];
373+
return StaticInvocation(
374+
elementAtMethods[nt],
375+
Arguments([node.receiver, node.arguments.positional[0]],
376+
types: typeArguments));
377+
}
344378
}
345379
} on _FfiStaticTypeError {
346380
// It's OK to swallow the exception because the diagnostics issued will
@@ -361,9 +395,7 @@ class _FfiUseSiteTransformer extends FfiTransformer {
361395
final DartType shouldBeElementType =
362396
convertNativeTypeToDartType(containerTypeArg, allowStructs);
363397
if (elementType == shouldBeElementType) return;
364-
// Both subtypes and implicit downcasts are allowed statically.
365-
if (env.isSubtypeOf(shouldBeElementType, elementType,
366-
SubtypeCheckMode.ignoringNullabilities)) return;
398+
// We disable implicit downcasts, they will go away when NNBD lands.
367399
if (env.isSubtypeOf(elementType, shouldBeElementType,
368400
SubtypeCheckMode.ignoringNullabilities)) return;
369401
diagnosticReporter.report(

0 commit comments

Comments
 (0)