Skip to content

Commit dcdfcc2

Browse files
alexmarkovCommit Queue
authored and
Commit Queue
committed
Reland "[vm/ffi] Express FFI call closures explicitly in AST"
This reverts commit 1800039. The changes fd2e9b9 and c20f9ea are relanded as is. Reason for revert was fixed separately in https://dart-review.googlesource.com/c/sdk/+/341621 TEST=ci CoreLibraryReviewExempt: Implementation change only. Issue: #54172 Issue: #39692 Change-Id: I1a2324768502e5ffbce328127938c0d3c96c38ba Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/341642 Reviewed-by: Siva Annamalai <[email protected]> Commit-Queue: Alexander Markov <[email protected]>
1 parent a32d37e commit dcdfcc2

39 files changed

+552
-512
lines changed

pkg/vm/lib/transformations/ffi/common.dart

Lines changed: 7 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -236,7 +236,7 @@ class FfiTransformer extends Transformer {
236236
final Procedure abiSpecificIntegerArrayElemAt;
237237
final Procedure abiSpecificIntegerArraySetElemAt;
238238
final Procedure asFunctionMethod;
239-
final Procedure asFunctionInternal;
239+
final Procedure ffiCallMethod;
240240
final Procedure sizeOfMethod;
241241
final Procedure lookupFunctionMethod;
242242
final Procedure fromFunctionMethod;
@@ -289,6 +289,8 @@ class FfiTransformer extends Transformer {
289289
final Field nativeCallablePointerField;
290290
final Procedure nativeAddressOf;
291291
final Procedure nativePrivateAddressOf;
292+
final Class ffiCallClass;
293+
final Field ffiCallIsLeafField;
292294

293295
late final InterfaceType nativeFieldWrapperClass1Type;
294296
late final InterfaceType voidType;
@@ -481,8 +483,7 @@ class FfiTransformer extends Transformer {
481483
index.getProcedure('dart:ffi', 'AbiSpecificIntegerArray', '[]='),
482484
asFunctionMethod = index.getProcedure(
483485
'dart:ffi', 'NativeFunctionPointer', 'asFunction'),
484-
asFunctionInternal =
485-
index.getTopLevelProcedure('dart:ffi', '_asFunctionInternal'),
486+
ffiCallMethod = index.getTopLevelProcedure('dart:ffi', '_ffiCall'),
486487
sizeOfMethod = index.getTopLevelProcedure('dart:ffi', 'sizeOf'),
487488
lookupFunctionMethod = index.getProcedure(
488489
'dart:ffi', 'DynamicLibraryExtension', 'lookupFunction'),
@@ -589,7 +590,9 @@ class FfiTransformer extends Transformer {
589590
nativeAddressOf =
590591
index.getMember('dart:ffi', 'Native', 'addressOf') as Procedure,
591592
nativePrivateAddressOf =
592-
index.getMember('dart:ffi', 'Native', '_addressOf') as Procedure {
593+
index.getMember('dart:ffi', 'Native', '_addressOf') as Procedure,
594+
ffiCallClass = index.getClass('dart:ffi', '_FfiCall'),
595+
ffiCallIsLeafField = index.getField('dart:ffi', '_FfiCall', 'isLeaf') {
593596
nativeFieldWrapperClass1Type = nativeFieldWrapperClass1Class.getThisType(
594597
coreTypes, Nullability.nonNullable);
595598
voidType = nativeTypesClasses[NativeType.kVoid]!
@@ -1269,37 +1272,6 @@ class FfiTransformer extends Transformer {
12691272
..fileOffset = nestedExpression.fileOffset;
12701273
}
12711274

1272-
/// Creates an invocation to asFunctionInternal.
1273-
///
1274-
/// Adds a native effect invoking a compound constructors if this is used
1275-
/// as return type.
1276-
Expression buildAsFunctionInternal({
1277-
required Expression functionPointer,
1278-
required DartType nativeSignature,
1279-
required DartType dartSignature,
1280-
required bool isLeaf,
1281-
required int fileOffset,
1282-
}) {
1283-
final asFunctionInternalInvocation = StaticInvocation(
1284-
asFunctionInternal,
1285-
Arguments([
1286-
functionPointer,
1287-
BoolLiteral(isLeaf),
1288-
], types: [
1289-
dartSignature,
1290-
nativeSignature,
1291-
]))
1292-
..fileOffset = fileOffset;
1293-
1294-
final possibleCompoundReturn = findCompoundReturnType(dartSignature);
1295-
if (possibleCompoundReturn != null) {
1296-
return invokeCompoundConstructor(
1297-
asFunctionInternalInvocation, possibleCompoundReturn);
1298-
}
1299-
1300-
return asFunctionInternalInvocation;
1301-
}
1302-
13031275
/// Returns the compound [Class] if a compound is returned, otherwise `null`.
13041276
Class? findCompoundReturnType(DartType dartSignature) {
13051277
if (dartSignature is! FunctionType) {

pkg/vm/lib/transformations/ffi/use_sites.dart

Lines changed: 92 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -106,9 +106,14 @@ mixin _FfiUseSiteTransformer on FfiTransformer {
106106
// callback.
107107
int callbackCount = 0;
108108

109+
// Used to create private top-level trampoline methods with unique names
110+
// for each call.
111+
int callCount = 0;
112+
109113
@override
110114
TreeNode visitLibrary(Library node) {
111115
callbackCount = 0;
116+
callCount = 0;
112117
return super.visitLibrary(node);
113118
}
114119

@@ -360,10 +365,12 @@ mixin _FfiUseSiteTransformer on FfiTransformer {
360365
);
361366
final DartType nativeSignature = nativeType.typeArguments[0];
362367

363-
return buildAsFunctionInternal(
368+
return _replaceAsFunction(
364369
functionPointer: node.arguments.positional[0],
370+
pointerType: InterfaceType(
371+
pointerClass, Nullability.nonNullable, [nativeType]),
365372
nativeSignature: nativeSignature,
366-
dartSignature: dartType,
373+
dartSignature: dartType as FunctionType,
367374
isLeaf: isLeaf,
368375
fileOffset: node.fileOffset,
369376
);
@@ -439,6 +446,84 @@ mixin _FfiUseSiteTransformer on FfiTransformer {
439446
return node;
440447
}
441448

449+
/// Create Dart function which calls native code.
450+
///
451+
/// Adds a native effect invoking a compound constructors if this is used
452+
/// as return type.
453+
Expression _replaceAsFunction({
454+
required Expression functionPointer,
455+
required DartType pointerType,
456+
required DartType nativeSignature,
457+
required FunctionType dartSignature,
458+
required bool isLeaf,
459+
required int fileOffset,
460+
}) {
461+
assert(dartSignature.namedParameters.isEmpty);
462+
final functionPointerVarName = '#ffiTarget$callCount';
463+
final closureName = '#ffiClosure$callCount';
464+
++callCount;
465+
466+
final pointerVar = VariableDeclaration(functionPointerVarName,
467+
initializer: functionPointer, type: pointerType, isSynthesized: true);
468+
469+
final positionalParameters = [
470+
for (int i = 0; i < dartSignature.positionalParameters.length; ++i)
471+
VariableDeclaration(
472+
'arg${i + 1}',
473+
type: dartSignature.positionalParameters[i],
474+
)
475+
];
476+
477+
final closure = FunctionDeclaration(
478+
VariableDeclaration(closureName,
479+
type: dartSignature, isSynthesized: true)
480+
..addAnnotation(ConstantExpression(
481+
InstanceConstant(coreTypes.pragmaClass.reference, [], {
482+
coreTypes.pragmaName.fieldReference:
483+
StringConstant('vm:ffi:call-closure'),
484+
coreTypes.pragmaOptions.fieldReference: InstanceConstant(
485+
ffiCallClass.reference,
486+
[nativeSignature],
487+
{
488+
ffiCallIsLeafField.fieldReference: BoolConstant(isLeaf),
489+
},
490+
),
491+
}))),
492+
FunctionNode(
493+
Block([
494+
for (final param in positionalParameters)
495+
ExpressionStatement(StaticInvocation(
496+
nativeEffectMethod, Arguments([VariableGet(param)]))),
497+
ReturnStatement(StaticInvocation(
498+
ffiCallMethod,
499+
Arguments([
500+
VariableGet(pointerVar),
501+
], types: [
502+
dartSignature.returnType,
503+
]))
504+
..fileOffset = fileOffset),
505+
]),
506+
positionalParameters: positionalParameters,
507+
requiredParameterCount: dartSignature.requiredParameterCount,
508+
returnType: dartSignature.returnType)
509+
..fileOffset = fileOffset)
510+
..fileOffset = fileOffset;
511+
512+
final result = BlockExpression(
513+
Block([
514+
pointerVar,
515+
closure,
516+
]),
517+
VariableGet(closure.variable));
518+
519+
final possibleCompoundReturn = findCompoundReturnType(dartSignature);
520+
if (possibleCompoundReturn != null) {
521+
return invokeCompoundConstructor(result, possibleCompoundReturn);
522+
}
523+
524+
return result;
525+
}
526+
442527
Expression invokeCompoundConstructors(
443528
Expression nestedExpression, List<Class> compoundClasses) =>
444529
compoundClasses
@@ -452,10 +537,6 @@ mixin _FfiUseSiteTransformer on FfiTransformer {
452537
// 'lookupFunction' are constants, so by inlining the call to 'asFunction' at
453538
// the call-site, we ensure that there are no generic calls to 'asFunction'.
454539
Expression _replaceLookupFunction(StaticInvocation node) {
455-
// The generated code looks like:
456-
//
457-
// _asFunctionInternal<DS, NS>(lookup<NativeFunction<NS>>(symbolName),
458-
// isLeaf)
459540
final DartType nativeSignature = node.arguments.types[0];
460541
final DartType dartSignature = node.arguments.types[1];
461542

@@ -468,21 +549,19 @@ mixin _FfiUseSiteTransformer on FfiTransformer {
468549
final FunctionType lookupFunctionType =
469550
libraryLookupMethod.getterType as FunctionType;
470551

471-
final Expression lookupResult = InstanceInvocation(
472-
InstanceAccessKind.Instance,
473-
node.arguments.positional[0],
474-
libraryLookupMethod.name,
475-
lookupArgs,
552+
final lookupResult = InstanceInvocation(InstanceAccessKind.Instance,
553+
node.arguments.positional[0], libraryLookupMethod.name, lookupArgs,
476554
interfaceTarget: libraryLookupMethod,
477555
functionType: FunctionTypeInstantiator.instantiate(
478556
lookupFunctionType, lookupTypeArgs));
479557

480558
final isLeaf = getIsLeafBoolean(node) ?? false;
481559

482-
return buildAsFunctionInternal(
560+
return _replaceAsFunction(
483561
functionPointer: lookupResult,
562+
pointerType: lookupResult.functionType.returnType,
484563
nativeSignature: nativeSignature,
485-
dartSignature: dartSignature,
564+
dartSignature: dartSignature as FunctionType,
486565
isLeaf: isLeaf,
487566
fileOffset: node.fileOffset,
488567
);
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
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+
// Tests for NativeFunctionPointer.asFunction transformation.
6+
7+
import 'dart:ffi';
8+
9+
testVoidNoArg() {
10+
final pointer =
11+
Pointer<NativeFunction<Void Function()>>.fromAddress(0xdeadbeef);
12+
final function = pointer.asFunction<void Function()>();
13+
function();
14+
}
15+
16+
testIntInt() {
17+
final pointer =
18+
Pointer<NativeFunction<Int32 Function(Int64)>>.fromAddress(0xdeadbeef);
19+
final function = pointer.asFunction<int Function(int)>();
20+
return function(42);
21+
}
22+
23+
testLeaf5Args() {
24+
final pointer = Pointer<
25+
NativeFunction<
26+
Int32 Function(
27+
Int32, Int32, Int32, Int32, Int32)>>.fromAddress(0xdeadbeef);
28+
final function =
29+
pointer.asFunction<int Function(int, int, int, int, int)>(isLeaf: true);
30+
return function(1, 2, 3, 4, 5);
31+
}
32+
33+
void main() {
34+
testVoidNoArg();
35+
testIntInt();
36+
testLeaf5Args();
37+
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
library #lib;
2+
import self as self;
3+
import "dart:ffi" as ffi;
4+
import "dart:core" as core;
5+
import "dart:_internal" as _in;
6+
7+
import "dart:ffi";
8+
9+
static method testVoidNoArg() → dynamic {
10+
final ffi::Pointer<ffi::NativeFunction<() → ffi::Void>> pointer = [@vm.inferred-type.metadata=dart.ffi::Pointer] ffi::Pointer::fromAddress<ffi::NativeFunction<() → ffi::Void>>(3735928559);
11+
final () → void function = block {
12+
[@vm.inferred-type.metadata=dart.ffi::Pointer] synthesized ffi::Pointer<ffi::NativeFunction<() → ffi::Void>> #ffiTarget0 = pointer;
13+
@#C4
14+
function #ffiClosure0() → void {
15+
return ffi::_ffiCall<void>(#ffiTarget0);
16+
}
17+
} =>#ffiClosure0;
18+
function(){() → void};
19+
}
20+
[@vm.unboxing-info.metadata=()->i]static method testIntInt() → dynamic {
21+
final ffi::Pointer<ffi::NativeFunction<(ffi::Int64) → ffi::Int32>> pointer = [@vm.inferred-type.metadata=dart.ffi::Pointer] ffi::Pointer::fromAddress<ffi::NativeFunction<(ffi::Int64) → ffi::Int32>>(3735928559);
22+
final (core::int) → core::int function = block {
23+
[@vm.inferred-type.metadata=dart.ffi::Pointer] synthesized ffi::Pointer<ffi::NativeFunction<(ffi::Int64) → ffi::Int32>> #ffiTarget1 = pointer;
24+
@#C6
25+
function #ffiClosure1(core::int arg1) → core::int {
26+
_in::_nativeEffect(arg1);
27+
return ffi::_ffiCall<core::int>(#ffiTarget1);
28+
}
29+
} =>#ffiClosure1;
30+
return function(42){(core::int) → core::int};
31+
}
32+
[@vm.unboxing-info.metadata=()->i]static method testLeaf5Args() → dynamic {
33+
final ffi::Pointer<ffi::NativeFunction<(ffi::Int32, ffi::Int32, ffi::Int32, ffi::Int32, ffi::Int32) → ffi::Int32>> pointer = [@vm.inferred-type.metadata=dart.ffi::Pointer] ffi::Pointer::fromAddress<ffi::NativeFunction<(ffi::Int32, ffi::Int32, ffi::Int32, ffi::Int32, ffi::Int32) → ffi::Int32>>(3735928559);
34+
final (core::int, core::int, core::int, core::int, core::int) → core::int function = block {
35+
[@vm.inferred-type.metadata=dart.ffi::Pointer] synthesized ffi::Pointer<ffi::NativeFunction<(ffi::Int32, ffi::Int32, ffi::Int32, ffi::Int32, ffi::Int32) → ffi::Int32>> #ffiTarget2 = pointer;
36+
@#C9
37+
function #ffiClosure2(core::int arg1, core::int arg2, core::int arg3, core::int arg4, core::int arg5) → core::int {
38+
_in::_nativeEffect(arg1);
39+
_in::_nativeEffect(arg2);
40+
_in::_nativeEffect(arg3);
41+
_in::_nativeEffect(arg4);
42+
_in::_nativeEffect(arg5);
43+
return ffi::_ffiCall<core::int>(#ffiTarget2);
44+
}
45+
} =>#ffiClosure2;
46+
return function(1, 2, 3, 4, 5){(core::int, core::int, core::int, core::int, core::int) → core::int};
47+
}
48+
static method main() → void {
49+
self::testVoidNoArg();
50+
self::testIntInt();
51+
self::testLeaf5Args();
52+
}
53+
constants {
54+
#C1 = "vm:ffi:call-closure"
55+
#C2 = false
56+
#C3 = ffi::_FfiCall<() → ffi::Void> {isLeaf:#C2}
57+
#C4 = core::pragma {name:#C1, options:#C3}
58+
#C5 = ffi::_FfiCall<(ffi::Int64) → ffi::Int32> {isLeaf:#C2}
59+
#C6 = core::pragma {name:#C1, options:#C5}
60+
#C7 = true
61+
#C8 = ffi::_FfiCall<(ffi::Int32, ffi::Int32, ffi::Int32, ffi::Int32, ffi::Int32) → ffi::Int32> {isLeaf:#C7}
62+
#C9 = core::pragma {name:#C1, options:#C8}
63+
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
library #lib;
2+
import self as self;
3+
import "dart:ffi" as ffi;
4+
import "dart:core" as core;
5+
import "dart:_internal" as _in;
6+
7+
import "dart:ffi";
8+
9+
static method testVoidNoArg() → dynamic {
10+
final ffi::Pointer<ffi::NativeFunction<() → ffi::Void>> pointer = ffi::Pointer::fromAddress<ffi::NativeFunction<() → ffi::Void>>(3735928559);
11+
final () → void function = block {
12+
synthesized ffi::Pointer<ffi::NativeFunction<() → ffi::Void>> #ffiTarget0 = pointer;
13+
@#C4
14+
function #ffiClosure0() → void {
15+
return ffi::_ffiCall<void>(#ffiTarget0);
16+
}
17+
} =>#ffiClosure0;
18+
function(){() → void};
19+
}
20+
static method testIntInt() → dynamic {
21+
final ffi::Pointer<ffi::NativeFunction<(ffi::Int64) → ffi::Int32>> pointer = ffi::Pointer::fromAddress<ffi::NativeFunction<(ffi::Int64) → ffi::Int32>>(3735928559);
22+
final (core::int) → core::int function = block {
23+
synthesized ffi::Pointer<ffi::NativeFunction<(ffi::Int64) → ffi::Int32>> #ffiTarget1 = pointer;
24+
@#C6
25+
function #ffiClosure1(core::int arg1) → core::int {
26+
_in::_nativeEffect(arg1);
27+
return ffi::_ffiCall<core::int>(#ffiTarget1);
28+
}
29+
} =>#ffiClosure1;
30+
return function(42){(core::int) → core::int};
31+
}
32+
static method testLeaf5Args() → dynamic {
33+
final ffi::Pointer<ffi::NativeFunction<(ffi::Int32, ffi::Int32, ffi::Int32, ffi::Int32, ffi::Int32) → ffi::Int32>> pointer = ffi::Pointer::fromAddress<ffi::NativeFunction<(ffi::Int32, ffi::Int32, ffi::Int32, ffi::Int32, ffi::Int32) → ffi::Int32>>(3735928559);
34+
final (core::int, core::int, core::int, core::int, core::int) → core::int function = block {
35+
synthesized ffi::Pointer<ffi::NativeFunction<(ffi::Int32, ffi::Int32, ffi::Int32, ffi::Int32, ffi::Int32) → ffi::Int32>> #ffiTarget2 = pointer;
36+
@#C9
37+
function #ffiClosure2(core::int arg1, core::int arg2, core::int arg3, core::int arg4, core::int arg5) → core::int {
38+
_in::_nativeEffect(arg1);
39+
_in::_nativeEffect(arg2);
40+
_in::_nativeEffect(arg3);
41+
_in::_nativeEffect(arg4);
42+
_in::_nativeEffect(arg5);
43+
return ffi::_ffiCall<core::int>(#ffiTarget2);
44+
}
45+
} =>#ffiClosure2;
46+
return function(1, 2, 3, 4, 5){(core::int, core::int, core::int, core::int, core::int) → core::int};
47+
}
48+
static method main() → void {
49+
self::testVoidNoArg();
50+
self::testIntInt();
51+
self::testLeaf5Args();
52+
}
53+
constants {
54+
#C1 = "vm:ffi:call-closure"
55+
#C2 = false
56+
#C3 = ffi::_FfiCall<() → ffi::Void> {isLeaf:#C2}
57+
#C4 = core::pragma {name:#C1, options:#C3}
58+
#C5 = ffi::_FfiCall<(ffi::Int64) → ffi::Int32> {isLeaf:#C2}
59+
#C6 = core::pragma {name:#C1, options:#C5}
60+
#C7 = true
61+
#C8 = ffi::_FfiCall<(ffi::Int32, ffi::Int32, ffi::Int32, ffi::Int32, ffi::Int32) → ffi::Int32> {isLeaf:#C7}
62+
#C9 = core::pragma {name:#C1, options:#C8}
63+
}

0 commit comments

Comments
 (0)