Reland "[vm/ffi] Express FFI call closures explicitly in AST"
This reverts commit 1800039c2af43501880e3f3774322c09423e9361.
The changes fd2e9b9f1af9e2ced5d263956f82e4ef3b583c8a and
c20f9eaf6ff3518b3d689abea411b7f9ca05dedb 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: https://github.com/dart-lang/sdk/issues/54172
Issue: https://github.com/dart-lang/sdk/issues/39692
Change-Id: I1a2324768502e5ffbce328127938c0d3c96c38ba
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/341642
Reviewed-by: Siva Annamalai <asiva@google.com>
Commit-Queue: Alexander Markov <alexmarkov@google.com>
diff --git a/pkg/vm/lib/transformations/ffi/common.dart b/pkg/vm/lib/transformations/ffi/common.dart
index a41d7a0..2b664e4 100644
--- a/pkg/vm/lib/transformations/ffi/common.dart
+++ b/pkg/vm/lib/transformations/ffi/common.dart
@@ -236,7 +236,7 @@
final Procedure abiSpecificIntegerArrayElemAt;
final Procedure abiSpecificIntegerArraySetElemAt;
final Procedure asFunctionMethod;
- final Procedure asFunctionInternal;
+ final Procedure ffiCallMethod;
final Procedure sizeOfMethod;
final Procedure lookupFunctionMethod;
final Procedure fromFunctionMethod;
@@ -289,6 +289,8 @@
final Field nativeCallablePointerField;
final Procedure nativeAddressOf;
final Procedure nativePrivateAddressOf;
+ final Class ffiCallClass;
+ final Field ffiCallIsLeafField;
late final InterfaceType nativeFieldWrapperClass1Type;
late final InterfaceType voidType;
@@ -481,8 +483,7 @@
index.getProcedure('dart:ffi', 'AbiSpecificIntegerArray', '[]='),
asFunctionMethod = index.getProcedure(
'dart:ffi', 'NativeFunctionPointer', 'asFunction'),
- asFunctionInternal =
- index.getTopLevelProcedure('dart:ffi', '_asFunctionInternal'),
+ ffiCallMethod = index.getTopLevelProcedure('dart:ffi', '_ffiCall'),
sizeOfMethod = index.getTopLevelProcedure('dart:ffi', 'sizeOf'),
lookupFunctionMethod = index.getProcedure(
'dart:ffi', 'DynamicLibraryExtension', 'lookupFunction'),
@@ -589,7 +590,9 @@
nativeAddressOf =
index.getMember('dart:ffi', 'Native', 'addressOf') as Procedure,
nativePrivateAddressOf =
- index.getMember('dart:ffi', 'Native', '_addressOf') as Procedure {
+ index.getMember('dart:ffi', 'Native', '_addressOf') as Procedure,
+ ffiCallClass = index.getClass('dart:ffi', '_FfiCall'),
+ ffiCallIsLeafField = index.getField('dart:ffi', '_FfiCall', 'isLeaf') {
nativeFieldWrapperClass1Type = nativeFieldWrapperClass1Class.getThisType(
coreTypes, Nullability.nonNullable);
voidType = nativeTypesClasses[NativeType.kVoid]!
@@ -1269,37 +1272,6 @@
..fileOffset = nestedExpression.fileOffset;
}
- /// Creates an invocation to asFunctionInternal.
- ///
- /// Adds a native effect invoking a compound constructors if this is used
- /// as return type.
- Expression buildAsFunctionInternal({
- required Expression functionPointer,
- required DartType nativeSignature,
- required DartType dartSignature,
- required bool isLeaf,
- required int fileOffset,
- }) {
- final asFunctionInternalInvocation = StaticInvocation(
- asFunctionInternal,
- Arguments([
- functionPointer,
- BoolLiteral(isLeaf),
- ], types: [
- dartSignature,
- nativeSignature,
- ]))
- ..fileOffset = fileOffset;
-
- final possibleCompoundReturn = findCompoundReturnType(dartSignature);
- if (possibleCompoundReturn != null) {
- return invokeCompoundConstructor(
- asFunctionInternalInvocation, possibleCompoundReturn);
- }
-
- return asFunctionInternalInvocation;
- }
-
/// Returns the compound [Class] if a compound is returned, otherwise `null`.
Class? findCompoundReturnType(DartType dartSignature) {
if (dartSignature is! FunctionType) {
diff --git a/pkg/vm/lib/transformations/ffi/use_sites.dart b/pkg/vm/lib/transformations/ffi/use_sites.dart
index 0d15a5a..a44f71c 100644
--- a/pkg/vm/lib/transformations/ffi/use_sites.dart
+++ b/pkg/vm/lib/transformations/ffi/use_sites.dart
@@ -106,9 +106,14 @@
// callback.
int callbackCount = 0;
+ // Used to create private top-level trampoline methods with unique names
+ // for each call.
+ int callCount = 0;
+
@override
TreeNode visitLibrary(Library node) {
callbackCount = 0;
+ callCount = 0;
return super.visitLibrary(node);
}
@@ -360,10 +365,12 @@
);
final DartType nativeSignature = nativeType.typeArguments[0];
- return buildAsFunctionInternal(
+ return _replaceAsFunction(
functionPointer: node.arguments.positional[0],
+ pointerType: InterfaceType(
+ pointerClass, Nullability.nonNullable, [nativeType]),
nativeSignature: nativeSignature,
- dartSignature: dartType,
+ dartSignature: dartType as FunctionType,
isLeaf: isLeaf,
fileOffset: node.fileOffset,
);
@@ -439,6 +446,84 @@
return node;
}
+ /// Create Dart function which calls native code.
+ ///
+ /// Adds a native effect invoking a compound constructors if this is used
+ /// as return type.
+ Expression _replaceAsFunction({
+ required Expression functionPointer,
+ required DartType pointerType,
+ required DartType nativeSignature,
+ required FunctionType dartSignature,
+ required bool isLeaf,
+ required int fileOffset,
+ }) {
+ assert(dartSignature.namedParameters.isEmpty);
+ final functionPointerVarName = '#ffiTarget$callCount';
+ final closureName = '#ffiClosure$callCount';
+ ++callCount;
+
+ final pointerVar = VariableDeclaration(functionPointerVarName,
+ initializer: functionPointer, type: pointerType, isSynthesized: true);
+
+ final positionalParameters = [
+ for (int i = 0; i < dartSignature.positionalParameters.length; ++i)
+ VariableDeclaration(
+ 'arg${i + 1}',
+ type: dartSignature.positionalParameters[i],
+ )
+ ];
+
+ final closure = FunctionDeclaration(
+ VariableDeclaration(closureName,
+ type: dartSignature, isSynthesized: true)
+ ..addAnnotation(ConstantExpression(
+ InstanceConstant(coreTypes.pragmaClass.reference, [], {
+ coreTypes.pragmaName.fieldReference:
+ StringConstant('vm:ffi:call-closure'),
+ coreTypes.pragmaOptions.fieldReference: InstanceConstant(
+ ffiCallClass.reference,
+ [nativeSignature],
+ {
+ ffiCallIsLeafField.fieldReference: BoolConstant(isLeaf),
+ },
+ ),
+ }))),
+ FunctionNode(
+ Block([
+ for (final param in positionalParameters)
+ ExpressionStatement(StaticInvocation(
+ nativeEffectMethod, Arguments([VariableGet(param)]))),
+ ReturnStatement(StaticInvocation(
+ ffiCallMethod,
+ Arguments([
+ VariableGet(pointerVar),
+ ], types: [
+ dartSignature.returnType,
+ ]))
+ ..fileOffset = fileOffset),
+ ]),
+ positionalParameters: positionalParameters,
+ requiredParameterCount: dartSignature.requiredParameterCount,
+ returnType: dartSignature.returnType)
+ ..fileOffset = fileOffset)
+ ..fileOffset = fileOffset;
+
+ final result = BlockExpression(
+ Block([
+ pointerVar,
+ closure,
+ ]),
+ VariableGet(closure.variable));
+
+ final possibleCompoundReturn = findCompoundReturnType(dartSignature);
+ if (possibleCompoundReturn != null) {
+ return invokeCompoundConstructor(result, possibleCompoundReturn);
+ }
+
+ return result;
+ }
+
Expression invokeCompoundConstructors(
Expression nestedExpression, List<Class> compoundClasses) =>
compoundClasses
@@ -452,10 +537,6 @@
// 'lookupFunction' are constants, so by inlining the call to 'asFunction' at
// the call-site, we ensure that there are no generic calls to 'asFunction'.
Expression _replaceLookupFunction(StaticInvocation node) {
- // The generated code looks like:
- //
- // _asFunctionInternal<DS, NS>(lookup<NativeFunction<NS>>(symbolName),
- // isLeaf)
final DartType nativeSignature = node.arguments.types[0];
final DartType dartSignature = node.arguments.types[1];
@@ -468,21 +549,19 @@
final FunctionType lookupFunctionType =
libraryLookupMethod.getterType as FunctionType;
- final Expression lookupResult = InstanceInvocation(
- InstanceAccessKind.Instance,
- node.arguments.positional[0],
- libraryLookupMethod.name,
- lookupArgs,
+ final lookupResult = InstanceInvocation(InstanceAccessKind.Instance,
+ node.arguments.positional[0], libraryLookupMethod.name, lookupArgs,
interfaceTarget: libraryLookupMethod,
functionType: FunctionTypeInstantiator.instantiate(
lookupFunctionType, lookupTypeArgs));
final isLeaf = getIsLeafBoolean(node) ?? false;
- return buildAsFunctionInternal(
+ return _replaceAsFunction(
functionPointer: lookupResult,
+ pointerType: lookupResult.functionType.returnType,
nativeSignature: nativeSignature,
- dartSignature: dartSignature,
+ dartSignature: dartSignature as FunctionType,
isLeaf: isLeaf,
fileOffset: node.fileOffset,
);
diff --git a/pkg/vm/testcases/transformations/ffi/as_function.dart b/pkg/vm/testcases/transformations/ffi/as_function.dart
new file mode 100644
index 0000000..8bf0dc0
--- /dev/null
+++ b/pkg/vm/testcases/transformations/ffi/as_function.dart
@@ -0,0 +1,37 @@
+// 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.
+
+// Tests for NativeFunctionPointer.asFunction transformation.
+
+import 'dart:ffi';
+
+testVoidNoArg() {
+ final pointer =
+ Pointer<NativeFunction<Void Function()>>.fromAddress(0xdeadbeef);
+ final function = pointer.asFunction<void Function()>();
+ function();
+}
+
+testIntInt() {
+ final pointer =
+ Pointer<NativeFunction<Int32 Function(Int64)>>.fromAddress(0xdeadbeef);
+ final function = pointer.asFunction<int Function(int)>();
+ return function(42);
+}
+
+testLeaf5Args() {
+ final pointer = Pointer<
+ NativeFunction<
+ Int32 Function(
+ Int32, Int32, Int32, Int32, Int32)>>.fromAddress(0xdeadbeef);
+ final function =
+ pointer.asFunction<int Function(int, int, int, int, int)>(isLeaf: true);
+ return function(1, 2, 3, 4, 5);
+}
+
+void main() {
+ testVoidNoArg();
+ testIntInt();
+ testLeaf5Args();
+}
diff --git a/pkg/vm/testcases/transformations/ffi/as_function.dart.aot.expect b/pkg/vm/testcases/transformations/ffi/as_function.dart.aot.expect
new file mode 100644
index 0000000..233545a
--- /dev/null
+++ b/pkg/vm/testcases/transformations/ffi/as_function.dart.aot.expect
@@ -0,0 +1,63 @@
+library #lib;
+import self as self;
+import "dart:ffi" as ffi;
+import "dart:core" as core;
+import "dart:_internal" as _in;
+
+import "dart:ffi";
+
+static method testVoidNoArg() → dynamic {
+ final ffi::Pointer<ffi::NativeFunction<() → ffi::Void>> pointer = [@vm.inferred-type.metadata=dart.ffi::Pointer] ffi::Pointer::fromAddress<ffi::NativeFunction<() → ffi::Void>>(3735928559);
+ final () → void function = block {
+ [@vm.inferred-type.metadata=dart.ffi::Pointer] synthesized ffi::Pointer<ffi::NativeFunction<() → ffi::Void>> #ffiTarget0 = pointer;
+ @#C4
+ function #ffiClosure0() → void {
+ return ffi::_ffiCall<void>(#ffiTarget0);
+ }
+ } =>#ffiClosure0;
+ function(){() → void};
+}
+[@vm.unboxing-info.metadata=()->i]static method testIntInt() → dynamic {
+ 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);
+ final (core::int) → core::int function = block {
+ [@vm.inferred-type.metadata=dart.ffi::Pointer] synthesized ffi::Pointer<ffi::NativeFunction<(ffi::Int64) → ffi::Int32>> #ffiTarget1 = pointer;
+ @#C6
+ function #ffiClosure1(core::int arg1) → core::int {
+ _in::_nativeEffect(arg1);
+ return ffi::_ffiCall<core::int>(#ffiTarget1);
+ }
+ } =>#ffiClosure1;
+ return function(42){(core::int) → core::int};
+}
+[@vm.unboxing-info.metadata=()->i]static method testLeaf5Args() → dynamic {
+ 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);
+ final (core::int, core::int, core::int, core::int, core::int) → core::int function = block {
+ [@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;
+ @#C9
+ function #ffiClosure2(core::int arg1, core::int arg2, core::int arg3, core::int arg4, core::int arg5) → core::int {
+ _in::_nativeEffect(arg1);
+ _in::_nativeEffect(arg2);
+ _in::_nativeEffect(arg3);
+ _in::_nativeEffect(arg4);
+ _in::_nativeEffect(arg5);
+ return ffi::_ffiCall<core::int>(#ffiTarget2);
+ }
+ } =>#ffiClosure2;
+ return function(1, 2, 3, 4, 5){(core::int, core::int, core::int, core::int, core::int) → core::int};
+}
+static method main() → void {
+ self::testVoidNoArg();
+ self::testIntInt();
+ self::testLeaf5Args();
+}
+constants {
+ #C1 = "vm:ffi:call-closure"
+ #C2 = false
+ #C3 = ffi::_FfiCall<() → ffi::Void> {isLeaf:#C2}
+ #C4 = core::pragma {name:#C1, options:#C3}
+ #C5 = ffi::_FfiCall<(ffi::Int64) → ffi::Int32> {isLeaf:#C2}
+ #C6 = core::pragma {name:#C1, options:#C5}
+ #C7 = true
+ #C8 = ffi::_FfiCall<(ffi::Int32, ffi::Int32, ffi::Int32, ffi::Int32, ffi::Int32) → ffi::Int32> {isLeaf:#C7}
+ #C9 = core::pragma {name:#C1, options:#C8}
+}
diff --git a/pkg/vm/testcases/transformations/ffi/as_function.dart.expect b/pkg/vm/testcases/transformations/ffi/as_function.dart.expect
new file mode 100644
index 0000000..4bd77d2
--- /dev/null
+++ b/pkg/vm/testcases/transformations/ffi/as_function.dart.expect
@@ -0,0 +1,63 @@
+library #lib;
+import self as self;
+import "dart:ffi" as ffi;
+import "dart:core" as core;
+import "dart:_internal" as _in;
+
+import "dart:ffi";
+
+static method testVoidNoArg() → dynamic {
+ final ffi::Pointer<ffi::NativeFunction<() → ffi::Void>> pointer = ffi::Pointer::fromAddress<ffi::NativeFunction<() → ffi::Void>>(3735928559);
+ final () → void function = block {
+ synthesized ffi::Pointer<ffi::NativeFunction<() → ffi::Void>> #ffiTarget0 = pointer;
+ @#C4
+ function #ffiClosure0() → void {
+ return ffi::_ffiCall<void>(#ffiTarget0);
+ }
+ } =>#ffiClosure0;
+ function(){() → void};
+}
+static method testIntInt() → dynamic {
+ final ffi::Pointer<ffi::NativeFunction<(ffi::Int64) → ffi::Int32>> pointer = ffi::Pointer::fromAddress<ffi::NativeFunction<(ffi::Int64) → ffi::Int32>>(3735928559);
+ final (core::int) → core::int function = block {
+ synthesized ffi::Pointer<ffi::NativeFunction<(ffi::Int64) → ffi::Int32>> #ffiTarget1 = pointer;
+ @#C6
+ function #ffiClosure1(core::int arg1) → core::int {
+ _in::_nativeEffect(arg1);
+ return ffi::_ffiCall<core::int>(#ffiTarget1);
+ }
+ } =>#ffiClosure1;
+ return function(42){(core::int) → core::int};
+}
+static method testLeaf5Args() → dynamic {
+ 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);
+ final (core::int, core::int, core::int, core::int, core::int) → core::int function = block {
+ synthesized ffi::Pointer<ffi::NativeFunction<(ffi::Int32, ffi::Int32, ffi::Int32, ffi::Int32, ffi::Int32) → ffi::Int32>> #ffiTarget2 = pointer;
+ @#C9
+ function #ffiClosure2(core::int arg1, core::int arg2, core::int arg3, core::int arg4, core::int arg5) → core::int {
+ _in::_nativeEffect(arg1);
+ _in::_nativeEffect(arg2);
+ _in::_nativeEffect(arg3);
+ _in::_nativeEffect(arg4);
+ _in::_nativeEffect(arg5);
+ return ffi::_ffiCall<core::int>(#ffiTarget2);
+ }
+ } =>#ffiClosure2;
+ return function(1, 2, 3, 4, 5){(core::int, core::int, core::int, core::int, core::int) → core::int};
+}
+static method main() → void {
+ self::testVoidNoArg();
+ self::testIntInt();
+ self::testLeaf5Args();
+}
+constants {
+ #C1 = "vm:ffi:call-closure"
+ #C2 = false
+ #C3 = ffi::_FfiCall<() → ffi::Void> {isLeaf:#C2}
+ #C4 = core::pragma {name:#C1, options:#C3}
+ #C5 = ffi::_FfiCall<(ffi::Int64) → ffi::Int32> {isLeaf:#C2}
+ #C6 = core::pragma {name:#C1, options:#C5}
+ #C7 = true
+ #C8 = ffi::_FfiCall<(ffi::Int32, ffi::Int32, ffi::Int32, ffi::Int32, ffi::Int32) → ffi::Int32> {isLeaf:#C7}
+ #C9 = core::pragma {name:#C1, options:#C8}
+}
diff --git a/pkg/vm/testcases/transformations/type_flow/transformer/ffi_struct_constructors.dart.expect b/pkg/vm/testcases/transformations/type_flow/transformer/ffi_struct_constructors.dart.expect
index f6c28c4..2e9b3d2 100644
--- a/pkg/vm/testcases/transformations/type_flow/transformer/ffi_struct_constructors.dart.expect
+++ b/pkg/vm/testcases/transformations/type_flow/transformer/ffi_struct_constructors.dart.expect
@@ -67,7 +67,13 @@
final ffi::DynamicLibrary dylib = [@vm.inferred-type.metadata=dart.ffi::DynamicLibrary] ffi::DynamicLibrary::executable();
final () → self::Struct1 function1 = block {
_in::_nativeEffect(new self::Struct1::#fromTypedDataBase([@vm.inferred-type.metadata=dart.typed_data::_Uint8List] typ::Uint8List::•(#C18)));
- } =>ffi::_asFunctionInternal<() → self::Struct1, () → self::Struct1>([@vm.direct-call.metadata=dart.ffi::DynamicLibrary.lookup] [@vm.inferred-type.metadata=dart.ffi::Pointer (skip check)] dylib.{ffi::DynamicLibrary::lookup}<ffi::NativeFunction<() → self::Struct1>>("function1"){(core::String) → ffi::Pointer<ffi::NativeFunction<() → self::Struct1>>}, false);
+ } => block {
+ [@vm.inferred-type.metadata=dart.ffi::Pointer] synthesized ffi::Pointer<ffi::NativeFunction<() → self::Struct1>> #ffiTarget0 = [@vm.direct-call.metadata=dart.ffi::DynamicLibrary.lookup] [@vm.inferred-type.metadata=dart.ffi::Pointer (skip check)] dylib.{ffi::DynamicLibrary::lookup}<ffi::NativeFunction<() → self::Struct1>>("function1"){(core::String) → ffi::Pointer<ffi::NativeFunction<() → self::Struct1>>};
+ @#C22
+ function #ffiClosure0() → self::Struct1 {
+ return ffi::_ffiCall<self::Struct1>(#ffiTarget0);
+ }
+ } =>#ffiClosure0;
final self::Struct1 struct1 = function1(){() → self::Struct1};
core::print(struct1);
}
@@ -75,7 +81,13 @@
final ffi::Pointer<ffi::NativeFunction<() → self::Struct2>> pointer = [@vm.inferred-type.metadata=dart.ffi::Pointer] ffi::Pointer::fromAddress<ffi::NativeFunction<() → self::Struct2>>(3735928559);
final () → self::Struct2 function2 = block {
_in::_nativeEffect(new self::Struct2::#fromTypedDataBase([@vm.inferred-type.metadata=dart.typed_data::_Uint8List] typ::Uint8List::•(#C18)));
- } =>ffi::_asFunctionInternal<() → self::Struct2, () → self::Struct2>(pointer, false);
+ } => block {
+ [@vm.inferred-type.metadata=dart.ffi::Pointer] synthesized ffi::Pointer<ffi::NativeFunction<() → self::Struct2>> #ffiTarget1 = pointer;
+ @#C24
+ function #ffiClosure1() → self::Struct2 {
+ return ffi::_ffiCall<self::Struct2>(#ffiTarget1);
+ }
+ } =>#ffiClosure1;
final self::Struct2 struct2 = function2(){() → self::Struct2};
core::print(struct2);
}
@@ -90,12 +102,26 @@
}
static method testLookupFunctionArgument() → void {
final ffi::DynamicLibrary dylib = [@vm.inferred-type.metadata=dart.ffi::DynamicLibrary] ffi::DynamicLibrary::executable();
- final (self::Struct5) → void function5 = [@vm.inferred-type.metadata=dart.core::_Closure] ffi::_asFunctionInternal<(self::Struct5) → void, (self::Struct5) → ffi::Void>([@vm.direct-call.metadata=dart.ffi::DynamicLibrary.lookup] [@vm.inferred-type.metadata=dart.ffi::Pointer (skip check)] dylib.{ffi::DynamicLibrary::lookup}<ffi::NativeFunction<(self::Struct5) → ffi::Void>>("function5"){(core::String) → ffi::Pointer<ffi::NativeFunction<(self::Struct5) → ffi::Void>>}, false);
+ final (self::Struct5) → void function5 = block {
+ [@vm.inferred-type.metadata=dart.ffi::Pointer] synthesized ffi::Pointer<ffi::NativeFunction<(self::Struct5) → ffi::Void>> #ffiTarget2 = [@vm.direct-call.metadata=dart.ffi::DynamicLibrary.lookup] [@vm.inferred-type.metadata=dart.ffi::Pointer (skip check)] dylib.{ffi::DynamicLibrary::lookup}<ffi::NativeFunction<(self::Struct5) → ffi::Void>>("function5"){(core::String) → ffi::Pointer<ffi::NativeFunction<(self::Struct5) → ffi::Void>>};
+ @#C26
+ function #ffiClosure2(self::Struct5 arg1) → void {
+ _in::_nativeEffect(arg1);
+ return ffi::_ffiCall<void>(#ffiTarget2);
+ }
+ } =>#ffiClosure2;
core::print(function5);
}
static method testAsFunctionArgument() → void {
final ffi::Pointer<ffi::NativeFunction<(self::Struct6) → ffi::Void>> pointer = [@vm.inferred-type.metadata=dart.ffi::Pointer] ffi::Pointer::fromAddress<ffi::NativeFunction<(self::Struct6) → ffi::Void>>(3735928559);
- final (self::Struct6) → void function6 = [@vm.inferred-type.metadata=dart.core::_Closure] ffi::_asFunctionInternal<(self::Struct6) → void, (self::Struct6) → ffi::Void>(pointer, false);
+ final (self::Struct6) → void function6 = block {
+ [@vm.inferred-type.metadata=dart.ffi::Pointer] synthesized ffi::Pointer<ffi::NativeFunction<(self::Struct6) → ffi::Void>> #ffiTarget3 = pointer;
+ @#C28
+ function #ffiClosure3(self::Struct6 arg1) → void {
+ _in::_nativeEffect(arg1);
+ return ffi::_ffiCall<void>(#ffiTarget3);
+ }
+ } =>#ffiClosure3;
core::print(function6);
}
static method returnStruct7() → self::Struct7 {
@@ -135,4 +161,14 @@
#C16 = static-tearoff self::useStruct3
#C17 = static-tearoff self::returnStruct7
#C18 = 1
+ #C19 = "vm:ffi:call-closure"
+ #C20 = false
+ #C21 = ffi::_FfiCall<() → self::Struct1> {isLeaf:#C20}
+ #C22 = core::pragma {name:#C19, options:#C21}
+ #C23 = ffi::_FfiCall<() → self::Struct2> {isLeaf:#C20}
+ #C24 = core::pragma {name:#C19, options:#C23}
+ #C25 = ffi::_FfiCall<(self::Struct5) → ffi::Void> {isLeaf:#C20}
+ #C26 = core::pragma {name:#C19, options:#C25}
+ #C27 = ffi::_FfiCall<(self::Struct6) → ffi::Void> {isLeaf:#C20}
+ #C28 = core::pragma {name:#C19, options:#C27}
}
diff --git a/runtime/docs/compiler/ffi_pragmas.md b/runtime/docs/compiler/ffi_pragmas.md
index 545e8ed..6240f55 100644
--- a/runtime/docs/compiler/ffi_pragmas.md
+++ b/runtime/docs/compiler/ffi_pragmas.md
@@ -45,3 +45,11 @@
* [runtime/vm/kernel_loader.cc](../../../runtime/vm/kernel_loader.cc)
* [runtime/vm/object.cc](../../../runtime/vm/object.cc)
+## FFI Calls
+
+This pragma is used to mark Dart closures which perform FFI calls:
+
+```
+ @pragma('vm:ffi:call-closure', _FfiCall<Int32 Function(Int32)>(isLeaf: false))
+ int #ffiCall0(int arg1) => _ffiCall<int>(target);
+```
diff --git a/runtime/docs/pragmas.md b/runtime/docs/pragmas.md
index 120db16..4e274b4 100644
--- a/runtime/docs/pragmas.md
+++ b/runtime/docs/pragmas.md
@@ -47,6 +47,7 @@
| Pragma | Meaning |
| --- | --- |
+| `vm:ffi:call-closure`| [Closure performing FFI calls](compiler/ffi_pragmas.md) |
| `vm:ffi:native-assets` | [Passing a native assets mapping to the VM](compiler/ffi_pragmas.md) |
| `vm:ffi:native`| [Passing a native arguments to the VM](compiler/ffi_pragmas.md) |
diff --git a/runtime/lib/ffi.cc b/runtime/lib/ffi.cc
index 799dd78..0d06325 100644
--- a/runtime/lib/ffi.cc
+++ b/runtime/lib/ffi.cc
@@ -20,7 +20,6 @@
#if !defined(DART_PRECOMPILED_RUNTIME)
#include "vm/compiler/assembler/assembler.h"
-#include "vm/compiler/ffi/call.h"
#include "vm/compiler/ffi/callback.h"
#include "vm/compiler/ffi/marshaller.h"
#include "vm/compiler/jit/compiler.h"
@@ -28,11 +27,6 @@
namespace dart {
-// Static invocations to this method are translated directly in streaming FGB.
-DEFINE_NATIVE_ENTRY(Ffi_asFunctionInternal, 2, 2) {
- UNREACHABLE();
-}
-
DEFINE_NATIVE_ENTRY(Ffi_createNativeCallableListener, 1, 2) {
const auto& send_function =
Function::CheckedHandle(zone, arguments->NativeArg0());
diff --git a/runtime/vm/bootstrap_natives.h b/runtime/vm/bootstrap_natives.h
index b9e42ce..11be9be 100644
--- a/runtime/vm/bootstrap_natives.h
+++ b/runtime/vm/bootstrap_natives.h
@@ -321,7 +321,6 @@
V(VMService_DecodeAssets, 1) \
V(VMService_AddUserTagsToStreamableSampleList, 1) \
V(VMService_RemoveUserTagsFromStreamableSampleList, 1) \
- V(Ffi_asFunctionInternal, 2) \
V(Ffi_createNativeCallableListener, 2) \
V(Ffi_createNativeCallableIsolateLocal, 3) \
V(Ffi_deleteNativeCallable, 1) \
diff --git a/runtime/vm/canonical_tables.h b/runtime/vm/canonical_tables.h
index 70e4618..0b0f5ef 100644
--- a/runtime/vm/canonical_tables.h
+++ b/runtime/vm/canonical_tables.h
@@ -425,7 +425,7 @@
f1.FfiCSignature() == f2.FfiCSignature() &&
f1.FfiCallbackExceptionalReturn() ==
f2.FfiCallbackExceptionalReturn() &&
- f1.GetFfiFunctionKind() == f2.GetFfiFunctionKind());
+ f1.GetFfiCallbackKind() == f2.GetFfiCallbackKind());
}
static bool ReportStats() { return false; }
};
diff --git a/runtime/vm/compiler/aot/precompiler.cc b/runtime/vm/compiler/aot/precompiler.cc
index b98a9c4..2a28718 100644
--- a/runtime/vm/compiler/aot/precompiler.cc
+++ b/runtime/vm/compiler/aot/precompiler.cc
@@ -1044,7 +1044,7 @@
// Local closure function.
const auto& target = Function::Cast(entry);
AddFunction(target, RetainReasons::kLocalClosure);
- if (target.IsFfiTrampoline()) {
+ if (target.IsFfiCallbackTrampoline()) {
const auto& callback_target =
Function::Handle(Z, target.FfiCallbackTarget());
if (!callback_target.IsNull()) {
@@ -1117,7 +1117,7 @@
const Class& owner = Class::Handle(Z, function.Owner());
AddTypesOf(owner);
- if (function.IsFfiTrampoline()) {
+ if (function.IsFfiCallbackTrampoline()) {
AddType(FunctionType::Handle(Z, function.FfiCSignature()));
}
@@ -2030,7 +2030,7 @@
// Ffi trampoline functions are not reachable from program structure,
// they are referenced only from code (object pool).
if (!functions_to_retain_.ContainsKey(function) &&
- !function.IsFfiTrampoline()) {
+ !function.IsFfiCallbackTrampoline()) {
FATAL("Function %s was not traced in TraceForRetainedFunctions\n",
function.ToFullyQualifiedCString());
}
@@ -2189,7 +2189,7 @@
// which need the signature.
return AddRetainReason(sig, RetainReasons::kClosureSignature);
}
- if (function.IsFfiTrampoline()) {
+ if (function.IsFfiCallbackTrampoline()) {
// FFI trampolines may be dynamically called.
return AddRetainReason(sig, RetainReasons::kFfiTrampolineSignature);
}
@@ -3015,7 +3015,7 @@
}
// Retain Code objects corresponding to FFI trampolines.
- if (function_.IsFfiTrampoline()) {
+ if (function_.IsFfiCallbackTrampoline()) {
++codes_with_ffi_trampoline_function_;
return;
}
@@ -3455,8 +3455,7 @@
function.AttachCode(code);
}
- if (function.IsFfiTrampoline() &&
- function.GetFfiFunctionKind() != FfiFunctionKind::kCall) {
+ if (function.IsFfiCallbackTrampoline()) {
compiler::ffi::SetFfiCallbackCode(thread(), function, code);
}
}
diff --git a/runtime/vm/compiler/backend/flow_graph.cc b/runtime/vm/compiler/backend/flow_graph.cc
index 1688efe..b208aac 100644
--- a/runtime/vm/compiler/backend/flow_graph.cc
+++ b/runtime/vm/compiler/backend/flow_graph.cc
@@ -167,7 +167,7 @@
bool FlowGraph::ShouldReorderBlocks(const Function& function,
bool is_optimized) {
return is_optimized && FLAG_reorder_basic_blocks &&
- !function.is_intrinsic() && !function.IsFfiTrampoline();
+ !function.is_intrinsic() && !function.IsFfiCallbackTrampoline();
}
GrowableArray<BlockEntryInstr*>* FlowGraph::CodegenBlockOrder(
diff --git a/runtime/vm/compiler/backend/il_serializer.cc b/runtime/vm/compiler/backend/il_serializer.cc
index b2d5dfc..d3cbf79 100644
--- a/runtime/vm/compiler/backend/il_serializer.cc
+++ b/runtime/vm/compiler/backend/il_serializer.cc
@@ -11,7 +11,6 @@
#include "vm/compiler/backend/flow_graph.h"
#include "vm/compiler/backend/il.h"
#include "vm/compiler/backend/range_analysis.h"
-#include "vm/compiler/ffi/call.h"
#include "vm/compiler/frontend/flow_graph_builder.h"
#include "vm/object_store.h"
#include "vm/parser.h"
@@ -837,20 +836,12 @@
return;
}
case UntaggedFunction::kFfiTrampoline: {
- s->Write<uint8_t>(static_cast<uint8_t>(x.GetFfiFunctionKind()));
+ s->Write<uint8_t>(static_cast<uint8_t>(x.GetFfiCallbackKind()));
s->Write<const FunctionType&>(
FunctionType::Handle(zone, x.FfiCSignature()));
- if (x.GetFfiFunctionKind() != FfiFunctionKind::kCall) {
- s->Write<const Function&>(
- Function::Handle(zone, x.FfiCallbackTarget()));
- s->Write<const Instance&>(
- Instance::Handle(zone, x.FfiCallbackExceptionalReturn()));
- } else {
- s->Write<const String&>(String::Handle(zone, x.name()));
- s->Write<const FunctionType&>(
- FunctionType::Handle(zone, x.signature()));
- s->Write<bool>(x.FfiIsLeaf());
- }
+ s->Write<const Function&>(Function::Handle(zone, x.FfiCallbackTarget()));
+ s->Write<const Instance&>(
+ Instance::Handle(zone, x.FfiCallbackExceptionalReturn()));
return;
}
default:
@@ -927,23 +918,14 @@
target.GetDynamicInvocationForwarder(name));
}
case UntaggedFunction::kFfiTrampoline: {
- const FfiFunctionKind kind =
- static_cast<FfiFunctionKind>(d->Read<uint8_t>());
+ const FfiCallbackKind kind =
+ static_cast<FfiCallbackKind>(d->Read<uint8_t>());
const FunctionType& c_signature = d->Read<const FunctionType&>();
- if (kind != FfiFunctionKind::kCall) {
- const Function& callback_target = d->Read<const Function&>();
- const Instance& exceptional_return = d->Read<const Instance&>();
- return Function::ZoneHandle(
- zone, compiler::ffi::NativeCallbackFunction(
- c_signature, callback_target, exceptional_return, kind));
- } else {
- const String& name = d->Read<const String&>();
- const FunctionType& signature = d->Read<const FunctionType&>();
- const bool is_leaf = d->Read<bool>();
- return Function::ZoneHandle(
- zone, compiler::ffi::TrampolineFunction(name, signature,
- c_signature, is_leaf));
- }
+ const Function& callback_target = d->Read<const Function&>();
+ const Instance& exceptional_return = d->Read<const Instance&>();
+ return Function::ZoneHandle(
+ zone, compiler::ffi::NativeCallbackFunction(
+ c_signature, callback_target, exceptional_return, kind));
}
default:
UNIMPLEMENTED();
diff --git a/runtime/vm/compiler/compiler_sources.gni b/runtime/vm/compiler/compiler_sources.gni
index 9a051b6e..997bb8d 100644
--- a/runtime/vm/compiler/compiler_sources.gni
+++ b/runtime/vm/compiler/compiler_sources.gni
@@ -100,8 +100,6 @@
"compiler_timings.h",
"ffi/abi.cc",
"ffi/abi.h",
- "ffi/call.cc",
- "ffi/call.h",
"ffi/callback.cc",
"ffi/callback.h",
"ffi/frame_rebase.cc",
diff --git a/runtime/vm/compiler/ffi/call.cc b/runtime/vm/compiler/ffi/call.cc
deleted file mode 100644
index faf6936..0000000
--- a/runtime/vm/compiler/ffi/call.cc
+++ /dev/null
@@ -1,83 +0,0 @@
-// Copyright (c) 2020, 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.
-
-#include "vm/compiler/ffi/call.h"
-
-#include "vm/class_finalizer.h"
-#include "vm/symbols.h"
-
-namespace dart {
-
-namespace compiler {
-
-namespace ffi {
-
-// TODO(dartbug.com/36607): Cache the trampolines.
-FunctionPtr TrampolineFunction(const String& name,
- const FunctionType& signature,
- const FunctionType& c_signature,
- bool is_leaf) {
- ASSERT(signature.num_implicit_parameters() == 1);
- Thread* thread = Thread::Current();
- Zone* zone = thread->zone();
- const Library& lib = Library::Handle(zone, Library::FfiLibrary());
- const Class& owner_class = Class::Handle(zone, lib.toplevel_class());
- Function& function = Function::Handle(
- zone, Function::New(signature, name, UntaggedFunction::kFfiTrampoline,
- /*is_static=*/true,
- /*is_const=*/false,
- /*is_abstract=*/false,
- /*is_external=*/false,
- /*is_native=*/false, owner_class,
- TokenPosition::kMinSource));
- function.set_is_debuggable(false);
-
- // Create unique names for the parameters, as they are used in scope building
- // and error messages.
- if (signature.num_fixed_parameters() > 0) {
- function.CreateNameArray();
- function.SetParameterNameAt(0, Symbols::ClosureParameter());
- auto& param_name = String::Handle(zone);
- for (intptr_t i = 1, n = signature.num_fixed_parameters(); i < n; ++i) {
- param_name = Symbols::NewFormatted(thread, ":ffi_param%" Pd, i);
- function.SetParameterNameAt(i, param_name);
- }
- }
-
- function.SetFfiCSignature(c_signature);
- function.SetFfiIsLeaf(is_leaf);
- function.SetFfiFunctionKind(FfiFunctionKind::kCall);
-
- return function.ptr();
-}
-
-FunctionPtr TrampolineFunction(const FunctionType& dart_signature,
- const FunctionType& c_signature,
- bool is_leaf,
- const String& function_name) {
- Thread* thread = Thread::Current();
- Zone* zone = thread->zone();
- String& name =
- String::Handle(zone, Symbols::NewFormatted(thread, "FfiTrampoline_%s",
- function_name.ToCString()));
-
- // Trampolines have no optional arguments.
- FunctionType& signature = FunctionType::Handle(zone, FunctionType::New());
- const intptr_t num_fixed = dart_signature.num_fixed_parameters();
- signature.set_num_implicit_parameters(1);
- signature.set_num_fixed_parameters(num_fixed);
- signature.set_result_type(
- AbstractType::Handle(zone, dart_signature.result_type()));
- signature.set_parameter_types(
- Array::Handle(zone, dart_signature.parameter_types()));
- signature ^= ClassFinalizer::FinalizeType(signature);
-
- return TrampolineFunction(name, signature, c_signature, is_leaf);
-}
-
-} // namespace ffi
-
-} // namespace compiler
-
-} // namespace dart
diff --git a/runtime/vm/compiler/ffi/call.h b/runtime/vm/compiler/ffi/call.h
deleted file mode 100644
index 11c328a..0000000
--- a/runtime/vm/compiler/ffi/call.h
+++ /dev/null
@@ -1,38 +0,0 @@
-// Copyright (c) 2020, 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.
-
-#ifndef RUNTIME_VM_COMPILER_FFI_CALL_H_
-#define RUNTIME_VM_COMPILER_FFI_CALL_H_
-
-#if defined(DART_PRECOMPILED_RUNTIME)
-#error "AOT runtime should not use compiler sources (including header files)"
-#endif // defined(DART_PRECOMPILED_RUNTIME)
-
-#include <platform/globals.h>
-
-#include "vm/raw_object.h"
-
-namespace dart {
-
-namespace compiler {
-
-namespace ffi {
-
-FunctionPtr TrampolineFunction(const String& name,
- const FunctionType& signature,
- const FunctionType& c_signature,
- bool is_leaf);
-
-FunctionPtr TrampolineFunction(const FunctionType& dart_signature,
- const FunctionType& c_signature,
- bool is_leaf,
- const String& function_name);
-
-} // namespace ffi
-
-} // namespace compiler
-
-} // namespace dart
-
-#endif // RUNTIME_VM_COMPILER_FFI_CALL_H_
diff --git a/runtime/vm/compiler/ffi/callback.cc b/runtime/vm/compiler/ffi/callback.cc
index 7a9ea1c..984b2a0 100644
--- a/runtime/vm/compiler/ffi/callback.cc
+++ b/runtime/vm/compiler/ffi/callback.cc
@@ -18,13 +18,13 @@
const String& NativeCallbackFunctionName(Thread* thread,
Zone* zone,
const Function& dart_target,
- FfiFunctionKind kind) {
+ FfiCallbackKind kind) {
switch (kind) {
- case FfiFunctionKind::kAsyncCallback:
+ case FfiCallbackKind::kAsyncCallback:
return Symbols::FfiAsyncCallback();
- case FfiFunctionKind::kIsolateLocalClosureCallback:
+ case FfiCallbackKind::kIsolateLocalClosureCallback:
return Symbols::FfiIsolateLocalCallback();
- case FfiFunctionKind::kIsolateLocalStaticCallback:
+ case FfiCallbackKind::kIsolateLocalStaticCallback:
return String::Handle(
zone, Symbols::FromConcat(thread, Symbols::FfiCallback(),
String::Handle(zone, dart_target.name())));
@@ -36,7 +36,7 @@
FunctionPtr NativeCallbackFunction(const FunctionType& c_signature,
const Function& dart_target,
const Instance& exceptional_return,
- FfiFunctionKind kind) {
+ FfiCallbackKind kind) {
Thread* const thread = Thread::Current();
Zone* const zone = thread->zone();
Function& function = Function::Handle(zone);
@@ -64,7 +64,7 @@
// the body.
function.SetFfiCSignature(c_signature);
function.SetFfiCallbackTarget(dart_target);
- function.SetFfiFunctionKind(kind);
+ function.SetFfiCallbackKind(kind);
// We need to load the exceptional return value as a constant in the generated
// function. Even though the FE ensures that it is a constant, it could still
diff --git a/runtime/vm/compiler/ffi/callback.h b/runtime/vm/compiler/ffi/callback.h
index 88af9a99..02b4294 100644
--- a/runtime/vm/compiler/ffi/callback.h
+++ b/runtime/vm/compiler/ffi/callback.h
@@ -23,7 +23,7 @@
FunctionPtr NativeCallbackFunction(const FunctionType& c_signature,
const Function& dart_target,
const Instance& exceptional_return,
- FfiFunctionKind kind);
+ FfiCallbackKind kind);
// Builds a mapping from `callback-id` to code object / ...
//
diff --git a/runtime/vm/compiler/frontend/base_flow_graph_builder.cc b/runtime/vm/compiler/frontend/base_flow_graph_builder.cc
index 59c2d68..8b78359 100644
--- a/runtime/vm/compiler/frontend/base_flow_graph_builder.cc
+++ b/runtime/vm/compiler/frontend/base_flow_graph_builder.cc
@@ -7,7 +7,6 @@
#include <utility>
#include "vm/compiler/backend/range_analysis.h" // For Range.
-#include "vm/compiler/ffi/call.h"
#include "vm/compiler/frontend/flow_graph_builder.h" // For InlineExitCollector.
#include "vm/compiler/jit/compiler.h" // For Compiler::IsBackgroundCompilation().
#include "vm/compiler/runtime_api.h"
@@ -1025,65 +1024,6 @@
return Fragment(box);
}
-Fragment BaseFlowGraphBuilder::BuildFfiAsFunctionInternalCall(
- const TypeArguments& signatures,
- bool is_leaf) {
- ASSERT(signatures.Length() == 2);
- const auto& sig0 = AbstractType::Handle(signatures.TypeAt(0));
- const auto& sig1 = AbstractType::Handle(signatures.TypeAt(1));
-
- if (!signatures.IsInstantiated() || !sig0.IsFunctionType() ||
- !sig1.IsFunctionType()) {
- const auto& msg = String::Handle(String::NewFormatted(
- "Invalid type arguments passed to dart:ffi _asFunctionInternal: %s",
- String::Handle(signatures.UserVisibleName()).ToCString()));
- const auto& language_error =
- Error::Handle(LanguageError::New(msg, Report::kError, Heap::kOld));
- Report::LongJump(language_error);
- }
-
- const auto& dart_type = FunctionType::Cast(sig0);
- const auto& native_type = FunctionType::Cast(sig1);
-
- // AbiSpecificTypes can have an incomplete mapping.
- const char* error = nullptr;
- compiler::ffi::NativeFunctionTypeFromFunctionType(zone_, native_type, &error);
- if (error != nullptr) {
- const auto& language_error = Error::Handle(
- LanguageError::New(String::Handle(String::New(error, Heap::kOld)),
- Report::kError, Heap::kOld));
- Report::LongJump(language_error);
- }
-
- const auto& name =
- String::Handle(parsed_function_->function().UserVisibleName());
- const Function& target = Function::ZoneHandle(
- compiler::ffi::TrampolineFunction(dart_type, native_type, is_leaf, name));
-
- Fragment code;
- // Store the pointer in the context, we cannot load the untagged address
- // here as these can be unoptimized call sites.
- LocalVariable* pointer = MakeTemporary();
-
- code += Constant(target);
-
- auto& context_slots = CompilerState::Current().GetDummyContextSlots(
- /*context_id=*/0, /*num_variables=*/1);
- code += AllocateContext(context_slots);
- LocalVariable* context = MakeTemporary();
-
- code += LoadLocal(context);
- code += LoadLocal(pointer);
- code += StoreNativeField(*context_slots[0]);
-
- code += AllocateClosure();
-
- // Drop address.
- code += DropTempsPreserveTop(1);
-
- return code;
-}
-
Fragment BaseFlowGraphBuilder::DebugStepCheck(TokenPosition position) {
#ifdef PRODUCT
return Fragment();
diff --git a/runtime/vm/compiler/frontend/base_flow_graph_builder.h b/runtime/vm/compiler/frontend/base_flow_graph_builder.h
index ab8c026..e3df988 100644
--- a/runtime/vm/compiler/frontend/base_flow_graph_builder.h
+++ b/runtime/vm/compiler/frontend/base_flow_graph_builder.h
@@ -405,12 +405,6 @@
return stack_ == nullptr ? 0 : stack_->definition()->temp_index() + 1;
}
- // Builds the graph for an invocation of '_asFunctionInternal'.
- //
- // 'signatures' contains the pair [<dart signature>, <native signature>].
- Fragment BuildFfiAsFunctionInternalCall(const TypeArguments& signatures,
- bool is_leaf);
-
Fragment AllocateObject(TokenPosition position,
const Class& klass,
intptr_t argument_count);
diff --git a/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc b/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc
index 78984ab..ee5c2be 100644
--- a/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc
+++ b/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc
@@ -3348,18 +3348,18 @@
return BuildNativeEffect();
case MethodRecognizer::kReachabilityFence:
return BuildReachabilityFence();
- case MethodRecognizer::kFfiAsFunctionInternal:
- return BuildFfiAsFunctionInternal();
+ case MethodRecognizer::kFfiCall:
+ return BuildFfiCall();
case MethodRecognizer::kFfiNativeCallbackFunction:
return BuildFfiNativeCallbackFunction(
- FfiFunctionKind::kIsolateLocalStaticCallback);
+ FfiCallbackKind::kIsolateLocalStaticCallback);
case MethodRecognizer::kFfiNativeAddressOf:
return BuildFfiNativeAddressOf();
case MethodRecognizer::kFfiNativeIsolateLocalCallbackFunction:
return BuildFfiNativeCallbackFunction(
- FfiFunctionKind::kIsolateLocalClosureCallback);
+ FfiCallbackKind::kIsolateLocalClosureCallback);
case MethodRecognizer::kFfiNativeAsyncCallbackFunction:
- return BuildFfiNativeCallbackFunction(FfiFunctionKind::kAsyncCallback);
+ return BuildFfiNativeCallbackFunction(FfiCallbackKind::kAsyncCallback);
case MethodRecognizer::kFfiLoadAbiSpecificInt:
return BuildLoadAbiSpecificInt(/*at_index=*/false);
case MethodRecognizer::kFfiLoadAbiSpecificIntAtIndex:
@@ -6221,34 +6221,46 @@
return code;
}
-Fragment StreamingFlowGraphBuilder::BuildFfiAsFunctionInternal() {
+Fragment StreamingFlowGraphBuilder::BuildFfiCall() {
const intptr_t argc = ReadUInt(); // Read argument count.
- ASSERT(argc == 2); // Pointer, isLeaf.
+ ASSERT(argc == 1); // Target pointer.
const intptr_t list_length = ReadListLength(); // Read types list length.
- ASSERT(list_length == 2); // Dart signature, then native signature
- // Read types.
- const TypeArguments& type_arguments = T.BuildTypeArguments(list_length);
- Fragment code;
+ T.BuildTypeArguments(list_length); // Read types.
// Read positional argument count.
const intptr_t positional_count = ReadListLength();
- ASSERT(positional_count == 2);
- code += BuildExpression(); // Build first positional argument (pointer).
+ ASSERT(positional_count == argc);
- // The second argument, `isLeaf`, is only used internally and dictates whether
- // we can do a lightweight leaf function call.
- bool is_leaf = false;
- Fragment frag = BuildExpression();
- ASSERT(frag.entry->IsConstant());
- if (frag.entry->AsConstant()->value().ptr() == Object::bool_true().ptr()) {
- is_leaf = true;
- }
- Pop();
+ Fragment code;
+ // Push the target function pointer passed as Pointer object.
+ code += BuildExpression();
+ // This can only be Pointer, so it is always safe to LoadUntagged.
+ code += B->LoadUntagged(compiler::target::PointerBase::data_offset());
+ code += B->ConvertUntaggedToUnboxed(kUnboxedFfiIntPtr);
// Skip (empty) named arguments list.
const intptr_t named_args_len = ReadListLength();
ASSERT(named_args_len == 0);
- code += B->BuildFfiAsFunctionInternalCall(type_arguments, is_leaf);
+ const auto& native_type = FunctionType::ZoneHandle(
+ Z, parsed_function()->function().FfiCSignature());
+
+ // AbiSpecificTypes can have an incomplete mapping.
+ const char* error = nullptr;
+ compiler::ffi::NativeFunctionTypeFromFunctionType(Z, native_type, &error);
+ if (error != nullptr) {
+ const auto& language_error = Error::Handle(
+ LanguageError::New(String::Handle(String::New(error, Heap::kOld)),
+ Report::kError, Heap::kOld));
+ Report::LongJump(language_error);
+ }
+
+ code += B->FfiCallFunctionBody(parsed_function()->function(), native_type,
+ /*first_argument_parameter_offset=*/1);
+
+ ASSERT(code.is_closed());
+
+ NullConstant(); // Maintain stack balance.
+
return code;
}
@@ -6313,23 +6325,23 @@
}
Fragment StreamingFlowGraphBuilder::BuildFfiNativeCallbackFunction(
- FfiFunctionKind kind) {
+ FfiCallbackKind kind) {
// The call-site must look like this (guaranteed by the FE which inserts it):
//
- // FfiFunctionKind::kIsolateLocalStaticCallback:
+ // FfiCallbackKind::kIsolateLocalStaticCallback:
// _nativeCallbackFunction<NativeSignatureType>(target, exceptionalReturn)
//
- // FfiFunctionKind::kAsyncCallback:
+ // FfiCallbackKind::kAsyncCallback:
// _nativeAsyncCallbackFunction<NativeSignatureType>()
//
- // FfiFunctionKind::kIsolateLocalClosureCallback:
+ // FfiCallbackKind::kIsolateLocalClosureCallback:
// _nativeIsolateLocalCallbackFunction<NativeSignatureType>(
// exceptionalReturn)
//
// The FE also guarantees that the arguments are constants.
- const bool has_target = kind == FfiFunctionKind::kIsolateLocalStaticCallback;
- const bool has_exceptional_return = kind != FfiFunctionKind::kAsyncCallback;
+ const bool has_target = kind == FfiCallbackKind::kIsolateLocalStaticCallback;
+ const bool has_exceptional_return = kind != FfiCallbackKind::kAsyncCallback;
const intptr_t expected_argc =
static_cast<int>(has_target) + static_cast<int>(has_exceptional_return);
diff --git a/runtime/vm/compiler/frontend/kernel_binary_flowgraph.h b/runtime/vm/compiler/frontend/kernel_binary_flowgraph.h
index e05f6e3..475d660 100644
--- a/runtime/vm/compiler/frontend/kernel_binary_flowgraph.h
+++ b/runtime/vm/compiler/frontend/kernel_binary_flowgraph.h
@@ -387,13 +387,12 @@
Fragment BuildLoadAbiSpecificInt(bool at_index);
Fragment BuildStoreAbiSpecificInt(bool at_index);
- // Build FG for '_asFunctionInternal'. Reads an Arguments from the
- // Kernel buffer and pushes the resulting closure.
- Fragment BuildFfiAsFunctionInternal();
+ // Build FG for FFI call.
+ Fragment BuildFfiCall();
// Build FG for '_nativeCallbackFunction'. Reads an Arguments from the
// Kernel buffer and pushes the resulting Function object.
- Fragment BuildFfiNativeCallbackFunction(FfiFunctionKind kind);
+ Fragment BuildFfiNativeCallbackFunction(FfiCallbackKind kind);
Fragment BuildFfiNativeAddressOf();
diff --git a/runtime/vm/compiler/frontend/kernel_to_il.cc b/runtime/vm/compiler/frontend/kernel_to_il.cc
index 903781e..c360281 100644
--- a/runtime/vm/compiler/frontend/kernel_to_il.cc
+++ b/runtime/vm/compiler/frontend/kernel_to_il.cc
@@ -400,11 +400,12 @@
}
Fragment FlowGraphBuilder::FfiCall(
- const compiler::ffi::CallMarshaller& marshaller) {
+ const compiler::ffi::CallMarshaller& marshaller,
+ bool is_leaf) {
Fragment body;
- FfiCallInstr* const call = new (Z) FfiCallInstr(
- GetNextDeoptId(), marshaller, parsed_function_->function().FfiIsLeaf());
+ FfiCallInstr* const call =
+ new (Z) FfiCallInstr(GetNextDeoptId(), marshaller, is_leaf);
for (intptr_t i = call->InputCount() - 1; i >= 0; --i) {
call->SetInputAt(i, Pop());
@@ -5030,14 +5031,12 @@
FlowGraph* FlowGraphBuilder::BuildGraphOfFfiTrampoline(
const Function& function) {
- switch (function.GetFfiFunctionKind()) {
- case FfiFunctionKind::kIsolateLocalStaticCallback:
- case FfiFunctionKind::kIsolateLocalClosureCallback:
+ switch (function.GetFfiCallbackKind()) {
+ case FfiCallbackKind::kIsolateLocalStaticCallback:
+ case FfiCallbackKind::kIsolateLocalClosureCallback:
return BuildGraphOfSyncFfiCallback(function);
- case FfiFunctionKind::kAsyncCallback:
+ case FfiCallbackKind::kAsyncCallback:
return BuildGraphOfAsyncFfiCallback(function);
- case FfiFunctionKind::kCall:
- return BuildGraphOfFfiCall(function);
}
UNREACHABLE();
return nullptr;
@@ -5128,26 +5127,6 @@
return FfiNativeLookupAddress(native_instance);
}
-Fragment FlowGraphBuilder::FfiCallLookupAddress(const Function& function) {
- ASSERT(function.IsFfiTrampoline());
- const intptr_t kClosureParameterOffset = 0;
- Fragment body;
- // Push the function pointer, which is stored (as Pointer object) in the
- // first slot of the context.
- body +=
- LoadLocal(parsed_function_->ParameterVariable(kClosureParameterOffset));
- body += LoadNativeField(Slot::Closure_context());
- body += LoadNativeField(Slot::GetContextVariableSlotFor(
- thread_, *MakeImplicitClosureScope(
- Z, Class::Handle(IG->object_store()->ffi_pointer_class()))
- ->context_variables()[0]));
-
- // This can only be Pointer, so it is always safe to LoadUntagged.
- body += LoadUntagged(compiler::target::PointerBase::data_offset());
- body += ConvertUntaggedToUnboxed(kUnboxedFfiIntPtr);
- return body;
-}
-
Fragment FlowGraphBuilder::FfiNativeFunctionBody(const Function& function) {
ASSERT(function.is_ffi_native());
ASSERT(!IsRecognizedMethodForFlowGraph(function));
@@ -5157,18 +5136,16 @@
Fragment body;
body += FfiNativeLookupAddress(function);
- body += FfiCallFunctionBody(function, c_signature);
+ body += FfiCallFunctionBody(function, c_signature,
+ /*first_argument_parameter_offset=*/0);
return body;
}
Fragment FlowGraphBuilder::FfiCallFunctionBody(
const Function& function,
- const FunctionType& c_signature) {
- ASSERT(function.is_ffi_native() || function.IsFfiTrampoline());
- const bool is_ffi_native = function.is_ffi_native();
- const intptr_t kClosureParameterOffset = 0;
- const intptr_t first_argument_parameter_offset =
- is_ffi_native ? 0 : kClosureParameterOffset + 1;
+ const FunctionType& c_signature,
+ intptr_t first_argument_parameter_offset) {
+ ASSERT(function.is_ffi_native() || function.IsFfiCallClosure());
LocalVariable* address = MakeTemporary("address");
@@ -5258,7 +5235,7 @@
body += LoadLocal(return_compound_typed_data);
}
- body += FfiCall(marshaller);
+ body += FfiCall(marshaller, function.FfiIsLeaf());
for (intptr_t i = 0; i < marshaller.num_args(); i++) {
if (marshaller.IsPointer(i)) {
@@ -5321,31 +5298,6 @@
return body;
}
-FlowGraph* FlowGraphBuilder::BuildGraphOfFfiCall(const Function& function) {
- graph_entry_ =
- new (Z) GraphEntryInstr(*parsed_function_, Compiler::kNoOSRDeoptId);
-
- auto normal_entry = BuildFunctionEntry(graph_entry_);
- graph_entry_->set_normal_entry(normal_entry);
-
- PrologueInfo prologue_info(-1, -1);
-
- BlockEntryInstr* instruction_cursor =
- BuildPrologue(normal_entry, &prologue_info);
-
- Fragment function_body(instruction_cursor);
- function_body += CheckStackOverflowInPrologue(function.token_pos());
-
- const auto& c_signature =
- FunctionType::ZoneHandle(Z, function.FfiCSignature());
-
- function_body += FfiCallLookupAddress(function);
- function_body += FfiCallFunctionBody(function, c_signature);
-
- return new (Z) FlowGraph(*parsed_function_, graph_entry_, last_used_block_id_,
- prologue_info);
-}
-
Fragment FlowGraphBuilder::LoadNativeArg(
const compiler::ffi::CallbackMarshaller& marshaller,
intptr_t arg_index) {
@@ -5381,8 +5333,8 @@
RELEASE_ASSERT(error == nullptr);
RELEASE_ASSERT(marshaller_ptr != nullptr);
const auto& marshaller = *marshaller_ptr;
- const bool is_closure = function.GetFfiFunctionKind() ==
- FfiFunctionKind::kIsolateLocalClosureCallback;
+ const bool is_closure = function.GetFfiCallbackKind() ==
+ FfiCallbackKind::kIsolateLocalClosureCallback;
graph_entry_ =
new (Z) GraphEntryInstr(*parsed_function_, Compiler::kNoOSRDeoptId);
diff --git a/runtime/vm/compiler/frontend/kernel_to_il.h b/runtime/vm/compiler/frontend/kernel_to_il.h
index 88fa285..c112a32 100644
--- a/runtime/vm/compiler/frontend/kernel_to_il.h
+++ b/runtime/vm/compiler/frontend/kernel_to_il.h
@@ -137,8 +137,6 @@
FlowGraph* BuildGraphOfFfiTrampoline(const Function& function);
FlowGraph* BuildGraphOfSyncFfiCallback(const Function& function);
FlowGraph* BuildGraphOfAsyncFfiCallback(const Function& function);
- FlowGraph* BuildGraphOfFfiCall(const Function& function);
- Fragment FfiCallLookupAddress(const Function& function);
// Resolves the address of a native symbol from the constant data of a
// vm:ffi:native pragma.
@@ -150,7 +148,8 @@
Fragment FfiNativeLookupAddress(const Function& function);
// Expects target address on stack.
Fragment FfiCallFunctionBody(const Function& function,
- const FunctionType& c_signature);
+ const FunctionType& c_signature,
+ intptr_t first_argument_parameter_offset);
Fragment FfiNativeFunctionBody(const Function& function);
Fragment NativeFunctionBody(const Function& function,
LocalVariable* first_parameter);
@@ -204,7 +203,8 @@
bool receiver_is_not_smi = false,
bool is_call_on_this = false);
- Fragment FfiCall(const compiler::ffi::CallMarshaller& marshaller);
+ Fragment FfiCall(const compiler::ffi::CallMarshaller& marshaller,
+ bool is_leaf);
Fragment CCall(
const compiler::ffi::NativeCallingConvention& native_calling_convention);
diff --git a/runtime/vm/compiler/frontend/scope_builder.cc b/runtime/vm/compiler/frontend/scope_builder.cc
index 328eae0..435d92f 100644
--- a/runtime/vm/compiler/frontend/scope_builder.cc
+++ b/runtime/vm/compiler/frontend/scope_builder.cc
@@ -154,8 +154,7 @@
FunctionNodeHelper::kPositionalParameters);
// NOTE: FunctionNode is read further below the if.
- intptr_t pos = 0;
- if (function.is_ffi_native()) {
+ if (function.is_ffi_native() || function.IsFfiCallClosure()) {
needs_expr_temp_ = true;
// Calls with handles need try/catch variables.
if (function.FfiCSignatureContainsHandles()) {
@@ -167,7 +166,9 @@
FinalizeCatchVariables();
--depth_.catch_;
}
- } else if (function.IsClosureFunction()) {
+ }
+ intptr_t pos = 0;
+ if (function.IsClosureFunction()) {
LocalVariable* closure_parameter = MakeVariable(
TokenPosition::kNoSource, TokenPosition::kNoSource,
Symbols::ClosureParameter(), AbstractType::dynamic_type());
@@ -406,17 +407,14 @@
}
case UntaggedFunction::kFfiTrampoline: {
needs_expr_temp_ = true;
- // Callbacks and calls with handles need try/catch variables.
- if ((function.GetFfiFunctionKind() != FfiFunctionKind::kCall ||
- function.FfiCSignatureContainsHandles())) {
- ++depth_.try_;
- AddTryVariables();
- --depth_.try_;
- ++depth_.catch_;
- AddCatchVariables();
- FinalizeCatchVariables();
- --depth_.catch_;
- }
+ // Callbacks need try/catch variables.
+ ++depth_.try_;
+ AddTryVariables();
+ --depth_.try_;
+ ++depth_.catch_;
+ AddCatchVariables();
+ FinalizeCatchVariables();
+ --depth_.catch_;
FALL_THROUGH;
}
case UntaggedFunction::kInvokeFieldDispatcher: {
@@ -437,7 +435,7 @@
LocalVariable* variable = MakeVariable(
TokenPosition::kNoSource, TokenPosition::kNoSource,
String::ZoneHandle(Z, function.ParameterNameAt(i)),
- AbstractType::ZoneHandle(Z, function.IsFfiTrampoline()
+ AbstractType::ZoneHandle(Z, function.IsFfiCallbackTrampoline()
? function.ParameterTypeAt(i)
: Object::dynamic_type().ptr()));
bool added = scope_->InsertParameterAt(i, variable);
diff --git a/runtime/vm/compiler/jit/compiler.cc b/runtime/vm/compiler/jit/compiler.cc
index ff2122f..46b07a2 100644
--- a/runtime/vm/compiler/jit/compiler.cc
+++ b/runtime/vm/compiler/jit/compiler.cc
@@ -476,8 +476,7 @@
}
}
- if (function.IsFfiTrampoline() &&
- function.GetFfiFunctionKind() != FfiFunctionKind::kCall) {
+ if (function.IsFfiCallbackTrampoline()) {
compiler::ffi::SetFfiCallbackCode(thread(), function, code);
}
diff --git a/runtime/vm/compiler/recognized_methods_list.h b/runtime/vm/compiler/recognized_methods_list.h
index 3f0dcc4..8b47dc3 100644
--- a/runtime/vm/compiler/recognized_methods_list.h
+++ b/runtime/vm/compiler/recognized_methods_list.h
@@ -272,7 +272,7 @@
V(_WeakReference, get:target, WeakReference_getTarget, 0xc98185aa) \
V(_WeakReference, set:_target, WeakReference_setTarget, 0xc71add9a) \
V(::, _abi, FfiAbi, 0x7c3c2b95) \
- V(::, _asFunctionInternal, FfiAsFunctionInternal, 0x630c8491) \
+ V(::, _ffiCall, FfiCall, 0x6118e962) \
V(::, _nativeCallbackFunction, FfiNativeCallbackFunction, 0x3fe722bc) \
V(::, _nativeAsyncCallbackFunction, FfiNativeAsyncCallbackFunction, \
0xbec4b7b9) \
diff --git a/runtime/vm/ffi_callback_metadata.cc b/runtime/vm/ffi_callback_metadata.cc
index c8d5f3c..93febd1 100644
--- a/runtime/vm/ffi_callback_metadata.cc
+++ b/runtime/vm/ffi_callback_metadata.cc
@@ -275,13 +275,13 @@
if (closure.IsNull()) {
// If the closure is null, it means the target is a static function, so is
// baked into the trampoline and is an ordinary sync callback.
- ASSERT(function.GetFfiFunctionKind() ==
- FfiFunctionKind::kIsolateLocalStaticCallback);
+ ASSERT(function.GetFfiCallbackKind() ==
+ FfiCallbackKind::kIsolateLocalStaticCallback);
return CreateSyncFfiCallbackImpl(isolate, zone, function, nullptr,
list_head);
} else {
- ASSERT(function.GetFfiFunctionKind() ==
- FfiFunctionKind::kIsolateLocalClosureCallback);
+ ASSERT(function.GetFfiCallbackKind() ==
+ FfiCallbackKind::kIsolateLocalClosureCallback);
return CreateSyncFfiCallbackImpl(isolate, zone, function,
CreatePersistentHandle(isolate, closure),
list_head);
@@ -319,7 +319,7 @@
const Function& send_function,
Dart_Port send_port,
Metadata** list_head) {
- ASSERT(send_function.GetFfiFunctionKind() == FfiFunctionKind::kAsyncCallback);
+ ASSERT(send_function.GetFfiCallbackKind() == FfiCallbackKind::kAsyncCallback);
return CreateMetadataEntry(isolate, TrampolineType::kAsync,
GetEntryPoint(zone, send_function),
static_cast<uint64_t>(send_port), list_head);
diff --git a/runtime/vm/ffi_callback_metadata_test.cc b/runtime/vm/ffi_callback_metadata_test.cc
index 2040e1d..b09896b 100644
--- a/runtime/vm/ffi_callback_metadata_test.cc
+++ b/runtime/vm/ffi_callback_metadata_test.cc
@@ -22,7 +22,7 @@
namespace dart {
-FunctionPtr CreateTestFunction(FfiFunctionKind kind) {
+FunctionPtr CreateTestFunction(FfiCallbackKind kind) {
const auto& ffi_lib = Library::Handle(Library::FfiLibrary());
const auto& ffi_void = Class::Handle(ffi_lib.LookupClass(Symbols::FfiVoid()));
const auto& ffi_void_type =
@@ -92,7 +92,7 @@
auto* zone = thread->zone();
const auto& func = Function::Handle(
- CreateTestFunction(FfiFunctionKind::kIsolateLocalStaticCallback));
+ CreateTestFunction(FfiCallbackKind::kIsolateLocalStaticCallback));
const auto& code = Code::Handle(func.EnsureHasCode());
EXPECT(!code.IsNull());
@@ -185,7 +185,7 @@
auto* zone = thread->zone();
const Function& func =
- Function::Handle(CreateTestFunction(FfiFunctionKind::kAsyncCallback));
+ Function::Handle(CreateTestFunction(FfiCallbackKind::kAsyncCallback));
const Code& code = Code::Handle(func.EnsureHasCode());
EXPECT(!code.IsNull());
@@ -280,13 +280,13 @@
auto* zone = thread->zone();
const Function& func = Function::Handle(
- CreateTestFunction(FfiFunctionKind::kIsolateLocalClosureCallback));
+ CreateTestFunction(FfiCallbackKind::kIsolateLocalClosureCallback));
const Code& code = Code::Handle(func.EnsureHasCode());
EXPECT(!code.IsNull());
- // Using a FfiFunctionKind::kSync function as a dummy closure.
+ // Using a FfiCallbackKind::kSync function as a dummy closure.
const Function& closure_func = Function::Handle(
- CreateTestFunction(FfiFunctionKind::kIsolateLocalStaticCallback));
+ CreateTestFunction(FfiCallbackKind::kIsolateLocalStaticCallback));
const Context& context = Context::Handle(Context::null());
const Closure& closure1 = Closure::Handle(
Closure::New(Object::null_type_arguments(),
@@ -373,7 +373,7 @@
auto* fcm = FfiCallbackMetadata::Instance();
const Function& func =
- Function::Handle(CreateTestFunction(FfiFunctionKind::kAsyncCallback));
+ Function::Handle(CreateTestFunction(FfiCallbackKind::kAsyncCallback));
const Code& code = Code::Handle(func.EnsureHasCode());
EXPECT(!code.IsNull());
@@ -440,7 +440,7 @@
FfiCallbackMetadata::Metadata* list_head = nullptr;
const auto& sync_func = Function::Handle(
- CreateTestFunction(FfiFunctionKind::kIsolateLocalStaticCallback));
+ CreateTestFunction(FfiCallbackKind::kIsolateLocalStaticCallback));
const auto& sync_code = Code::Handle(sync_func.EnsureHasCode());
EXPECT(!sync_code.IsNull());
@@ -521,11 +521,11 @@
FfiCallbackMetadata::Metadata* list_head = nullptr;
const Function& async_func =
- Function::Handle(CreateTestFunction(FfiFunctionKind::kAsyncCallback));
+ Function::Handle(CreateTestFunction(FfiCallbackKind::kAsyncCallback));
const Code& async_code = Code::Handle(async_func.EnsureHasCode());
EXPECT(!async_code.IsNull());
const Function& sync_func = Function::Handle(
- CreateTestFunction(FfiFunctionKind::kIsolateLocalStaticCallback));
+ CreateTestFunction(FfiCallbackKind::kIsolateLocalStaticCallback));
const auto& sync_code = Code::Handle(sync_func.EnsureHasCode());
EXPECT(!sync_code.IsNull());
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc
index 1fd399b..ae81ed8 100644
--- a/runtime/vm/object.cc
+++ b/runtime/vm/object.cc
@@ -8337,7 +8337,8 @@
FunctionPtr Function::implicit_closure_function() const {
if (IsClosureFunction() || IsDispatcherOrImplicitAccessor() ||
- IsFieldInitializer() || IsFfiTrampoline() || IsMethodExtractor()) {
+ IsFieldInitializer() || IsFfiCallbackTrampoline() ||
+ IsMethodExtractor()) {
return Function::null();
}
const Object& obj = Object::Handle(data());
@@ -8372,7 +8373,7 @@
}
void Function::SetFfiCSignature(const FunctionType& sig) const {
- ASSERT(IsFfiTrampoline());
+ ASSERT(IsFfiCallbackTrampoline());
const Object& obj = Object::Handle(data());
ASSERT(!obj.IsNull());
FfiTrampolineData::Cast(obj).set_c_signature(sig);
@@ -8380,15 +8381,21 @@
FunctionTypePtr Function::FfiCSignature() const {
auto* const zone = Thread::Current()->zone();
- if (IsFfiTrampoline()) {
+ if (IsFfiCallbackTrampoline()) {
const Object& obj = Object::Handle(zone, data());
ASSERT(!obj.IsNull());
return FfiTrampolineData::Cast(obj).c_signature();
}
- ASSERT(is_ffi_native());
- auto const& native_instance = Instance::Handle(GetNativeAnnotation());
+ auto& pragma_value = Instance::Handle(zone);
+ if (is_ffi_native()) {
+ pragma_value = GetNativeAnnotation();
+ } else if (IsFfiCallClosure()) {
+ pragma_value = GetFfiCallClosurePragmaValue();
+ } else {
+ UNREACHABLE();
+ }
const auto& type_args =
- TypeArguments::Handle(zone, native_instance.GetTypeArguments());
+ TypeArguments::Handle(zone, pragma_value.GetTypeArguments());
ASSERT(type_args.Length() == 1);
const auto& native_type =
FunctionType::Cast(AbstractType::ZoneHandle(zone, type_args.TypeAt(0)));
@@ -8415,7 +8422,7 @@
// Keep consistent with BaseMarshaller::IsCompound.
bool Function::FfiCSignatureReturnsStruct() const {
- ASSERT(IsFfiTrampoline());
+ ASSERT(IsFfiCallbackTrampoline());
Zone* zone = Thread::Current()->zone();
const auto& c_signature = FunctionType::Handle(zone, FfiCSignature());
const auto& type = AbstractType::Handle(zone, c_signature.result_type());
@@ -8441,8 +8448,7 @@
}
int32_t Function::FfiCallbackId() const {
- ASSERT(IsFfiTrampoline());
- ASSERT(GetFfiFunctionKind() != FfiFunctionKind::kCall);
+ ASSERT(IsFfiCallbackTrampoline());
const auto& obj = Object::Handle(data());
ASSERT(!obj.IsNull());
@@ -8454,8 +8460,7 @@
}
void Function::AssignFfiCallbackId(int32_t callback_id) const {
- ASSERT(IsFfiTrampoline());
- ASSERT(GetFfiFunctionKind() != FfiFunctionKind::kCall);
+ ASSERT(IsFfiCallbackTrampoline());
const auto& obj = Object::Handle(data());
ASSERT(!obj.IsNull());
@@ -8466,69 +8471,64 @@
}
bool Function::FfiIsLeaf() const {
- if (IsFfiTrampoline()) {
- const Object& obj = Object::Handle(untag()->data());
- ASSERT(!obj.IsNull());
- return FfiTrampolineData::Cast(obj).is_leaf();
- }
- ASSERT(is_ffi_native());
Zone* zone = Thread::Current()->zone();
- auto const& native_instance = Instance::Handle(GetNativeAnnotation());
- const auto& native_class = Class::Handle(zone, native_instance.clazz());
- const auto& native_class_fields = Array::Handle(zone, native_class.fields());
- ASSERT(native_class_fields.Length() == 4);
- const auto& is_leaf_field =
- Field::Handle(zone, Field::RawCast(native_class_fields.At(3)));
- ASSERT(!is_leaf_field.is_static());
- return Bool::Handle(zone,
- Bool::RawCast(native_instance.GetField(is_leaf_field)))
+ auto& pragma_value = Instance::Handle(zone);
+ if (is_ffi_native()) {
+ pragma_value = GetNativeAnnotation();
+ } else if (IsFfiCallClosure()) {
+ pragma_value = GetFfiCallClosurePragmaValue();
+ } else {
+ UNREACHABLE();
+ }
+ const auto& pragma_value_class = Class::Handle(zone, pragma_value.clazz());
+ const auto& pragma_value_fields =
+ Array::Handle(zone, pragma_value_class.fields());
+ ASSERT(pragma_value_fields.Length() >= 1);
+ const auto& is_leaf_field = Field::Handle(
+ zone,
+ Field::RawCast(pragma_value_fields.At(pragma_value_fields.Length() - 1)));
+ ASSERT(is_leaf_field.name() == Symbols::isLeaf().ptr());
+ return Bool::Handle(zone, Bool::RawCast(pragma_value.GetField(is_leaf_field)))
.value();
}
-void Function::SetFfiIsLeaf(bool is_leaf) const {
- ASSERT(IsFfiTrampoline());
- const Object& obj = Object::Handle(untag()->data());
- ASSERT(!obj.IsNull());
- FfiTrampolineData::Cast(obj).set_is_leaf(is_leaf);
-}
-
FunctionPtr Function::FfiCallbackTarget() const {
- ASSERT(IsFfiTrampoline());
+ ASSERT(IsFfiCallbackTrampoline());
const Object& obj = Object::Handle(data());
ASSERT(!obj.IsNull());
return FfiTrampolineData::Cast(obj).callback_target();
}
void Function::SetFfiCallbackTarget(const Function& target) const {
- ASSERT(IsFfiTrampoline());
+ ASSERT(IsFfiCallbackTrampoline());
const Object& obj = Object::Handle(data());
ASSERT(!obj.IsNull());
FfiTrampolineData::Cast(obj).set_callback_target(target);
}
InstancePtr Function::FfiCallbackExceptionalReturn() const {
- ASSERT(IsFfiTrampoline());
+ ASSERT(IsFfiCallbackTrampoline());
const Object& obj = Object::Handle(data());
ASSERT(!obj.IsNull());
return FfiTrampolineData::Cast(obj).callback_exceptional_return();
}
void Function::SetFfiCallbackExceptionalReturn(const Instance& value) const {
- ASSERT(IsFfiTrampoline());
+ ASSERT(IsFfiCallbackTrampoline());
const Object& obj = Object::Handle(data());
ASSERT(!obj.IsNull());
FfiTrampolineData::Cast(obj).set_callback_exceptional_return(value);
}
-FfiFunctionKind Function::GetFfiFunctionKind() const {
- ASSERT(IsFfiTrampoline());
+FfiCallbackKind Function::GetFfiCallbackKind() const {
+ ASSERT(IsFfiCallbackTrampoline());
const Object& obj = Object::Handle(data());
ASSERT(!obj.IsNull());
return FfiTrampolineData::Cast(obj).ffi_function_kind();
}
-void Function::SetFfiFunctionKind(FfiFunctionKind value) const {
- ASSERT(IsFfiTrampoline());
+void Function::SetFfiCallbackKind(FfiCallbackKind value) const {
+ ASSERT(IsFfiCallbackTrampoline());
const Object& obj = Object::Handle(data());
ASSERT(!obj.IsNull());
FfiTrampolineData::Cast(obj).set_ffi_function_kind(value);
@@ -9133,7 +9133,8 @@
}
bool Function::ForceOptimize() const {
- if (RecognizedKindForceOptimize() || IsFfiTrampoline() || is_ffi_native() ||
+ if (RecognizedKindForceOptimize() || IsFfiCallClosure() ||
+ IsFfiCallbackTrampoline() || is_ffi_native() ||
IsTypedDataViewFactory() || IsUnmodifiableTypedDataViewFactory()) {
return true;
}
@@ -9174,6 +9175,25 @@
return InVmTests(*this);
}
+bool Function::IsFfiCallClosure() const {
+ if (!IsNonImplicitClosureFunction()) return false;
+ if (!has_pragma()) return false;
+ return Library::FindPragma(Thread::Current(), /*only_core=*/false, *this,
+ Symbols::vm_ffi_call_closure());
+}
+
+InstancePtr Function::GetFfiCallClosurePragmaValue() const {
+ ASSERT(IsFfiCallClosure());
+ Thread* thread = Thread::Current();
+ Zone* zone = thread->zone();
+ auto& pragma_value = Object::Handle(zone);
+ Library::FindPragma(thread, /*only_core=*/false, *this,
+ Symbols::vm_ffi_call_closure(),
+ /*multiple=*/false, &pragma_value);
+ ASSERT(!pragma_value.IsNull());
+ return Instance::Cast(pragma_value).ptr();
+}
+
bool Function::RecognizedKindForceOptimize() const {
switch (recognized_kind()) {
// Uses unboxed/untagged data not supported in unoptimized.
@@ -9248,7 +9268,7 @@
#if !defined(DART_PRECOMPILED_RUNTIME)
bool Function::CanBeInlined() const {
if (ForceOptimize()) {
- if (IsFfiTrampoline() || is_ffi_native()) {
+ if (IsFfiCallClosure() || IsFfiCallbackTrampoline() || is_ffi_native()) {
// We currently don't support inlining FFI trampolines. Some of them
// are naturally non-inlinable because they contain a try/catch block,
// but this condition is broader than strictly necessary.
@@ -11000,7 +11020,7 @@
intptr_t Function::KernelLibraryIndex() const {
if (IsNoSuchMethodDispatcher() || IsInvokeFieldDispatcher() ||
- IsFfiTrampoline()) {
+ IsFfiCallbackTrampoline()) {
return -1;
}
if (is_eval_function()) {
@@ -11763,16 +11783,12 @@
StoreNonPointer(&untag()->callback_id_, callback_id);
}
-void FfiTrampolineData::set_is_leaf(bool is_leaf) const {
- StoreNonPointer(&untag()->is_leaf_, is_leaf);
-}
-
void FfiTrampolineData::set_callback_exceptional_return(
const Instance& value) const {
untag()->set_callback_exceptional_return(value.ptr());
}
-void FfiTrampolineData::set_ffi_function_kind(FfiFunctionKind kind) const {
+void FfiTrampolineData::set_ffi_function_kind(FfiCallbackKind kind) const {
StoreNonPointer(&untag()->ffi_function_kind_, static_cast<uint8_t>(kind));
}
diff --git a/runtime/vm/object.h b/runtime/vm/object.h
index 80ba527..255cab4 100644
--- a/runtime/vm/object.h
+++ b/runtime/vm/object.h
@@ -2949,8 +2949,7 @@
}
};
-enum class FfiFunctionKind : uint8_t {
- kCall,
+enum class FfiCallbackKind : uint8_t {
kIsolateLocalStaticCallback,
kIsolateLocalClosureCallback,
kAsyncCallback,
@@ -2986,37 +2985,31 @@
bool FfiCSignatureReturnsStruct() const;
// Can only be called on FFI trampolines.
- // -1 for Dart -> native calls.
int32_t FfiCallbackId() const;
// Should be called when ffi trampoline function object is created.
void AssignFfiCallbackId(int32_t callback_id) const;
- // Can only be called on FFI trampolines.
+ // Can only be called on FFI natives and FFI call closures.
bool FfiIsLeaf() const;
// Can only be called on FFI trampolines.
- void SetFfiIsLeaf(bool is_leaf) const;
-
- // Can only be called on FFI trampolines.
- // Null for Dart -> native calls.
FunctionPtr FfiCallbackTarget() const;
// Can only be called on FFI trampolines.
void SetFfiCallbackTarget(const Function& target) const;
// Can only be called on FFI trampolines.
- // Null for Dart -> native calls.
InstancePtr FfiCallbackExceptionalReturn() const;
// Can only be called on FFI trampolines.
void SetFfiCallbackExceptionalReturn(const Instance& value) const;
// Can only be called on FFI trampolines.
- FfiFunctionKind GetFfiFunctionKind() const;
+ FfiCallbackKind GetFfiCallbackKind() const;
// Can only be called on FFI trampolines.
- void SetFfiFunctionKind(FfiFunctionKind value) const;
+ void SetFfiCallbackKind(FfiCallbackKind value) const;
// Return the signature of this function.
PRECOMPILER_WSR_FIELD_DECLARATION(FunctionType, signature);
@@ -3902,15 +3895,22 @@
}
// Returns true if this function represents an ffi trampoline.
- bool IsFfiTrampoline() const {
+ bool IsFfiCallbackTrampoline() const {
return kind() == UntaggedFunction::kFfiTrampoline;
}
- static bool IsFfiTrampoline(FunctionPtr function) {
+ static bool IsFfiCallbackTrampoline(FunctionPtr function) {
NoSafepointScope no_safepoint;
return function->untag()->kind_tag_.Read<KindBits>() ==
UntaggedFunction::kFfiTrampoline;
}
+ // Returns true if this function is a closure function
+ // used to represent ffi call.
+ bool IsFfiCallClosure() const;
+
+ // Returns value of vm:ffi:call-closure pragma.
+ InstancePtr GetFfiCallClosurePragmaValue() const;
+
// Returns true for functions which execution can be suspended
// using Suspend/Resume stubs. Such functions have an artificial
// :suspend_state local variable at the fixed location of the frame.
@@ -4347,17 +4347,14 @@
}
void set_callback_exceptional_return(const Instance& value) const;
- FfiFunctionKind ffi_function_kind() const {
- return static_cast<FfiFunctionKind>(untag()->ffi_function_kind_);
+ FfiCallbackKind ffi_function_kind() const {
+ return static_cast<FfiCallbackKind>(untag()->ffi_function_kind_);
}
- void set_ffi_function_kind(FfiFunctionKind kind) const;
+ void set_ffi_function_kind(FfiCallbackKind kind) const;
int32_t callback_id() const { return untag()->callback_id_; }
void set_callback_id(int32_t value) const;
- bool is_leaf() const { return untag()->is_leaf_; }
- void set_is_leaf(bool value) const;
-
static FfiTrampolineDataPtr New();
FINAL_HEAP_OBJECT_IMPLEMENTATION(FfiTrampolineData, Object);
diff --git a/runtime/vm/raw_object.h b/runtime/vm/raw_object.h
index 291d46e..15e8b7f 100644
--- a/runtime/vm/raw_object.h
+++ b/runtime/vm/raw_object.h
@@ -1501,10 +1501,7 @@
// Check 'callback_target_' to determine if this is a callback or not.
int32_t callback_id_;
- // Whether this is a leaf call - i.e. one that doesn't call back into Dart.
- bool is_leaf_;
-
- // The kind of trampoline this is. See FfiFunctionKind.
+ // The kind of trampoline this is. See FfiCallbackKind.
uint8_t ffi_function_kind_;
};
diff --git a/runtime/vm/resolver.cc b/runtime/vm/resolver.cc
index 1d1b196..1947035 100644
--- a/runtime/vm/resolver.cc
+++ b/runtime/vm/resolver.cc
@@ -129,7 +129,7 @@
// FfiTrampolines are the only functions that can still be called
// dynamically without going through a dynamic invocation forwarder.
RELEASE_ASSERT(!Function::IsDynamicInvocationForwarderName(function_name) &&
- !function.IsFfiTrampoline());
+ !function.IsFfiCallbackTrampoline());
// The signature for this function was dropped in the precompiler, which
// means it is not a possible target for a dynamic call in the program.
// That means we're resolving an UnlinkedCall for an InstanceCall to
diff --git a/runtime/vm/symbols.h b/runtime/vm/symbols.h
index 846d717..c937627 100644
--- a/runtime/vm/symbols.h
+++ b/runtime/vm/symbols.h
@@ -501,6 +501,7 @@
V(from, "from") \
V(get, "get") \
V(index_temp, ":index_temp") \
+ V(isLeaf, "isLeaf") \
V(isPaused, "isPaused") \
V(match_end_index, ":match_end_index") \
V(match_start_index, ":match_start_index") \
@@ -529,6 +530,7 @@
V(vm_exact_result_type, "vm:exact-result-type") \
V(vm_external_name, "vm:external-name") \
V(vm_ffi_abi_specific_mapping, "vm:ffi:abi-specific-mapping") \
+ V(vm_ffi_call_closure, "vm:ffi:call-closure") \
V(vm_ffi_native, "vm:ffi:native") \
V(vm_ffi_native_assets, "vm:ffi:native-assets") \
V(vm_ffi_struct_fields, "vm:ffi:struct-fields") \
diff --git a/sdk/lib/_internal/vm/lib/ffi_patch.dart b/sdk/lib/_internal/vm/lib/ffi_patch.dart
index b1dd9ec..6c1e88f 100644
--- a/sdk/lib/_internal/vm/lib/ffi_patch.dart
+++ b/sdk/lib/_internal/vm/lib/ffi_patch.dart
@@ -82,13 +82,21 @@
@pragma("vm:idempotent")
external Pointer<T> _fromAddress<T extends NativeType>(int ptr);
-// The real implementation of this function (for interface calls) lives in
-// BuildFfiAsFunctionInternal in the Kernel frontend. No calls can actually
-// reach this function.
+/// Argument for vm:ffi:call-closure pragma describing FFI call.
+final class _FfiCall<NativeSignature> {
+ // Implementation note: VM hardcodes the layout of this class (number and
+ // order of its fields), so adding/removing/changing fields requires
+ // updating the VM code (see Function::GetFfiCallClosurePragmaValue()).
+ final bool isLeaf;
+ const _FfiCall({this.isLeaf = false});
+}
+
+// Helper function to perform FFI call.
+// Inserted by FFI kernel transformation into the FFI call closures.
+// Implemented in BuildFfiCall
+// in runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc.
@pragma("vm:recognized", "other")
-@pragma("vm:external-name", "Ffi_asFunctionInternal")
-external DS _asFunctionInternal<DS extends Function, NS extends Function>(
- Pointer<NativeFunction<NS>> ptr, bool isLeaf);
+external ReturnType _ffiCall<ReturnType>(Pointer<NativeFunction> target);
@pragma("vm:recognized", "other")
@pragma("vm:idempotent")
diff --git a/sdk/lib/ffi/ffi.dart b/sdk/lib/ffi/ffi.dart
index 577e329..2eec984 100644
--- a/sdk/lib/ffi/ffi.dart
+++ b/sdk/lib/ffi/ffi.dart
@@ -69,7 +69,7 @@
/// On 32-bit systems, the upper 32-bits of the result are 0.
external int get address;
- /// Cast Pointer<T> to a Pointer<V>.
+ /// Cast Pointer<T> to a Pointer<U>.
external Pointer<U> cast<U extends NativeType>();
/// Equality for Pointers only depends on their address.
@@ -1556,6 +1556,10 @@
/// NOTE: This is an experimental feature and may change in the future.
@Since('2.19')
final class Native<T> {
+ // Implementation note: VM hardcodes the layout of this class (number and
+ // order of its fields), so adding/removing/changing fields requires
+ // updating the VM code (see Function::GetNativeAnnotation()).
+
/// The native symbol to be resolved, if not using the default.
///
/// If not specified, the default symbol used for native function lookup
diff --git a/tests/ffi/abi_specific_int_incomplete_jit_test.dart b/tests/ffi/abi_specific_int_incomplete_jit_test.dart
index 54f6843..9038f6e 100644
--- a/tests/ffi/abi_specific_int_incomplete_jit_test.dart
+++ b/tests/ffi/abi_specific_int_incomplete_jit_test.dart
@@ -119,21 +119,27 @@
Expect.throws(() {
nullptr
.cast<NativeFunction<Int32 Function(Incomplete)>>()
- .asFunction<int Function(int)>();
+ .asFunction<int Function(int)>()
+ .call(42);
});
Expect.throws(() {
nullptr
.cast<NativeFunction<Incomplete Function(Int32)>>()
- .asFunction<int Function(int)>();
+ .asFunction<int Function(int)>()
+ .call(42);
});
+ final p = calloc<Int64>(100).cast<IncompleteArrayStruct>();
Expect.throws(() {
nullptr
.cast<NativeFunction<Int32 Function(IncompleteArrayStruct)>>()
- .asFunction<int Function(IncompleteArrayStruct)>();
+ .asFunction<int Function(IncompleteArrayStruct)>()
+ .call(p.ref);
});
+ calloc.free(p);
Expect.throws(() {
nullptr
.cast<NativeFunction<IncompleteArrayStruct Function()>>()
- .asFunction<IncompleteArrayStruct Function()>();
+ .asFunction<IncompleteArrayStruct Function()>()
+ .call();
});
}
diff --git a/tests/ffi/function_test.dart b/tests/ffi/function_test.dart
index 6d4c77e..1e7d5aa 100644
--- a/tests/ffi/function_test.dart
+++ b/tests/ffi/function_test.dart
@@ -459,6 +459,12 @@
Expect.approxEquals(1337.0, result);
}
+// Returns a possibly ofuscated 'arg2' identifier.
+String get arg2ObfuscatedName {
+ final str = (arg2: 0).toString();
+ return str.substring('('.length, str.length - ': 0)'.length);
+}
+
void testNativeFunctionNullableInt() {
final sumPlus42 = ffiTestFunctions.lookupFunction<
Int32 Function(Int32, Int32), int Function(int, int?)>("SumPlus42");
@@ -467,7 +473,7 @@
sumPlus42(3, null);
} catch (e) {
// TODO(http://dartbug.com/47098): Save param names to dwarf.
- Expect.isTrue(e.toString().contains('ffi_param2') ||
+ Expect.isTrue(e.toString().contains(arg2ObfuscatedName) ||
e.toString().contains('<optimized out>'));
}
}