mirror of
https://github.com/dart-lang/sdk
synced 2024-10-04 17:04:56 +00:00
[VM/FFI] Adds FFI leaf calls.
This CL adds FFI leaf calls by adding `lookupFunction(.., isLeaf)` and `_asFunctionInternal(.., isLeaf)`, which generate FFI leaf calls. These calls skip a lot of the usual frame building and generated <-> native transition overhead. `benchmark/FfiCall/` shows a 1.1x - 4.3x speed-up between the regular FFI calls and their leaf call counterparts (JIT, x64, release). TEST=Adds `tests/ffi{,_2}/vmspecific_leaf_call_test.dart`. Tested FFI tests. Closes: https://github.com/dart-lang/sdk/issues/36707 Cq-Include-Trybots: luci.dart.try:vm-precomp-ffi-qemu-linux-release-arm-try,vm-ffi-android-release-arm64-try,vm-ffi-android-release-arm-try,vm-ffi-android-product-arm64-try,vm-ffi-android-product-arm-try,vm-ffi-android-debug-arm64-try,vm-ffi-android-debug-arm-try,vm-kernel-linux-debug-ia32-try,vm-kernel-win-debug-ia32-try,vm-kernel-win-debug-x64-try,vm-kernel-win-release-x64-try,vm-kernel-mac-debug-x64-try,vm-kernel-precomp-nnbd-mac-release-simarm64-try,vm-kernel-precomp-android-release-arm64-try,vm-kernel-precomp-asan-linux-release-x64-try,vm-kernel-precomp-linux-release-simarm_x64-try,vm-kernel-precomp-obfuscate-linux-release-x64-try,vm-kernel-precomp-ubsan-linux-release-x64-try,vm-kernel-precomp-tsan-linux-release-x64-try,vm-kernel-precomp-win-release-x64-try,vm-precomp-ffi-qemu-linux-release-arm-try,vm-kernel-reload-rollback-linux-debug-x64-try,vm-kernel-reload-linux-debug-x64-try Bug: https://github.com/dart-lang/sdk/issues/36707 Change-Id: Id8824f36b0006bf09951207bd004356fe6e9f46e Cq-Do-Not-Cancel-Tryjobs: true Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/179768 Commit-Queue: Clement Skau <cskau@google.com> Reviewed-by: Daco Harkes <dacoharkes@google.com> Reviewed-by: Martin Kustermann <kustermann@google.com>
This commit is contained in:
parent
f364c8bf7b
commit
4d5055805f
|
@ -283,7 +283,10 @@ typedef Function20Object = Object Function(
|
|||
//
|
||||
|
||||
abstract class FfiBenchmarkBase extends BenchmarkBase {
|
||||
FfiBenchmarkBase(String name) : super(name);
|
||||
final bool isLeaf;
|
||||
|
||||
FfiBenchmarkBase(String name, {this.isLeaf: false})
|
||||
: super('$name${isLeaf ? 'Leaf' : ''}');
|
||||
|
||||
void expectEquals(actual, expected) {
|
||||
if (actual != expected) {
|
||||
|
@ -307,10 +310,13 @@ abstract class FfiBenchmarkBase extends BenchmarkBase {
|
|||
class Uint8x01 extends FfiBenchmarkBase {
|
||||
final Function1int f;
|
||||
|
||||
Uint8x01()
|
||||
: f = ffiTestFunctions.lookupFunction<NativeFunction1Uint8, Function1int>(
|
||||
'Function1Uint8'),
|
||||
super('FfiCall.Uint8x01');
|
||||
Uint8x01({isLeaf: false})
|
||||
: f = isLeaf
|
||||
? ffiTestFunctions.lookupFunction<NativeFunction1Uint8,
|
||||
Function1int>('Function1Uint8', isLeaf: true)
|
||||
: ffiTestFunctions.lookupFunction<NativeFunction1Uint8,
|
||||
Function1int>('Function1Uint8', isLeaf: false),
|
||||
super('FfiCall.Uint8x01', isLeaf: isLeaf);
|
||||
|
||||
@override
|
||||
void run() {
|
||||
|
@ -325,10 +331,13 @@ class Uint8x01 extends FfiBenchmarkBase {
|
|||
class Uint16x01 extends FfiBenchmarkBase {
|
||||
final Function1int f;
|
||||
|
||||
Uint16x01()
|
||||
: f = ffiTestFunctions.lookupFunction<NativeFunction1Uint16,
|
||||
Function1int>('Function1Uint16'),
|
||||
super('FfiCall.Uint16x01');
|
||||
Uint16x01({isLeaf: false})
|
||||
: f = isLeaf
|
||||
? ffiTestFunctions.lookupFunction<NativeFunction1Uint16,
|
||||
Function1int>('Function1Uint16', isLeaf: true)
|
||||
: ffiTestFunctions.lookupFunction<NativeFunction1Uint16,
|
||||
Function1int>('Function1Uint16', isLeaf: false),
|
||||
super('FfiCall.Uint16x01', isLeaf: isLeaf);
|
||||
|
||||
@override
|
||||
void run() {
|
||||
|
@ -343,10 +352,13 @@ class Uint16x01 extends FfiBenchmarkBase {
|
|||
class Uint32x01 extends FfiBenchmarkBase {
|
||||
final Function1int f;
|
||||
|
||||
Uint32x01()
|
||||
: f = ffiTestFunctions.lookupFunction<NativeFunction1Uint32,
|
||||
Function1int>('Function1Uint32'),
|
||||
super('FfiCall.Uint32x01');
|
||||
Uint32x01({isLeaf: false})
|
||||
: f = isLeaf
|
||||
? ffiTestFunctions.lookupFunction<NativeFunction1Uint32,
|
||||
Function1int>('Function1Uint32', isLeaf: true)
|
||||
: ffiTestFunctions.lookupFunction<NativeFunction1Uint32,
|
||||
Function1int>('Function1Uint32', isLeaf: false),
|
||||
super('FfiCall.Uint32x01', isLeaf: isLeaf);
|
||||
|
||||
@override
|
||||
void run() {
|
||||
|
@ -361,10 +373,13 @@ class Uint32x01 extends FfiBenchmarkBase {
|
|||
class Uint64x01 extends FfiBenchmarkBase {
|
||||
final Function1int f;
|
||||
|
||||
Uint64x01()
|
||||
: f = ffiTestFunctions.lookupFunction<NativeFunction1Uint64,
|
||||
Function1int>('Function1Uint64'),
|
||||
super('FfiCall.Uint64x01');
|
||||
Uint64x01({isLeaf: false})
|
||||
: f = isLeaf
|
||||
? ffiTestFunctions.lookupFunction<NativeFunction1Uint64,
|
||||
Function1int>('Function1Uint64', isLeaf: true)
|
||||
: ffiTestFunctions.lookupFunction<NativeFunction1Uint64,
|
||||
Function1int>('Function1Uint64', isLeaf: false),
|
||||
super('FfiCall.Uint64x01', isLeaf: isLeaf);
|
||||
|
||||
@override
|
||||
void run() {
|
||||
|
@ -379,10 +394,13 @@ class Uint64x01 extends FfiBenchmarkBase {
|
|||
class Int8x01 extends FfiBenchmarkBase {
|
||||
final Function1int f;
|
||||
|
||||
Int8x01()
|
||||
: f = ffiTestFunctions
|
||||
.lookupFunction<NativeFunction1Int8, Function1int>('Function1Int8'),
|
||||
super('FfiCall.Int8x01');
|
||||
Int8x01({isLeaf: false})
|
||||
: f = isLeaf
|
||||
? ffiTestFunctions.lookupFunction<NativeFunction1Int8,
|
||||
Function1int>('Function1Int8', isLeaf: true)
|
||||
: ffiTestFunctions.lookupFunction<NativeFunction1Int8,
|
||||
Function1int>('Function1Int8', isLeaf: false),
|
||||
super('FfiCall.Int8x01', isLeaf: isLeaf);
|
||||
|
||||
@override
|
||||
void run() {
|
||||
|
@ -397,10 +415,13 @@ class Int8x01 extends FfiBenchmarkBase {
|
|||
class Int16x01 extends FfiBenchmarkBase {
|
||||
final Function1int f;
|
||||
|
||||
Int16x01()
|
||||
: f = ffiTestFunctions.lookupFunction<NativeFunction1Int16, Function1int>(
|
||||
'Function1Int16'),
|
||||
super('FfiCall.Int16x01');
|
||||
Int16x01({isLeaf: false})
|
||||
: f = isLeaf
|
||||
? ffiTestFunctions.lookupFunction<NativeFunction1Int16,
|
||||
Function1int>('Function1Int16', isLeaf: true)
|
||||
: ffiTestFunctions.lookupFunction<NativeFunction1Int16,
|
||||
Function1int>('Function1Int16', isLeaf: false),
|
||||
super('FfiCall.Int16x01', isLeaf: isLeaf);
|
||||
|
||||
@override
|
||||
void run() {
|
||||
|
@ -415,10 +436,13 @@ class Int16x01 extends FfiBenchmarkBase {
|
|||
class Int32x01 extends FfiBenchmarkBase {
|
||||
final Function1int f;
|
||||
|
||||
Int32x01()
|
||||
: f = ffiTestFunctions.lookupFunction<NativeFunction1Int32, Function1int>(
|
||||
'Function1Int32'),
|
||||
super('FfiCall.Int32x01');
|
||||
Int32x01({isLeaf: false})
|
||||
: f = isLeaf
|
||||
? ffiTestFunctions.lookupFunction<NativeFunction1Int32,
|
||||
Function1int>('Function1Int32', isLeaf: true)
|
||||
: ffiTestFunctions.lookupFunction<NativeFunction1Int32,
|
||||
Function1int>('Function1Int32', isLeaf: false),
|
||||
super('FfiCall.Int32x01', isLeaf: isLeaf);
|
||||
|
||||
@override
|
||||
void run() {
|
||||
|
@ -433,10 +457,13 @@ class Int32x01 extends FfiBenchmarkBase {
|
|||
class Int32x02 extends FfiBenchmarkBase {
|
||||
final Function2int f;
|
||||
|
||||
Int32x02()
|
||||
: f = ffiTestFunctions.lookupFunction<NativeFunction2Int32, Function2int>(
|
||||
'Function2Int32'),
|
||||
super('FfiCall.Int32x02');
|
||||
Int32x02({isLeaf: false})
|
||||
: f = isLeaf
|
||||
? ffiTestFunctions.lookupFunction<NativeFunction2Int32,
|
||||
Function2int>('Function2Int32', isLeaf: true)
|
||||
: ffiTestFunctions.lookupFunction<NativeFunction2Int32,
|
||||
Function2int>('Function2Int32', isLeaf: false),
|
||||
super('FfiCall.Int32x02', isLeaf: isLeaf);
|
||||
|
||||
@override
|
||||
void run() {
|
||||
|
@ -451,10 +478,13 @@ class Int32x02 extends FfiBenchmarkBase {
|
|||
class Int32x04 extends FfiBenchmarkBase {
|
||||
final Function4int f;
|
||||
|
||||
Int32x04()
|
||||
: f = ffiTestFunctions.lookupFunction<NativeFunction4Int32, Function4int>(
|
||||
'Function4Int32'),
|
||||
super('FfiCall.Int32x04');
|
||||
Int32x04({isLeaf: false})
|
||||
: f = isLeaf
|
||||
? ffiTestFunctions.lookupFunction<NativeFunction4Int32,
|
||||
Function4int>('Function4Int32', isLeaf: true)
|
||||
: ffiTestFunctions.lookupFunction<NativeFunction4Int32,
|
||||
Function4int>('Function4Int32', isLeaf: false),
|
||||
super('FfiCall.Int32x04', isLeaf: isLeaf);
|
||||
|
||||
@override
|
||||
void run() {
|
||||
|
@ -469,10 +499,13 @@ class Int32x04 extends FfiBenchmarkBase {
|
|||
class Int32x10 extends FfiBenchmarkBase {
|
||||
final Function10int f;
|
||||
|
||||
Int32x10()
|
||||
: f = ffiTestFunctions.lookupFunction<NativeFunction10Int32,
|
||||
Function10int>('Function10Int32'),
|
||||
super('FfiCall.Int32x10');
|
||||
Int32x10({isLeaf: false})
|
||||
: f = isLeaf
|
||||
? ffiTestFunctions.lookupFunction<NativeFunction10Int32,
|
||||
Function10int>('Function10Int32', isLeaf: true)
|
||||
: ffiTestFunctions.lookupFunction<NativeFunction10Int32,
|
||||
Function10int>('Function10Int32', isLeaf: false),
|
||||
super('FfiCall.Int32x10', isLeaf: isLeaf);
|
||||
|
||||
@override
|
||||
void run() {
|
||||
|
@ -487,10 +520,13 @@ class Int32x10 extends FfiBenchmarkBase {
|
|||
class Int32x20 extends FfiBenchmarkBase {
|
||||
final Function20int f;
|
||||
|
||||
Int32x20()
|
||||
: f = ffiTestFunctions.lookupFunction<NativeFunction20Int32,
|
||||
Function20int>('Function20Int32'),
|
||||
super('FfiCall.Int32x20');
|
||||
Int32x20({isLeaf: false})
|
||||
: f = isLeaf
|
||||
? ffiTestFunctions.lookupFunction<NativeFunction20Int32,
|
||||
Function20int>('Function20Int32', isLeaf: true)
|
||||
: ffiTestFunctions.lookupFunction<NativeFunction20Int32,
|
||||
Function20int>('Function20Int32', isLeaf: false),
|
||||
super('FfiCall.Int32x20', isLeaf: isLeaf);
|
||||
|
||||
@override
|
||||
void run() {
|
||||
|
@ -505,10 +541,13 @@ class Int32x20 extends FfiBenchmarkBase {
|
|||
class Int64x01 extends FfiBenchmarkBase {
|
||||
final Function1int f;
|
||||
|
||||
Int64x01()
|
||||
: f = ffiTestFunctions.lookupFunction<NativeFunction1Int64, Function1int>(
|
||||
'Function1Int64'),
|
||||
super('FfiCall.Int64x01');
|
||||
Int64x01({isLeaf: false})
|
||||
: f = isLeaf
|
||||
? ffiTestFunctions.lookupFunction<NativeFunction1Int64,
|
||||
Function1int>('Function1Int64', isLeaf: true)
|
||||
: ffiTestFunctions.lookupFunction<NativeFunction1Int64,
|
||||
Function1int>('Function1Int64', isLeaf: false),
|
||||
super('FfiCall.Int64x01', isLeaf: isLeaf);
|
||||
|
||||
@override
|
||||
void run() {
|
||||
|
@ -523,10 +562,13 @@ class Int64x01 extends FfiBenchmarkBase {
|
|||
class Int64x02 extends FfiBenchmarkBase {
|
||||
final Function2int f;
|
||||
|
||||
Int64x02()
|
||||
: f = ffiTestFunctions.lookupFunction<NativeFunction2Int64, Function2int>(
|
||||
'Function2Int64'),
|
||||
super('FfiCall.Int64x02');
|
||||
Int64x02({isLeaf: false})
|
||||
: f = isLeaf
|
||||
? ffiTestFunctions.lookupFunction<NativeFunction2Int64,
|
||||
Function2int>('Function2Int64', isLeaf: true)
|
||||
: ffiTestFunctions.lookupFunction<NativeFunction2Int64,
|
||||
Function2int>('Function2Int64', isLeaf: false),
|
||||
super('FfiCall.Int64x02', isLeaf: isLeaf);
|
||||
|
||||
@override
|
||||
void run() {
|
||||
|
@ -541,10 +583,13 @@ class Int64x02 extends FfiBenchmarkBase {
|
|||
class Int64x04 extends FfiBenchmarkBase {
|
||||
final Function4int f;
|
||||
|
||||
Int64x04()
|
||||
: f = ffiTestFunctions.lookupFunction<NativeFunction4Int64, Function4int>(
|
||||
'Function4Int64'),
|
||||
super('FfiCall.Int64x04');
|
||||
Int64x04({isLeaf: false})
|
||||
: f = isLeaf
|
||||
? ffiTestFunctions.lookupFunction<NativeFunction4Int64,
|
||||
Function4int>('Function4Int64', isLeaf: true)
|
||||
: ffiTestFunctions.lookupFunction<NativeFunction4Int64,
|
||||
Function4int>('Function4Int64', isLeaf: false),
|
||||
super('FfiCall.Int64x04', isLeaf: isLeaf);
|
||||
|
||||
@override
|
||||
void run() {
|
||||
|
@ -559,10 +604,13 @@ class Int64x04 extends FfiBenchmarkBase {
|
|||
class Int64x10 extends FfiBenchmarkBase {
|
||||
final Function10int f;
|
||||
|
||||
Int64x10()
|
||||
: f = ffiTestFunctions.lookupFunction<NativeFunction10Int64,
|
||||
Function10int>('Function10Int64'),
|
||||
super('FfiCall.Int64x10');
|
||||
Int64x10({isLeaf: false})
|
||||
: f = isLeaf
|
||||
? ffiTestFunctions.lookupFunction<NativeFunction10Int64,
|
||||
Function10int>('Function10Int64', isLeaf: true)
|
||||
: ffiTestFunctions.lookupFunction<NativeFunction10Int64,
|
||||
Function10int>('Function10Int64', isLeaf: false),
|
||||
super('FfiCall.Int64x10', isLeaf: isLeaf);
|
||||
|
||||
@override
|
||||
void run() {
|
||||
|
@ -577,10 +625,13 @@ class Int64x10 extends FfiBenchmarkBase {
|
|||
class Int64x20 extends FfiBenchmarkBase {
|
||||
final Function20int f;
|
||||
|
||||
Int64x20()
|
||||
: f = ffiTestFunctions.lookupFunction<NativeFunction20Int64,
|
||||
Function20int>('Function20Int64'),
|
||||
super('FfiCall.Int64x20');
|
||||
Int64x20({isLeaf: false})
|
||||
: f = isLeaf
|
||||
? ffiTestFunctions.lookupFunction<NativeFunction20Int64,
|
||||
Function20int>('Function20Int64', isLeaf: true)
|
||||
: ffiTestFunctions.lookupFunction<NativeFunction20Int64,
|
||||
Function20int>('Function20Int64', isLeaf: false),
|
||||
super('FfiCall.Int64x20', isLeaf: isLeaf);
|
||||
|
||||
@override
|
||||
void run() {
|
||||
|
@ -595,10 +646,13 @@ class Int64x20 extends FfiBenchmarkBase {
|
|||
class Int64Mintx01 extends FfiBenchmarkBase {
|
||||
final Function1int f;
|
||||
|
||||
Int64Mintx01()
|
||||
: f = ffiTestFunctions.lookupFunction<NativeFunction1Int64, Function1int>(
|
||||
'Function1Int64'),
|
||||
super('FfiCall.Int64Mintx01');
|
||||
Int64Mintx01({isLeaf: false})
|
||||
: f = isLeaf
|
||||
? ffiTestFunctions.lookupFunction<NativeFunction1Int64,
|
||||
Function1int>('Function1Int64', isLeaf: true)
|
||||
: ffiTestFunctions.lookupFunction<NativeFunction1Int64,
|
||||
Function1int>('Function1Int64', isLeaf: false),
|
||||
super('FfiCall.Int64Mintx01', isLeaf: isLeaf);
|
||||
|
||||
@override
|
||||
void run() {
|
||||
|
@ -613,10 +667,13 @@ class Int64Mintx01 extends FfiBenchmarkBase {
|
|||
class Floatx01 extends FfiBenchmarkBase {
|
||||
final Function1double f;
|
||||
|
||||
Floatx01()
|
||||
: f = ffiTestFunctions.lookupFunction<NativeFunction1Float,
|
||||
Function1double>('Function1Float'),
|
||||
super('FfiCall.Floatx01');
|
||||
Floatx01({isLeaf: false})
|
||||
: f = isLeaf
|
||||
? ffiTestFunctions.lookupFunction<NativeFunction1Float,
|
||||
Function1double>('Function1Float', isLeaf: true)
|
||||
: ffiTestFunctions.lookupFunction<NativeFunction1Float,
|
||||
Function1double>('Function1Float', isLeaf: false),
|
||||
super('FfiCall.Floatx01', isLeaf: isLeaf);
|
||||
|
||||
@override
|
||||
void run() {
|
||||
|
@ -631,10 +688,13 @@ class Floatx01 extends FfiBenchmarkBase {
|
|||
class Floatx02 extends FfiBenchmarkBase {
|
||||
final Function2double f;
|
||||
|
||||
Floatx02()
|
||||
: f = ffiTestFunctions.lookupFunction<NativeFunction2Float,
|
||||
Function2double>('Function2Float'),
|
||||
super('FfiCall.Floatx02');
|
||||
Floatx02({isLeaf: false})
|
||||
: f = isLeaf
|
||||
? ffiTestFunctions.lookupFunction<NativeFunction2Float,
|
||||
Function2double>('Function2Float', isLeaf: true)
|
||||
: ffiTestFunctions.lookupFunction<NativeFunction2Float,
|
||||
Function2double>('Function2Float', isLeaf: false),
|
||||
super('FfiCall.Floatx02', isLeaf: isLeaf);
|
||||
|
||||
@override
|
||||
void run() {
|
||||
|
@ -649,10 +709,13 @@ class Floatx02 extends FfiBenchmarkBase {
|
|||
class Floatx04 extends FfiBenchmarkBase {
|
||||
final Function4double f;
|
||||
|
||||
Floatx04()
|
||||
: f = ffiTestFunctions.lookupFunction<NativeFunction4Float,
|
||||
Function4double>('Function4Float'),
|
||||
super('FfiCall.Floatx04');
|
||||
Floatx04({isLeaf: false})
|
||||
: f = isLeaf
|
||||
? ffiTestFunctions.lookupFunction<NativeFunction4Float,
|
||||
Function4double>('Function4Float', isLeaf: true)
|
||||
: ffiTestFunctions.lookupFunction<NativeFunction4Float,
|
||||
Function4double>('Function4Float', isLeaf: false),
|
||||
super('FfiCall.Floatx04', isLeaf: isLeaf);
|
||||
|
||||
@override
|
||||
void run() {
|
||||
|
@ -668,10 +731,13 @@ class Floatx04 extends FfiBenchmarkBase {
|
|||
class Floatx10 extends FfiBenchmarkBase {
|
||||
final Function10double f;
|
||||
|
||||
Floatx10()
|
||||
: f = ffiTestFunctions.lookupFunction<NativeFunction10Float,
|
||||
Function10double>('Function10Float'),
|
||||
super('FfiCall.Floatx10');
|
||||
Floatx10({isLeaf: false})
|
||||
: f = isLeaf
|
||||
? ffiTestFunctions.lookupFunction<NativeFunction10Float,
|
||||
Function10double>('Function10Float', isLeaf: true)
|
||||
: ffiTestFunctions.lookupFunction<NativeFunction10Float,
|
||||
Function10double>('Function10Float', isLeaf: false),
|
||||
super('FfiCall.Floatx10', isLeaf: isLeaf);
|
||||
|
||||
@override
|
||||
void run() {
|
||||
|
@ -688,10 +754,13 @@ class Floatx10 extends FfiBenchmarkBase {
|
|||
class Floatx20 extends FfiBenchmarkBase {
|
||||
final Function20double f;
|
||||
|
||||
Floatx20()
|
||||
: f = ffiTestFunctions.lookupFunction<NativeFunction20Float,
|
||||
Function20double>('Function20Float'),
|
||||
super('FfiCall.Floatx20');
|
||||
Floatx20({isLeaf: false})
|
||||
: f = isLeaf
|
||||
? ffiTestFunctions.lookupFunction<NativeFunction20Float,
|
||||
Function20double>('Function20Float', isLeaf: true)
|
||||
: ffiTestFunctions.lookupFunction<NativeFunction20Float,
|
||||
Function20double>('Function20Float', isLeaf: false),
|
||||
super('FfiCall.Floatx20', isLeaf: isLeaf);
|
||||
|
||||
@override
|
||||
void run() {
|
||||
|
@ -728,10 +797,13 @@ class Floatx20 extends FfiBenchmarkBase {
|
|||
class Doublex01 extends FfiBenchmarkBase {
|
||||
final Function1double f;
|
||||
|
||||
Doublex01()
|
||||
: f = ffiTestFunctions.lookupFunction<NativeFunction1Double,
|
||||
Function1double>('Function1Double'),
|
||||
super('FfiCall.Doublex01');
|
||||
Doublex01({isLeaf: false})
|
||||
: f = isLeaf
|
||||
? ffiTestFunctions.lookupFunction<NativeFunction1Double,
|
||||
Function1double>('Function1Double', isLeaf: true)
|
||||
: ffiTestFunctions.lookupFunction<NativeFunction1Double,
|
||||
Function1double>('Function1Double', isLeaf: false),
|
||||
super('FfiCall.Doublex01', isLeaf: isLeaf);
|
||||
|
||||
@override
|
||||
void run() {
|
||||
|
@ -747,10 +819,13 @@ class Doublex01 extends FfiBenchmarkBase {
|
|||
class Doublex02 extends FfiBenchmarkBase {
|
||||
final Function2double f;
|
||||
|
||||
Doublex02()
|
||||
: f = ffiTestFunctions.lookupFunction<NativeFunction2Double,
|
||||
Function2double>('Function2Double'),
|
||||
super('FfiCall.Doublex02');
|
||||
Doublex02({isLeaf: false})
|
||||
: f = isLeaf
|
||||
? ffiTestFunctions.lookupFunction<NativeFunction2Double,
|
||||
Function2double>('Function2Double', isLeaf: true)
|
||||
: ffiTestFunctions.lookupFunction<NativeFunction2Double,
|
||||
Function2double>('Function2Double', isLeaf: false),
|
||||
super('FfiCall.Doublex02', isLeaf: isLeaf);
|
||||
|
||||
@override
|
||||
void run() {
|
||||
|
@ -766,10 +841,13 @@ class Doublex02 extends FfiBenchmarkBase {
|
|||
class Doublex04 extends FfiBenchmarkBase {
|
||||
final Function4double f;
|
||||
|
||||
Doublex04()
|
||||
: f = ffiTestFunctions.lookupFunction<NativeFunction4Double,
|
||||
Function4double>('Function4Double'),
|
||||
super('FfiCall.Doublex04');
|
||||
Doublex04({isLeaf: false})
|
||||
: f = isLeaf
|
||||
? ffiTestFunctions.lookupFunction<NativeFunction4Double,
|
||||
Function4double>('Function4Double', isLeaf: true)
|
||||
: ffiTestFunctions.lookupFunction<NativeFunction4Double,
|
||||
Function4double>('Function4Double', isLeaf: false),
|
||||
super('FfiCall.Doublex04', isLeaf: isLeaf);
|
||||
|
||||
@override
|
||||
void run() {
|
||||
|
@ -785,10 +863,13 @@ class Doublex04 extends FfiBenchmarkBase {
|
|||
class Doublex10 extends FfiBenchmarkBase {
|
||||
final Function10double f;
|
||||
|
||||
Doublex10()
|
||||
: f = ffiTestFunctions.lookupFunction<NativeFunction10Double,
|
||||
Function10double>('Function10Double'),
|
||||
super('FfiCall.Doublex10');
|
||||
Doublex10({isLeaf: false})
|
||||
: f = isLeaf
|
||||
? ffiTestFunctions.lookupFunction<NativeFunction10Double,
|
||||
Function10double>('Function10Double', isLeaf: true)
|
||||
: ffiTestFunctions.lookupFunction<NativeFunction10Double,
|
||||
Function10double>('Function10Double', isLeaf: false),
|
||||
super('FfiCall.Doublex10', isLeaf: isLeaf);
|
||||
|
||||
@override
|
||||
void run() {
|
||||
|
@ -805,10 +886,13 @@ class Doublex10 extends FfiBenchmarkBase {
|
|||
class Doublex20 extends FfiBenchmarkBase {
|
||||
final Function20double f;
|
||||
|
||||
Doublex20()
|
||||
: f = ffiTestFunctions.lookupFunction<NativeFunction20Double,
|
||||
Function20double>('Function20Double'),
|
||||
super('FfiCall.Doublex20');
|
||||
Doublex20({isLeaf: false})
|
||||
: f = isLeaf
|
||||
? ffiTestFunctions.lookupFunction<NativeFunction20Double,
|
||||
Function20double>('Function20Double', isLeaf: true)
|
||||
: ffiTestFunctions.lookupFunction<NativeFunction20Double,
|
||||
Function20double>('Function20Double', isLeaf: false),
|
||||
super('FfiCall.Doublex20', isLeaf: isLeaf);
|
||||
|
||||
@override
|
||||
void run() {
|
||||
|
@ -845,10 +929,13 @@ class Doublex20 extends FfiBenchmarkBase {
|
|||
class PointerUint8x01 extends FfiBenchmarkBase {
|
||||
final Function1PointerUint8 f;
|
||||
|
||||
PointerUint8x01()
|
||||
: f = ffiTestFunctions.lookupFunction<Function1PointerUint8,
|
||||
Function1PointerUint8>('Function1PointerUint8'),
|
||||
super('FfiCall.PointerUint8x01');
|
||||
PointerUint8x01({isLeaf: false})
|
||||
: f = isLeaf
|
||||
? ffiTestFunctions.lookupFunction<Function1PointerUint8,
|
||||
Function1PointerUint8>('Function1PointerUint8', isLeaf: true)
|
||||
: ffiTestFunctions.lookupFunction<Function1PointerUint8,
|
||||
Function1PointerUint8>('Function1PointerUint8', isLeaf: false),
|
||||
super('FfiCall.PointerUint8x01', isLeaf: isLeaf);
|
||||
|
||||
Pointer<Uint8> p1 = nullptr;
|
||||
@override
|
||||
|
@ -869,10 +956,13 @@ class PointerUint8x01 extends FfiBenchmarkBase {
|
|||
class PointerUint8x02 extends FfiBenchmarkBase {
|
||||
final Function2PointerUint8 f;
|
||||
|
||||
PointerUint8x02()
|
||||
: f = ffiTestFunctions.lookupFunction<Function2PointerUint8,
|
||||
Function2PointerUint8>('Function2PointerUint8'),
|
||||
super('FfiCall.PointerUint8x02');
|
||||
PointerUint8x02({isLeaf: false})
|
||||
: f = isLeaf
|
||||
? ffiTestFunctions.lookupFunction<Function2PointerUint8,
|
||||
Function2PointerUint8>('Function2PointerUint8', isLeaf: true)
|
||||
: ffiTestFunctions.lookupFunction<Function2PointerUint8,
|
||||
Function2PointerUint8>('Function2PointerUint8', isLeaf: false),
|
||||
super('FfiCall.PointerUint8x02', isLeaf: isLeaf);
|
||||
|
||||
Pointer<Uint8> p1 = nullptr;
|
||||
Pointer<Uint8> p2 = nullptr;
|
||||
|
@ -901,10 +991,13 @@ class PointerUint8x02 extends FfiBenchmarkBase {
|
|||
class PointerUint8x04 extends FfiBenchmarkBase {
|
||||
final Function4PointerUint8 f;
|
||||
|
||||
PointerUint8x04()
|
||||
: f = ffiTestFunctions.lookupFunction<Function4PointerUint8,
|
||||
Function4PointerUint8>('Function4PointerUint8'),
|
||||
super('FfiCall.PointerUint8x04');
|
||||
PointerUint8x04({isLeaf: false})
|
||||
: f = isLeaf
|
||||
? ffiTestFunctions.lookupFunction<Function4PointerUint8,
|
||||
Function4PointerUint8>('Function4PointerUint8', isLeaf: true)
|
||||
: ffiTestFunctions.lookupFunction<Function4PointerUint8,
|
||||
Function4PointerUint8>('Function4PointerUint8', isLeaf: false),
|
||||
super('FfiCall.PointerUint8x04', isLeaf: isLeaf);
|
||||
|
||||
Pointer<Uint8> p1 = nullptr;
|
||||
Pointer<Uint8> p2 = nullptr;
|
||||
|
@ -937,10 +1030,15 @@ class PointerUint8x04 extends FfiBenchmarkBase {
|
|||
class PointerUint8x10 extends FfiBenchmarkBase {
|
||||
final Function10PointerUint8 f;
|
||||
|
||||
PointerUint8x10()
|
||||
: f = ffiTestFunctions.lookupFunction<Function10PointerUint8,
|
||||
Function10PointerUint8>('Function10PointerUint8'),
|
||||
super('FfiCall.PointerUint8x10');
|
||||
PointerUint8x10({isLeaf: false})
|
||||
: f = isLeaf
|
||||
? ffiTestFunctions.lookupFunction<Function10PointerUint8,
|
||||
Function10PointerUint8>('Function10PointerUint8', isLeaf: true)
|
||||
: ffiTestFunctions
|
||||
.lookupFunction<Function10PointerUint8, Function10PointerUint8>(
|
||||
'Function10PointerUint8',
|
||||
isLeaf: false),
|
||||
super('FfiCall.PointerUint8x10', isLeaf: isLeaf);
|
||||
|
||||
Pointer<Uint8> p1 = nullptr;
|
||||
Pointer<Uint8> p2 = nullptr;
|
||||
|
@ -985,10 +1083,15 @@ class PointerUint8x10 extends FfiBenchmarkBase {
|
|||
class PointerUint8x20 extends FfiBenchmarkBase {
|
||||
final Function20PointerUint8 f;
|
||||
|
||||
PointerUint8x20()
|
||||
: f = ffiTestFunctions.lookupFunction<Function20PointerUint8,
|
||||
Function20PointerUint8>('Function20PointerUint8'),
|
||||
super('FfiCall.PointerUint8x20');
|
||||
PointerUint8x20({isLeaf: false})
|
||||
: f = isLeaf
|
||||
? ffiTestFunctions.lookupFunction<Function20PointerUint8,
|
||||
Function20PointerUint8>('Function20PointerUint8', isLeaf: true)
|
||||
: ffiTestFunctions
|
||||
.lookupFunction<Function20PointerUint8, Function20PointerUint8>(
|
||||
'Function20PointerUint8',
|
||||
isLeaf: false),
|
||||
super('FfiCall.PointerUint8x20', isLeaf: isLeaf);
|
||||
|
||||
Pointer<Uint8> p1 = nullptr;
|
||||
Pointer<Uint8> p2 = nullptr;
|
||||
|
@ -1061,8 +1164,8 @@ class Handlex01 extends FfiBenchmarkBase {
|
|||
|
||||
Handlex01()
|
||||
: f = ffiTestFunctions.lookupFunction<NativeFunction1Handle,
|
||||
Function1Object>('Function1Handle'),
|
||||
super('FfiCall.Handlex01');
|
||||
Function1Object>('Function1Handle', isLeaf: false),
|
||||
super('FfiCall.Handlex01', isLeaf: false);
|
||||
|
||||
@override
|
||||
void run() {
|
||||
|
@ -1080,8 +1183,8 @@ class Handlex02 extends FfiBenchmarkBase {
|
|||
|
||||
Handlex02()
|
||||
: f = ffiTestFunctions.lookupFunction<NativeFunction2Handle,
|
||||
Function2Object>('Function2Handle'),
|
||||
super('FfiCall.Handlex02');
|
||||
Function2Object>('Function2Handle', isLeaf: false),
|
||||
super('FfiCall.Handlex02', isLeaf: false);
|
||||
|
||||
@override
|
||||
void run() {
|
||||
|
@ -1100,8 +1203,8 @@ class Handlex04 extends FfiBenchmarkBase {
|
|||
|
||||
Handlex04()
|
||||
: f = ffiTestFunctions.lookupFunction<NativeFunction4Handle,
|
||||
Function4Object>('Function4Handle'),
|
||||
super('FfiCall.Handlex04');
|
||||
Function4Object>('Function4Handle', isLeaf: false),
|
||||
super('FfiCall.Handlex04', isLeaf: false);
|
||||
|
||||
@override
|
||||
void run() {
|
||||
|
@ -1122,8 +1225,8 @@ class Handlex10 extends FfiBenchmarkBase {
|
|||
|
||||
Handlex10()
|
||||
: f = ffiTestFunctions.lookupFunction<NativeFunction10Handle,
|
||||
Function10Object>('Function10Handle'),
|
||||
super('FfiCall.Handlex10');
|
||||
Function10Object>('Function10Handle', isLeaf: false),
|
||||
super('FfiCall.Handlex10', isLeaf: false);
|
||||
|
||||
@override
|
||||
void run() {
|
||||
|
@ -1150,8 +1253,8 @@ class Handlex20 extends FfiBenchmarkBase {
|
|||
|
||||
Handlex20()
|
||||
: f = ffiTestFunctions.lookupFunction<NativeFunction20Handle,
|
||||
Function20Object>('Function20Handle'),
|
||||
super('FfiCall.Handlex20');
|
||||
Function20Object>('Function20Handle', isLeaf: false),
|
||||
super('FfiCall.Handlex20', isLeaf: false);
|
||||
|
||||
@override
|
||||
void run() {
|
||||
|
@ -1191,10 +1294,12 @@ class Handlex20 extends FfiBenchmarkBase {
|
|||
void main() {
|
||||
final benchmarks = [
|
||||
() => Uint8x01(),
|
||||
() => Uint8x01(isLeaf: true),
|
||||
() => Uint16x01(),
|
||||
() => Uint32x01(),
|
||||
() => Uint64x01(),
|
||||
() => Int8x01(),
|
||||
() => Int8x01(isLeaf: true),
|
||||
() => Int16x01(),
|
||||
() => Int32x01(),
|
||||
() => Int32x02(),
|
||||
|
@ -1206,22 +1311,27 @@ void main() {
|
|||
() => Int64x04(),
|
||||
() => Int64x10(),
|
||||
() => Int64x20(),
|
||||
() => Int64x20(isLeaf: true),
|
||||
() => Int64Mintx01(),
|
||||
() => Int64Mintx01(isLeaf: true),
|
||||
() => Floatx01(),
|
||||
() => Floatx02(),
|
||||
() => Floatx04(),
|
||||
() => Floatx10(),
|
||||
() => Floatx20(),
|
||||
() => Floatx20(isLeaf: true),
|
||||
() => Doublex01(),
|
||||
() => Doublex02(),
|
||||
() => Doublex04(),
|
||||
() => Doublex10(),
|
||||
() => Doublex20(),
|
||||
() => Doublex20(isLeaf: true),
|
||||
() => PointerUint8x01(),
|
||||
() => PointerUint8x02(),
|
||||
() => PointerUint8x04(),
|
||||
() => PointerUint8x10(),
|
||||
() => PointerUint8x20(),
|
||||
() => PointerUint8x20(isLeaf: true),
|
||||
() => Handlex01(),
|
||||
() => Handlex02(),
|
||||
() => Handlex04(),
|
||||
|
|
|
@ -285,7 +285,10 @@ typedef Function20Object = Object Function(
|
|||
//
|
||||
|
||||
abstract class FfiBenchmarkBase extends BenchmarkBase {
|
||||
FfiBenchmarkBase(String name) : super(name);
|
||||
final bool isLeaf;
|
||||
|
||||
FfiBenchmarkBase(String name, {this.isLeaf: false})
|
||||
: super('$name${isLeaf ? 'Leaf' : ''}');
|
||||
|
||||
void expectEquals(actual, expected) {
|
||||
if (actual != expected) {
|
||||
|
@ -309,10 +312,13 @@ abstract class FfiBenchmarkBase extends BenchmarkBase {
|
|||
class Uint8x01 extends FfiBenchmarkBase {
|
||||
final Function1int f;
|
||||
|
||||
Uint8x01()
|
||||
: f = ffiTestFunctions.lookupFunction<NativeFunction1Uint8, Function1int>(
|
||||
'Function1Uint8'),
|
||||
super('FfiCall.Uint8x01');
|
||||
Uint8x01({isLeaf: false})
|
||||
: f = isLeaf
|
||||
? ffiTestFunctions.lookupFunction<NativeFunction1Uint8,
|
||||
Function1int>('Function1Uint8', isLeaf: true)
|
||||
: ffiTestFunctions.lookupFunction<NativeFunction1Uint8,
|
||||
Function1int>('Function1Uint8', isLeaf: false),
|
||||
super('FfiCall.Uint8x01', isLeaf: isLeaf);
|
||||
|
||||
@override
|
||||
void run() {
|
||||
|
@ -327,10 +333,13 @@ class Uint8x01 extends FfiBenchmarkBase {
|
|||
class Uint16x01 extends FfiBenchmarkBase {
|
||||
final Function1int f;
|
||||
|
||||
Uint16x01()
|
||||
: f = ffiTestFunctions.lookupFunction<NativeFunction1Uint16,
|
||||
Function1int>('Function1Uint16'),
|
||||
super('FfiCall.Uint16x01');
|
||||
Uint16x01({isLeaf: false})
|
||||
: f = isLeaf
|
||||
? ffiTestFunctions.lookupFunction<NativeFunction1Uint16,
|
||||
Function1int>('Function1Uint16', isLeaf: true)
|
||||
: ffiTestFunctions.lookupFunction<NativeFunction1Uint16,
|
||||
Function1int>('Function1Uint16', isLeaf: false),
|
||||
super('FfiCall.Uint16x01', isLeaf: isLeaf);
|
||||
|
||||
@override
|
||||
void run() {
|
||||
|
@ -345,10 +354,13 @@ class Uint16x01 extends FfiBenchmarkBase {
|
|||
class Uint32x01 extends FfiBenchmarkBase {
|
||||
final Function1int f;
|
||||
|
||||
Uint32x01()
|
||||
: f = ffiTestFunctions.lookupFunction<NativeFunction1Uint32,
|
||||
Function1int>('Function1Uint32'),
|
||||
super('FfiCall.Uint32x01');
|
||||
Uint32x01({isLeaf: false})
|
||||
: f = isLeaf
|
||||
? ffiTestFunctions.lookupFunction<NativeFunction1Uint32,
|
||||
Function1int>('Function1Uint32', isLeaf: true)
|
||||
: ffiTestFunctions.lookupFunction<NativeFunction1Uint32,
|
||||
Function1int>('Function1Uint32', isLeaf: false),
|
||||
super('FfiCall.Uint32x01', isLeaf: isLeaf);
|
||||
|
||||
@override
|
||||
void run() {
|
||||
|
@ -363,10 +375,13 @@ class Uint32x01 extends FfiBenchmarkBase {
|
|||
class Uint64x01 extends FfiBenchmarkBase {
|
||||
final Function1int f;
|
||||
|
||||
Uint64x01()
|
||||
: f = ffiTestFunctions.lookupFunction<NativeFunction1Uint64,
|
||||
Function1int>('Function1Uint64'),
|
||||
super('FfiCall.Uint64x01');
|
||||
Uint64x01({isLeaf: false})
|
||||
: f = isLeaf
|
||||
? ffiTestFunctions.lookupFunction<NativeFunction1Uint64,
|
||||
Function1int>('Function1Uint64', isLeaf: true)
|
||||
: ffiTestFunctions.lookupFunction<NativeFunction1Uint64,
|
||||
Function1int>('Function1Uint64', isLeaf: false),
|
||||
super('FfiCall.Uint64x01', isLeaf: isLeaf);
|
||||
|
||||
@override
|
||||
void run() {
|
||||
|
@ -381,10 +396,13 @@ class Uint64x01 extends FfiBenchmarkBase {
|
|||
class Int8x01 extends FfiBenchmarkBase {
|
||||
final Function1int f;
|
||||
|
||||
Int8x01()
|
||||
: f = ffiTestFunctions
|
||||
.lookupFunction<NativeFunction1Int8, Function1int>('Function1Int8'),
|
||||
super('FfiCall.Int8x01');
|
||||
Int8x01({isLeaf: false})
|
||||
: f = isLeaf
|
||||
? ffiTestFunctions.lookupFunction<NativeFunction1Int8,
|
||||
Function1int>('Function1Int8', isLeaf: true)
|
||||
: ffiTestFunctions.lookupFunction<NativeFunction1Int8,
|
||||
Function1int>('Function1Int8', isLeaf: false),
|
||||
super('FfiCall.Int8x01', isLeaf: isLeaf);
|
||||
|
||||
@override
|
||||
void run() {
|
||||
|
@ -399,10 +417,13 @@ class Int8x01 extends FfiBenchmarkBase {
|
|||
class Int16x01 extends FfiBenchmarkBase {
|
||||
final Function1int f;
|
||||
|
||||
Int16x01()
|
||||
: f = ffiTestFunctions.lookupFunction<NativeFunction1Int16, Function1int>(
|
||||
'Function1Int16'),
|
||||
super('FfiCall.Int16x01');
|
||||
Int16x01({isLeaf: false})
|
||||
: f = isLeaf
|
||||
? ffiTestFunctions.lookupFunction<NativeFunction1Int16,
|
||||
Function1int>('Function1Int16', isLeaf: true)
|
||||
: ffiTestFunctions.lookupFunction<NativeFunction1Int16,
|
||||
Function1int>('Function1Int16', isLeaf: false),
|
||||
super('FfiCall.Int16x01', isLeaf: isLeaf);
|
||||
|
||||
@override
|
||||
void run() {
|
||||
|
@ -417,10 +438,13 @@ class Int16x01 extends FfiBenchmarkBase {
|
|||
class Int32x01 extends FfiBenchmarkBase {
|
||||
final Function1int f;
|
||||
|
||||
Int32x01()
|
||||
: f = ffiTestFunctions.lookupFunction<NativeFunction1Int32, Function1int>(
|
||||
'Function1Int32'),
|
||||
super('FfiCall.Int32x01');
|
||||
Int32x01({isLeaf: false})
|
||||
: f = isLeaf
|
||||
? ffiTestFunctions.lookupFunction<NativeFunction1Int32,
|
||||
Function1int>('Function1Int32', isLeaf: true)
|
||||
: ffiTestFunctions.lookupFunction<NativeFunction1Int32,
|
||||
Function1int>('Function1Int32', isLeaf: false),
|
||||
super('FfiCall.Int32x01', isLeaf: isLeaf);
|
||||
|
||||
@override
|
||||
void run() {
|
||||
|
@ -435,10 +459,13 @@ class Int32x01 extends FfiBenchmarkBase {
|
|||
class Int32x02 extends FfiBenchmarkBase {
|
||||
final Function2int f;
|
||||
|
||||
Int32x02()
|
||||
: f = ffiTestFunctions.lookupFunction<NativeFunction2Int32, Function2int>(
|
||||
'Function2Int32'),
|
||||
super('FfiCall.Int32x02');
|
||||
Int32x02({isLeaf: false})
|
||||
: f = isLeaf
|
||||
? ffiTestFunctions.lookupFunction<NativeFunction2Int32,
|
||||
Function2int>('Function2Int32', isLeaf: true)
|
||||
: ffiTestFunctions.lookupFunction<NativeFunction2Int32,
|
||||
Function2int>('Function2Int32', isLeaf: false),
|
||||
super('FfiCall.Int32x02', isLeaf: isLeaf);
|
||||
|
||||
@override
|
||||
void run() {
|
||||
|
@ -453,10 +480,13 @@ class Int32x02 extends FfiBenchmarkBase {
|
|||
class Int32x04 extends FfiBenchmarkBase {
|
||||
final Function4int f;
|
||||
|
||||
Int32x04()
|
||||
: f = ffiTestFunctions.lookupFunction<NativeFunction4Int32, Function4int>(
|
||||
'Function4Int32'),
|
||||
super('FfiCall.Int32x04');
|
||||
Int32x04({isLeaf: false})
|
||||
: f = isLeaf
|
||||
? ffiTestFunctions.lookupFunction<NativeFunction4Int32,
|
||||
Function4int>('Function4Int32', isLeaf: true)
|
||||
: ffiTestFunctions.lookupFunction<NativeFunction4Int32,
|
||||
Function4int>('Function4Int32', isLeaf: false),
|
||||
super('FfiCall.Int32x04', isLeaf: isLeaf);
|
||||
|
||||
@override
|
||||
void run() {
|
||||
|
@ -471,10 +501,13 @@ class Int32x04 extends FfiBenchmarkBase {
|
|||
class Int32x10 extends FfiBenchmarkBase {
|
||||
final Function10int f;
|
||||
|
||||
Int32x10()
|
||||
: f = ffiTestFunctions.lookupFunction<NativeFunction10Int32,
|
||||
Function10int>('Function10Int32'),
|
||||
super('FfiCall.Int32x10');
|
||||
Int32x10({isLeaf: false})
|
||||
: f = isLeaf
|
||||
? ffiTestFunctions.lookupFunction<NativeFunction10Int32,
|
||||
Function10int>('Function10Int32', isLeaf: true)
|
||||
: ffiTestFunctions.lookupFunction<NativeFunction10Int32,
|
||||
Function10int>('Function10Int32', isLeaf: false),
|
||||
super('FfiCall.Int32x10', isLeaf: isLeaf);
|
||||
|
||||
@override
|
||||
void run() {
|
||||
|
@ -489,10 +522,13 @@ class Int32x10 extends FfiBenchmarkBase {
|
|||
class Int32x20 extends FfiBenchmarkBase {
|
||||
final Function20int f;
|
||||
|
||||
Int32x20()
|
||||
: f = ffiTestFunctions.lookupFunction<NativeFunction20Int32,
|
||||
Function20int>('Function20Int32'),
|
||||
super('FfiCall.Int32x20');
|
||||
Int32x20({isLeaf: false})
|
||||
: f = isLeaf
|
||||
? ffiTestFunctions.lookupFunction<NativeFunction20Int32,
|
||||
Function20int>('Function20Int32', isLeaf: true)
|
||||
: ffiTestFunctions.lookupFunction<NativeFunction20Int32,
|
||||
Function20int>('Function20Int32', isLeaf: false),
|
||||
super('FfiCall.Int32x20', isLeaf: isLeaf);
|
||||
|
||||
@override
|
||||
void run() {
|
||||
|
@ -507,10 +543,13 @@ class Int32x20 extends FfiBenchmarkBase {
|
|||
class Int64x01 extends FfiBenchmarkBase {
|
||||
final Function1int f;
|
||||
|
||||
Int64x01()
|
||||
: f = ffiTestFunctions.lookupFunction<NativeFunction1Int64, Function1int>(
|
||||
'Function1Int64'),
|
||||
super('FfiCall.Int64x01');
|
||||
Int64x01({isLeaf: false})
|
||||
: f = isLeaf
|
||||
? ffiTestFunctions.lookupFunction<NativeFunction1Int64,
|
||||
Function1int>('Function1Int64', isLeaf: true)
|
||||
: ffiTestFunctions.lookupFunction<NativeFunction1Int64,
|
||||
Function1int>('Function1Int64', isLeaf: false),
|
||||
super('FfiCall.Int64x01', isLeaf: isLeaf);
|
||||
|
||||
@override
|
||||
void run() {
|
||||
|
@ -525,10 +564,13 @@ class Int64x01 extends FfiBenchmarkBase {
|
|||
class Int64x02 extends FfiBenchmarkBase {
|
||||
final Function2int f;
|
||||
|
||||
Int64x02()
|
||||
: f = ffiTestFunctions.lookupFunction<NativeFunction2Int64, Function2int>(
|
||||
'Function2Int64'),
|
||||
super('FfiCall.Int64x02');
|
||||
Int64x02({isLeaf: false})
|
||||
: f = isLeaf
|
||||
? ffiTestFunctions.lookupFunction<NativeFunction2Int64,
|
||||
Function2int>('Function2Int64', isLeaf: true)
|
||||
: ffiTestFunctions.lookupFunction<NativeFunction2Int64,
|
||||
Function2int>('Function2Int64', isLeaf: false),
|
||||
super('FfiCall.Int64x02', isLeaf: isLeaf);
|
||||
|
||||
@override
|
||||
void run() {
|
||||
|
@ -543,10 +585,13 @@ class Int64x02 extends FfiBenchmarkBase {
|
|||
class Int64x04 extends FfiBenchmarkBase {
|
||||
final Function4int f;
|
||||
|
||||
Int64x04()
|
||||
: f = ffiTestFunctions.lookupFunction<NativeFunction4Int64, Function4int>(
|
||||
'Function4Int64'),
|
||||
super('FfiCall.Int64x04');
|
||||
Int64x04({isLeaf: false})
|
||||
: f = isLeaf
|
||||
? ffiTestFunctions.lookupFunction<NativeFunction4Int64,
|
||||
Function4int>('Function4Int64', isLeaf: true)
|
||||
: ffiTestFunctions.lookupFunction<NativeFunction4Int64,
|
||||
Function4int>('Function4Int64', isLeaf: false),
|
||||
super('FfiCall.Int64x04', isLeaf: isLeaf);
|
||||
|
||||
@override
|
||||
void run() {
|
||||
|
@ -561,10 +606,13 @@ class Int64x04 extends FfiBenchmarkBase {
|
|||
class Int64x10 extends FfiBenchmarkBase {
|
||||
final Function10int f;
|
||||
|
||||
Int64x10()
|
||||
: f = ffiTestFunctions.lookupFunction<NativeFunction10Int64,
|
||||
Function10int>('Function10Int64'),
|
||||
super('FfiCall.Int64x10');
|
||||
Int64x10({isLeaf: false})
|
||||
: f = isLeaf
|
||||
? ffiTestFunctions.lookupFunction<NativeFunction10Int64,
|
||||
Function10int>('Function10Int64', isLeaf: true)
|
||||
: ffiTestFunctions.lookupFunction<NativeFunction10Int64,
|
||||
Function10int>('Function10Int64', isLeaf: false),
|
||||
super('FfiCall.Int64x10', isLeaf: isLeaf);
|
||||
|
||||
@override
|
||||
void run() {
|
||||
|
@ -579,10 +627,13 @@ class Int64x10 extends FfiBenchmarkBase {
|
|||
class Int64x20 extends FfiBenchmarkBase {
|
||||
final Function20int f;
|
||||
|
||||
Int64x20()
|
||||
: f = ffiTestFunctions.lookupFunction<NativeFunction20Int64,
|
||||
Function20int>('Function20Int64'),
|
||||
super('FfiCall.Int64x20');
|
||||
Int64x20({isLeaf: false})
|
||||
: f = isLeaf
|
||||
? ffiTestFunctions.lookupFunction<NativeFunction20Int64,
|
||||
Function20int>('Function20Int64', isLeaf: true)
|
||||
: ffiTestFunctions.lookupFunction<NativeFunction20Int64,
|
||||
Function20int>('Function20Int64', isLeaf: false),
|
||||
super('FfiCall.Int64x20', isLeaf: isLeaf);
|
||||
|
||||
@override
|
||||
void run() {
|
||||
|
@ -597,10 +648,13 @@ class Int64x20 extends FfiBenchmarkBase {
|
|||
class Int64Mintx01 extends FfiBenchmarkBase {
|
||||
final Function1int f;
|
||||
|
||||
Int64Mintx01()
|
||||
: f = ffiTestFunctions.lookupFunction<NativeFunction1Int64, Function1int>(
|
||||
'Function1Int64'),
|
||||
super('FfiCall.Int64Mintx01');
|
||||
Int64Mintx01({isLeaf: false})
|
||||
: f = isLeaf
|
||||
? ffiTestFunctions.lookupFunction<NativeFunction1Int64,
|
||||
Function1int>('Function1Int64', isLeaf: true)
|
||||
: ffiTestFunctions.lookupFunction<NativeFunction1Int64,
|
||||
Function1int>('Function1Int64', isLeaf: false),
|
||||
super('FfiCall.Int64Mintx01', isLeaf: isLeaf);
|
||||
|
||||
@override
|
||||
void run() {
|
||||
|
@ -615,10 +669,13 @@ class Int64Mintx01 extends FfiBenchmarkBase {
|
|||
class Floatx01 extends FfiBenchmarkBase {
|
||||
final Function1double f;
|
||||
|
||||
Floatx01()
|
||||
: f = ffiTestFunctions.lookupFunction<NativeFunction1Float,
|
||||
Function1double>('Function1Float'),
|
||||
super('FfiCall.Floatx01');
|
||||
Floatx01({isLeaf: false})
|
||||
: f = isLeaf
|
||||
? ffiTestFunctions.lookupFunction<NativeFunction1Float,
|
||||
Function1double>('Function1Float', isLeaf: true)
|
||||
: ffiTestFunctions.lookupFunction<NativeFunction1Float,
|
||||
Function1double>('Function1Float', isLeaf: false),
|
||||
super('FfiCall.Floatx01', isLeaf: isLeaf);
|
||||
|
||||
@override
|
||||
void run() {
|
||||
|
@ -633,10 +690,13 @@ class Floatx01 extends FfiBenchmarkBase {
|
|||
class Floatx02 extends FfiBenchmarkBase {
|
||||
final Function2double f;
|
||||
|
||||
Floatx02()
|
||||
: f = ffiTestFunctions.lookupFunction<NativeFunction2Float,
|
||||
Function2double>('Function2Float'),
|
||||
super('FfiCall.Floatx02');
|
||||
Floatx02({isLeaf: false})
|
||||
: f = isLeaf
|
||||
? ffiTestFunctions.lookupFunction<NativeFunction2Float,
|
||||
Function2double>('Function2Float', isLeaf: true)
|
||||
: ffiTestFunctions.lookupFunction<NativeFunction2Float,
|
||||
Function2double>('Function2Float', isLeaf: false),
|
||||
super('FfiCall.Floatx02', isLeaf: isLeaf);
|
||||
|
||||
@override
|
||||
void run() {
|
||||
|
@ -651,10 +711,13 @@ class Floatx02 extends FfiBenchmarkBase {
|
|||
class Floatx04 extends FfiBenchmarkBase {
|
||||
final Function4double f;
|
||||
|
||||
Floatx04()
|
||||
: f = ffiTestFunctions.lookupFunction<NativeFunction4Float,
|
||||
Function4double>('Function4Float'),
|
||||
super('FfiCall.Floatx04');
|
||||
Floatx04({isLeaf: false})
|
||||
: f = isLeaf
|
||||
? ffiTestFunctions.lookupFunction<NativeFunction4Float,
|
||||
Function4double>('Function4Float', isLeaf: true)
|
||||
: ffiTestFunctions.lookupFunction<NativeFunction4Float,
|
||||
Function4double>('Function4Float', isLeaf: false),
|
||||
super('FfiCall.Floatx04', isLeaf: isLeaf);
|
||||
|
||||
@override
|
||||
void run() {
|
||||
|
@ -670,10 +733,13 @@ class Floatx04 extends FfiBenchmarkBase {
|
|||
class Floatx10 extends FfiBenchmarkBase {
|
||||
final Function10double f;
|
||||
|
||||
Floatx10()
|
||||
: f = ffiTestFunctions.lookupFunction<NativeFunction10Float,
|
||||
Function10double>('Function10Float'),
|
||||
super('FfiCall.Floatx10');
|
||||
Floatx10({isLeaf: false})
|
||||
: f = isLeaf
|
||||
? ffiTestFunctions.lookupFunction<NativeFunction10Float,
|
||||
Function10double>('Function10Float', isLeaf: true)
|
||||
: ffiTestFunctions.lookupFunction<NativeFunction10Float,
|
||||
Function10double>('Function10Float', isLeaf: false),
|
||||
super('FfiCall.Floatx10', isLeaf: isLeaf);
|
||||
|
||||
@override
|
||||
void run() {
|
||||
|
@ -690,10 +756,13 @@ class Floatx10 extends FfiBenchmarkBase {
|
|||
class Floatx20 extends FfiBenchmarkBase {
|
||||
final Function20double f;
|
||||
|
||||
Floatx20()
|
||||
: f = ffiTestFunctions.lookupFunction<NativeFunction20Float,
|
||||
Function20double>('Function20Float'),
|
||||
super('FfiCall.Floatx20');
|
||||
Floatx20({isLeaf: false})
|
||||
: f = isLeaf
|
||||
? ffiTestFunctions.lookupFunction<NativeFunction20Float,
|
||||
Function20double>('Function20Float', isLeaf: true)
|
||||
: ffiTestFunctions.lookupFunction<NativeFunction20Float,
|
||||
Function20double>('Function20Float', isLeaf: false),
|
||||
super('FfiCall.Floatx20', isLeaf: isLeaf);
|
||||
|
||||
@override
|
||||
void run() {
|
||||
|
@ -730,10 +799,13 @@ class Floatx20 extends FfiBenchmarkBase {
|
|||
class Doublex01 extends FfiBenchmarkBase {
|
||||
final Function1double f;
|
||||
|
||||
Doublex01()
|
||||
: f = ffiTestFunctions.lookupFunction<NativeFunction1Double,
|
||||
Function1double>('Function1Double'),
|
||||
super('FfiCall.Doublex01');
|
||||
Doublex01({isLeaf: false})
|
||||
: f = isLeaf
|
||||
? ffiTestFunctions.lookupFunction<NativeFunction1Double,
|
||||
Function1double>('Function1Double', isLeaf: true)
|
||||
: ffiTestFunctions.lookupFunction<NativeFunction1Double,
|
||||
Function1double>('Function1Double', isLeaf: false),
|
||||
super('FfiCall.Doublex01', isLeaf: isLeaf);
|
||||
|
||||
@override
|
||||
void run() {
|
||||
|
@ -749,10 +821,13 @@ class Doublex01 extends FfiBenchmarkBase {
|
|||
class Doublex02 extends FfiBenchmarkBase {
|
||||
final Function2double f;
|
||||
|
||||
Doublex02()
|
||||
: f = ffiTestFunctions.lookupFunction<NativeFunction2Double,
|
||||
Function2double>('Function2Double'),
|
||||
super('FfiCall.Doublex02');
|
||||
Doublex02({isLeaf: false})
|
||||
: f = isLeaf
|
||||
? ffiTestFunctions.lookupFunction<NativeFunction2Double,
|
||||
Function2double>('Function2Double', isLeaf: true)
|
||||
: ffiTestFunctions.lookupFunction<NativeFunction2Double,
|
||||
Function2double>('Function2Double', isLeaf: false),
|
||||
super('FfiCall.Doublex02', isLeaf: isLeaf);
|
||||
|
||||
@override
|
||||
void run() {
|
||||
|
@ -768,10 +843,13 @@ class Doublex02 extends FfiBenchmarkBase {
|
|||
class Doublex04 extends FfiBenchmarkBase {
|
||||
final Function4double f;
|
||||
|
||||
Doublex04()
|
||||
: f = ffiTestFunctions.lookupFunction<NativeFunction4Double,
|
||||
Function4double>('Function4Double'),
|
||||
super('FfiCall.Doublex04');
|
||||
Doublex04({isLeaf: false})
|
||||
: f = isLeaf
|
||||
? ffiTestFunctions.lookupFunction<NativeFunction4Double,
|
||||
Function4double>('Function4Double', isLeaf: true)
|
||||
: ffiTestFunctions.lookupFunction<NativeFunction4Double,
|
||||
Function4double>('Function4Double', isLeaf: false),
|
||||
super('FfiCall.Doublex04', isLeaf: isLeaf);
|
||||
|
||||
@override
|
||||
void run() {
|
||||
|
@ -787,10 +865,13 @@ class Doublex04 extends FfiBenchmarkBase {
|
|||
class Doublex10 extends FfiBenchmarkBase {
|
||||
final Function10double f;
|
||||
|
||||
Doublex10()
|
||||
: f = ffiTestFunctions.lookupFunction<NativeFunction10Double,
|
||||
Function10double>('Function10Double'),
|
||||
super('FfiCall.Doublex10');
|
||||
Doublex10({isLeaf: false})
|
||||
: f = isLeaf
|
||||
? ffiTestFunctions.lookupFunction<NativeFunction10Double,
|
||||
Function10double>('Function10Double', isLeaf: true)
|
||||
: ffiTestFunctions.lookupFunction<NativeFunction10Double,
|
||||
Function10double>('Function10Double', isLeaf: false),
|
||||
super('FfiCall.Doublex10', isLeaf: isLeaf);
|
||||
|
||||
@override
|
||||
void run() {
|
||||
|
@ -807,10 +888,13 @@ class Doublex10 extends FfiBenchmarkBase {
|
|||
class Doublex20 extends FfiBenchmarkBase {
|
||||
final Function20double f;
|
||||
|
||||
Doublex20()
|
||||
: f = ffiTestFunctions.lookupFunction<NativeFunction20Double,
|
||||
Function20double>('Function20Double'),
|
||||
super('FfiCall.Doublex20');
|
||||
Doublex20({isLeaf: false})
|
||||
: f = isLeaf
|
||||
? ffiTestFunctions.lookupFunction<NativeFunction20Double,
|
||||
Function20double>('Function20Double', isLeaf: true)
|
||||
: ffiTestFunctions.lookupFunction<NativeFunction20Double,
|
||||
Function20double>('Function20Double', isLeaf: false),
|
||||
super('FfiCall.Doublex20', isLeaf: isLeaf);
|
||||
|
||||
@override
|
||||
void run() {
|
||||
|
@ -847,10 +931,13 @@ class Doublex20 extends FfiBenchmarkBase {
|
|||
class PointerUint8x01 extends FfiBenchmarkBase {
|
||||
final Function1PointerUint8 f;
|
||||
|
||||
PointerUint8x01()
|
||||
: f = ffiTestFunctions.lookupFunction<Function1PointerUint8,
|
||||
Function1PointerUint8>('Function1PointerUint8'),
|
||||
super('FfiCall.PointerUint8x01');
|
||||
PointerUint8x01({isLeaf: false})
|
||||
: f = isLeaf
|
||||
? ffiTestFunctions.lookupFunction<Function1PointerUint8,
|
||||
Function1PointerUint8>('Function1PointerUint8', isLeaf: true)
|
||||
: ffiTestFunctions.lookupFunction<Function1PointerUint8,
|
||||
Function1PointerUint8>('Function1PointerUint8', isLeaf: false),
|
||||
super('FfiCall.PointerUint8x01', isLeaf: isLeaf);
|
||||
|
||||
Pointer<Uint8> p1;
|
||||
@override
|
||||
|
@ -871,10 +958,13 @@ class PointerUint8x01 extends FfiBenchmarkBase {
|
|||
class PointerUint8x02 extends FfiBenchmarkBase {
|
||||
final Function2PointerUint8 f;
|
||||
|
||||
PointerUint8x02()
|
||||
: f = ffiTestFunctions.lookupFunction<Function2PointerUint8,
|
||||
Function2PointerUint8>('Function2PointerUint8'),
|
||||
super('FfiCall.PointerUint8x02');
|
||||
PointerUint8x02({isLeaf: false})
|
||||
: f = isLeaf
|
||||
? ffiTestFunctions.lookupFunction<Function2PointerUint8,
|
||||
Function2PointerUint8>('Function2PointerUint8', isLeaf: true)
|
||||
: ffiTestFunctions.lookupFunction<Function2PointerUint8,
|
||||
Function2PointerUint8>('Function2PointerUint8', isLeaf: false),
|
||||
super('FfiCall.PointerUint8x02', isLeaf: isLeaf);
|
||||
|
||||
Pointer<Uint8> p1, p2;
|
||||
|
||||
|
@ -902,10 +992,13 @@ class PointerUint8x02 extends FfiBenchmarkBase {
|
|||
class PointerUint8x04 extends FfiBenchmarkBase {
|
||||
final Function4PointerUint8 f;
|
||||
|
||||
PointerUint8x04()
|
||||
: f = ffiTestFunctions.lookupFunction<Function4PointerUint8,
|
||||
Function4PointerUint8>('Function4PointerUint8'),
|
||||
super('FfiCall.PointerUint8x04');
|
||||
PointerUint8x04({isLeaf: false})
|
||||
: f = isLeaf
|
||||
? ffiTestFunctions.lookupFunction<Function4PointerUint8,
|
||||
Function4PointerUint8>('Function4PointerUint8', isLeaf: true)
|
||||
: ffiTestFunctions.lookupFunction<Function4PointerUint8,
|
||||
Function4PointerUint8>('Function4PointerUint8', isLeaf: false),
|
||||
super('FfiCall.PointerUint8x04', isLeaf: isLeaf);
|
||||
|
||||
Pointer<Uint8> p1, p2, p3, p4;
|
||||
|
||||
|
@ -935,10 +1028,15 @@ class PointerUint8x04 extends FfiBenchmarkBase {
|
|||
class PointerUint8x10 extends FfiBenchmarkBase {
|
||||
final Function10PointerUint8 f;
|
||||
|
||||
PointerUint8x10()
|
||||
: f = ffiTestFunctions.lookupFunction<Function10PointerUint8,
|
||||
Function10PointerUint8>('Function10PointerUint8'),
|
||||
super('FfiCall.PointerUint8x10');
|
||||
PointerUint8x10({isLeaf: false})
|
||||
: f = isLeaf
|
||||
? ffiTestFunctions.lookupFunction<Function10PointerUint8,
|
||||
Function10PointerUint8>('Function10PointerUint8', isLeaf: true)
|
||||
: ffiTestFunctions
|
||||
.lookupFunction<Function10PointerUint8, Function10PointerUint8>(
|
||||
'Function10PointerUint8',
|
||||
isLeaf: false),
|
||||
super('FfiCall.PointerUint8x10', isLeaf: isLeaf);
|
||||
|
||||
Pointer<Uint8> p1, p2, p3, p4, p5, p6, p7, p8, p9, p10;
|
||||
|
||||
|
@ -974,10 +1072,15 @@ class PointerUint8x10 extends FfiBenchmarkBase {
|
|||
class PointerUint8x20 extends FfiBenchmarkBase {
|
||||
final Function20PointerUint8 f;
|
||||
|
||||
PointerUint8x20()
|
||||
: f = ffiTestFunctions.lookupFunction<Function20PointerUint8,
|
||||
Function20PointerUint8>('Function20PointerUint8'),
|
||||
super('FfiCall.PointerUint8x20');
|
||||
PointerUint8x20({isLeaf: false})
|
||||
: f = isLeaf
|
||||
? ffiTestFunctions.lookupFunction<Function20PointerUint8,
|
||||
Function20PointerUint8>('Function20PointerUint8', isLeaf: true)
|
||||
: ffiTestFunctions
|
||||
.lookupFunction<Function20PointerUint8, Function20PointerUint8>(
|
||||
'Function20PointerUint8',
|
||||
isLeaf: false),
|
||||
super('FfiCall.PointerUint8x20', isLeaf: isLeaf);
|
||||
|
||||
Pointer<Uint8> p1,
|
||||
p2,
|
||||
|
@ -1050,8 +1153,8 @@ class Handlex01 extends FfiBenchmarkBase {
|
|||
|
||||
Handlex01()
|
||||
: f = ffiTestFunctions.lookupFunction<NativeFunction1Handle,
|
||||
Function1Object>('Function1Handle'),
|
||||
super('FfiCall.Handlex01');
|
||||
Function1Object>('Function1Handle', isLeaf: false),
|
||||
super('FfiCall.Handlex01', isLeaf: false);
|
||||
|
||||
@override
|
||||
void run() {
|
||||
|
@ -1069,8 +1172,8 @@ class Handlex02 extends FfiBenchmarkBase {
|
|||
|
||||
Handlex02()
|
||||
: f = ffiTestFunctions.lookupFunction<NativeFunction2Handle,
|
||||
Function2Object>('Function2Handle'),
|
||||
super('FfiCall.Handlex02');
|
||||
Function2Object>('Function2Handle', isLeaf: false),
|
||||
super('FfiCall.Handlex02', isLeaf: false);
|
||||
|
||||
@override
|
||||
void run() {
|
||||
|
@ -1089,8 +1192,8 @@ class Handlex04 extends FfiBenchmarkBase {
|
|||
|
||||
Handlex04()
|
||||
: f = ffiTestFunctions.lookupFunction<NativeFunction4Handle,
|
||||
Function4Object>('Function4Handle'),
|
||||
super('FfiCall.Handlex04');
|
||||
Function4Object>('Function4Handle', isLeaf: false),
|
||||
super('FfiCall.Handlex04', isLeaf: false);
|
||||
|
||||
@override
|
||||
void run() {
|
||||
|
@ -1111,8 +1214,8 @@ class Handlex10 extends FfiBenchmarkBase {
|
|||
|
||||
Handlex10()
|
||||
: f = ffiTestFunctions.lookupFunction<NativeFunction10Handle,
|
||||
Function10Object>('Function10Handle'),
|
||||
super('FfiCall.Handlex10');
|
||||
Function10Object>('Function10Handle', isLeaf: false),
|
||||
super('FfiCall.Handlex10', isLeaf: false);
|
||||
|
||||
@override
|
||||
void run() {
|
||||
|
@ -1139,8 +1242,8 @@ class Handlex20 extends FfiBenchmarkBase {
|
|||
|
||||
Handlex20()
|
||||
: f = ffiTestFunctions.lookupFunction<NativeFunction20Handle,
|
||||
Function20Object>('Function20Handle'),
|
||||
super('FfiCall.Handlex20');
|
||||
Function20Object>('Function20Handle', isLeaf: false),
|
||||
super('FfiCall.Handlex20', isLeaf: false);
|
||||
|
||||
@override
|
||||
void run() {
|
||||
|
@ -1180,10 +1283,12 @@ class Handlex20 extends FfiBenchmarkBase {
|
|||
void main() {
|
||||
final benchmarks = [
|
||||
() => Uint8x01(),
|
||||
() => Uint8x01(isLeaf: true),
|
||||
() => Uint16x01(),
|
||||
() => Uint32x01(),
|
||||
() => Uint64x01(),
|
||||
() => Int8x01(),
|
||||
() => Int8x01(isLeaf: true),
|
||||
() => Int16x01(),
|
||||
() => Int32x01(),
|
||||
() => Int32x02(),
|
||||
|
@ -1195,22 +1300,27 @@ void main() {
|
|||
() => Int64x04(),
|
||||
() => Int64x10(),
|
||||
() => Int64x20(),
|
||||
() => Int64x20(isLeaf: true),
|
||||
() => Int64Mintx01(),
|
||||
() => Int64Mintx01(isLeaf: true),
|
||||
() => Floatx01(),
|
||||
() => Floatx02(),
|
||||
() => Floatx04(),
|
||||
() => Floatx10(),
|
||||
() => Floatx20(),
|
||||
() => Floatx20(isLeaf: true),
|
||||
() => Doublex01(),
|
||||
() => Doublex02(),
|
||||
() => Doublex04(),
|
||||
() => Doublex10(),
|
||||
() => Doublex20(),
|
||||
() => Doublex20(isLeaf: true),
|
||||
() => PointerUint8x01(),
|
||||
() => PointerUint8x02(),
|
||||
() => PointerUint8x04(),
|
||||
() => PointerUint8x10(),
|
||||
() => PointerUint8x20(),
|
||||
() => PointerUint8x20(isLeaf: true),
|
||||
() => Handlex01(),
|
||||
() => Handlex02(),
|
||||
() => Handlex04(),
|
||||
|
|
|
@ -3778,6 +3778,27 @@ const MessageCode messageFfiExpectedConstant = const MessageCode(
|
|||
"FfiExpectedConstant",
|
||||
message: r"""Exceptional return value must be a constant.""");
|
||||
|
||||
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
|
||||
const Template<Message Function(String name)> templateFfiExpectedConstantArg =
|
||||
const Template<Message Function(String name)>(
|
||||
messageTemplate: r"""Argument '#name' must be a constant.""",
|
||||
withArguments: _withArgumentsFfiExpectedConstantArg);
|
||||
|
||||
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
|
||||
const Code<Message Function(String name)> codeFfiExpectedConstantArg =
|
||||
const Code<Message Function(String name)>(
|
||||
"FfiExpectedConstantArg",
|
||||
);
|
||||
|
||||
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
|
||||
Message _withArgumentsFfiExpectedConstantArg(String name) {
|
||||
if (name.isEmpty) throw 'No name provided';
|
||||
name = demangleMixinApplicationName(name);
|
||||
return new Message(codeFfiExpectedConstantArg,
|
||||
message: """Argument '${name}' must be a constant.""",
|
||||
arguments: {'name': name});
|
||||
}
|
||||
|
||||
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
|
||||
const Template<Message Function(String name)>
|
||||
templateFfiExtendsOrImplementsSealedClass =
|
||||
|
@ -3931,6 +3952,24 @@ Message _withArgumentsFfiFieldNull(String name) {
|
|||
arguments: {'name': name});
|
||||
}
|
||||
|
||||
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
|
||||
const Code<Null> codeFfiLeafCallMustNotReturnHandle =
|
||||
messageFfiLeafCallMustNotReturnHandle;
|
||||
|
||||
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
|
||||
const MessageCode messageFfiLeafCallMustNotReturnHandle = const MessageCode(
|
||||
"FfiLeafCallMustNotReturnHandle",
|
||||
message: r"""FFI leaf call must not have Handle return type.""");
|
||||
|
||||
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
|
||||
const Code<Null> codeFfiLeafCallMustNotTakeHandle =
|
||||
messageFfiLeafCallMustNotTakeHandle;
|
||||
|
||||
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
|
||||
const MessageCode messageFfiLeafCallMustNotTakeHandle = const MessageCode(
|
||||
"FfiLeafCallMustNotTakeHandle",
|
||||
message: r"""FFI leaf call must not have Handle argument types.""");
|
||||
|
||||
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
|
||||
const Template<
|
||||
Message Function(String name)> templateFfiNotStatic = const Template<
|
||||
|
|
|
@ -462,6 +462,7 @@ const List<ErrorCode> errorCodeValues = [
|
|||
CompileTimeErrorCode.YIELD_IN_NON_GENERATOR,
|
||||
CompileTimeErrorCode.YIELD_OF_INVALID_TYPE,
|
||||
FfiCode.ANNOTATION_ON_POINTER_FIELD,
|
||||
FfiCode.ARGUMENT_MUST_BE_A_CONSTANT,
|
||||
FfiCode.EMPTY_STRUCT,
|
||||
FfiCode.EXTRA_ANNOTATION_ON_STRUCT_FIELD,
|
||||
FfiCode.EXTRA_SIZE_ANNOTATION_CARRAY,
|
||||
|
@ -470,6 +471,8 @@ const List<ErrorCode> errorCodeValues = [
|
|||
FfiCode.GENERIC_STRUCT_SUBCLASS,
|
||||
FfiCode.INVALID_EXCEPTION_VALUE,
|
||||
FfiCode.INVALID_FIELD_TYPE_IN_STRUCT,
|
||||
FfiCode.LEAF_CALL_MUST_NOT_RETURN_HANDLE,
|
||||
FfiCode.LEAF_CALL_MUST_NOT_TAKE_HANDLE,
|
||||
FfiCode.MISMATCHED_ANNOTATION_ON_STRUCT_FIELD,
|
||||
FfiCode.MISSING_ANNOTATION_ON_STRUCT_FIELD,
|
||||
FfiCode.MISSING_EXCEPTION_VALUE,
|
||||
|
|
|
@ -21,6 +21,15 @@ class FfiCode extends AnalyzerErrorCode {
|
|||
"any annotations.",
|
||||
correction: "Try removing the annotation.");
|
||||
|
||||
/**
|
||||
* Parameters:
|
||||
* 0: the name of the argument
|
||||
*/
|
||||
static const FfiCode ARGUMENT_MUST_BE_A_CONSTANT = FfiCode(
|
||||
name: 'ARGUMENT_MUST_BE_A_CONSTANT',
|
||||
message: "Argument '{0}' must be a constant.",
|
||||
correction: "Try replacing the value with a literal or const.");
|
||||
|
||||
/**
|
||||
* Parameters:
|
||||
* 0: the name of the struct class
|
||||
|
@ -103,6 +112,22 @@ class FfiCode extends AnalyzerErrorCode {
|
|||
"Try using 'int', 'double', 'Array', 'Pointer', or subtype of "
|
||||
"'Struct' or 'Union'.");
|
||||
|
||||
/**
|
||||
* No parameters.
|
||||
*/
|
||||
static const FfiCode LEAF_CALL_MUST_NOT_RETURN_HANDLE = FfiCode(
|
||||
name: 'LEAF_CALL_MUST_NOT_RETURN_HANDLE',
|
||||
message: "FFI leaf call must not return a Handle.",
|
||||
correction: "Try changing the return type to primitive or struct.");
|
||||
|
||||
/**
|
||||
* No parameters.
|
||||
*/
|
||||
static const FfiCode LEAF_CALL_MUST_NOT_TAKE_HANDLE = FfiCode(
|
||||
name: 'LEAF_CALL_MUST_NOT_TAKE_HANDLE',
|
||||
message: "FFI leaf call must not take arguments of type Handle.",
|
||||
correction: "Try changing the argument type to primitive or struct.");
|
||||
|
||||
/**
|
||||
* No parameters.
|
||||
*/
|
||||
|
|
|
@ -8,6 +8,7 @@ import 'package:analyzer/dart/element/element.dart';
|
|||
import 'package:analyzer/dart/element/type.dart';
|
||||
import 'package:analyzer/error/listener.dart';
|
||||
import 'package:analyzer/src/dart/ast/extensions.dart';
|
||||
import 'package:analyzer/src/dart/element/element.dart';
|
||||
import 'package:analyzer/src/dart/element/type_system.dart';
|
||||
import 'package:analyzer/src/dart/error/ffi_code.dart';
|
||||
|
||||
|
@ -20,6 +21,7 @@ class FfiVerifier extends RecursiveAstVisitor<void> {
|
|||
static const _allocatorExtensionName = 'AllocatorAlloc';
|
||||
static const _arrayClassName = 'Array';
|
||||
static const _dartFfiLibraryName = 'dart.ffi';
|
||||
static const _isLeafParamName = 'isLeaf';
|
||||
static const _opaqueClassName = 'Opaque';
|
||||
|
||||
static const List<String> _primitiveIntegerNativeTypes = [
|
||||
|
@ -341,6 +343,26 @@ class FfiVerifier extends RecursiveAstVisitor<void> {
|
|||
return false;
|
||||
}
|
||||
|
||||
// Get the const bool value of `expr` if it exists.
|
||||
// Return null if it isn't a const bool.
|
||||
bool? _maybeGetBoolConstValue(Expression expr) {
|
||||
if (expr is BooleanLiteral) {
|
||||
return expr.value;
|
||||
} else if (expr is Identifier) {
|
||||
final staticElm = expr.staticElement;
|
||||
if (staticElm is ConstVariableElement) {
|
||||
return staticElm.computeConstantValue()?.toBoolValue();
|
||||
}
|
||||
if (staticElm is PropertyAccessorElementImpl) {
|
||||
final v = staticElm.variable;
|
||||
if (v is ConstVariableElement) {
|
||||
return v.computeConstantValue()?.toBoolValue();
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
_PrimitiveDartType _primitiveNativeType(DartType nativeType) {
|
||||
if (nativeType is InterfaceType) {
|
||||
final element = nativeType.element;
|
||||
|
@ -461,7 +483,9 @@ class FfiVerifier extends RecursiveAstVisitor<void> {
|
|||
_errorReporter.reportErrorForNode(
|
||||
FfiCode.MUST_BE_A_SUBTYPE, node, [TPrime, F, 'asFunction']);
|
||||
}
|
||||
_validateFfiLeafCallUsesNoHandles(node, TPrime, node);
|
||||
}
|
||||
_validateIsLeafIsConst(node);
|
||||
}
|
||||
|
||||
/// Validates that the given [nativeType] is, when native types are converted
|
||||
|
@ -551,6 +575,37 @@ class FfiVerifier extends RecursiveAstVisitor<void> {
|
|||
}
|
||||
}
|
||||
|
||||
void _validateFfiLeafCallUsesNoHandles(
|
||||
MethodInvocation node, DartType nativeType, AstNode errorNode) {
|
||||
final args = node.argumentList.arguments;
|
||||
if (args.isNotEmpty) {
|
||||
for (final arg in args) {
|
||||
if (arg is NamedExpression) {
|
||||
if (arg.element?.name == _isLeafParamName) {
|
||||
// Handles are ok for regular (non-leaf) calls. Check `isLeaf:true`.
|
||||
final bool? isLeaf = _maybeGetBoolConstValue(arg.expression);
|
||||
if (isLeaf != null && isLeaf) {
|
||||
if (nativeType is FunctionType) {
|
||||
if (_primitiveNativeType(nativeType.returnType) ==
|
||||
_PrimitiveDartType.handle) {
|
||||
_errorReporter.reportErrorForNode(
|
||||
FfiCode.LEAF_CALL_MUST_NOT_RETURN_HANDLE, errorNode);
|
||||
}
|
||||
for (final param in nativeType.normalParameterTypes) {
|
||||
if (_primitiveNativeType(param) ==
|
||||
_PrimitiveDartType.handle) {
|
||||
_errorReporter.reportErrorForNode(
|
||||
FfiCode.LEAF_CALL_MUST_NOT_TAKE_HANDLE, errorNode);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Validate that the fields declared by the given [node] meet the
|
||||
/// requirements for fields within a struct or union class.
|
||||
void _validateFieldsInCompound(FieldDeclaration node) {
|
||||
|
@ -654,6 +709,27 @@ class FfiVerifier extends RecursiveAstVisitor<void> {
|
|||
}
|
||||
}
|
||||
|
||||
void _validateIsLeafIsConst(MethodInvocation node) {
|
||||
// Ensure `isLeaf` is const as we need the value at compile time to know
|
||||
// which trampoline to generate.
|
||||
final args = node.argumentList.arguments;
|
||||
if (args.isNotEmpty) {
|
||||
for (final arg in args) {
|
||||
if (arg is NamedExpression) {
|
||||
if (arg.element?.name == _isLeafParamName) {
|
||||
if (_maybeGetBoolConstValue(arg.expression) == null) {
|
||||
final AstNode errorNode = node;
|
||||
_errorReporter.reportErrorForNode(
|
||||
FfiCode.ARGUMENT_MUST_BE_A_CONSTANT,
|
||||
errorNode,
|
||||
[_isLeafParamName]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Validate the invocation of the instance method
|
||||
/// `DynamicLibrary.lookupFunction<S, F>()`.
|
||||
void _validateLookupFunction(MethodInvocation node) {
|
||||
|
@ -678,6 +754,8 @@ class FfiVerifier extends RecursiveAstVisitor<void> {
|
|||
_errorReporter.reportErrorForNode(
|
||||
FfiCode.MUST_BE_A_SUBTYPE, errorNode, [S, F, 'lookupFunction']);
|
||||
}
|
||||
_validateIsLeafIsConst(node);
|
||||
_validateFfiLeafCallUsesNoHandles(node, S, typeArguments![0]);
|
||||
}
|
||||
|
||||
/// Validate that none of the [annotations] are from `dart:ffi`.
|
||||
|
|
|
@ -620,6 +620,8 @@ class NativeType {
|
|||
const NativeType();
|
||||
}
|
||||
|
||||
class Handle extends NativeType {}
|
||||
|
||||
class Void extends NativeType {}
|
||||
|
||||
class Int8 extends NativeType {
|
||||
|
@ -674,7 +676,7 @@ final Pointer<Never> nullptr = Pointer.fromAddress(0);
|
|||
|
||||
extension NativeFunctionPointer<NF extends Function>
|
||||
on Pointer<NativeFunction<NF>> {
|
||||
external DF asFunction<DF extends Function>();
|
||||
external DF asFunction<DF extends Function>({bool isLeaf:false});
|
||||
}
|
||||
|
||||
class _Compound extends NativeType {}
|
||||
|
@ -689,11 +691,13 @@ class Packed {
|
|||
const Packed(this.memberAlignment);
|
||||
}
|
||||
|
||||
abstract class DynamicLibrary {}
|
||||
abstract class DynamicLibrary {
|
||||
external factory DynamicLibrary.open(String name);
|
||||
}
|
||||
|
||||
extension DynamicLibraryExtension on DynamicLibrary {
|
||||
external F lookupFunction<T extends Function, F extends Function>(
|
||||
String symbolName);
|
||||
String symbolName, {bool isLeaf:false});
|
||||
}
|
||||
|
||||
abstract class NativeFunction<T extends Function> extends NativeType {}
|
||||
|
|
|
@ -0,0 +1,78 @@
|
|||
// Copyright (c) 2021, 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.
|
||||
|
||||
import 'package:analyzer/src/dart/error/ffi_code.dart';
|
||||
import 'package:test_reflective_loader/test_reflective_loader.dart';
|
||||
|
||||
import '../dart/resolution/context_collection_resolution.dart';
|
||||
|
||||
main() {
|
||||
defineReflectiveSuite(() {
|
||||
defineReflectiveTests(ArgumentMustBeAConstantTest);
|
||||
});
|
||||
}
|
||||
|
||||
@reflectiveTest
|
||||
class ArgumentMustBeAConstantTest extends PubPackageResolutionTest {
|
||||
test_AsFunctionIsLeafGlobal() async {
|
||||
await assertErrorsInCode(r'''
|
||||
import 'dart:ffi';
|
||||
typedef Int8UnOp = Int8 Function(Int8);
|
||||
typedef IntUnOp = int Function(int);
|
||||
bool isLeaf = false;
|
||||
doThings() {
|
||||
Pointer<NativeFunction<Int8UnOp>> p = Pointer.fromAddress(1337);
|
||||
IntUnOp f = p.asFunction(isLeaf:isLeaf);
|
||||
f(8);
|
||||
}
|
||||
''', [
|
||||
error(FfiCode.ARGUMENT_MUST_BE_A_CONSTANT, 211, 27),
|
||||
]);
|
||||
}
|
||||
|
||||
test_AsFunctionIsLeafLocal() async {
|
||||
await assertErrorsInCode(r'''
|
||||
import 'dart:ffi';
|
||||
typedef Int8UnOp = Int8 Function(Int8);
|
||||
typedef IntUnOp = int Function(int);
|
||||
doThings() {
|
||||
bool isLeaf = false;
|
||||
Pointer<NativeFunction<Int8UnOp>> p = Pointer.fromAddress(1337);
|
||||
IntUnOp f = p.asFunction(isLeaf:isLeaf);
|
||||
f(8);
|
||||
}
|
||||
''', [
|
||||
error(FfiCode.ARGUMENT_MUST_BE_A_CONSTANT, 213, 27),
|
||||
]);
|
||||
}
|
||||
|
||||
test_AsFunctionIsLeafParam() async {
|
||||
await assertErrorsInCode(r'''
|
||||
import 'dart:ffi';
|
||||
typedef Int8UnOp = Int8 Function(Int8);
|
||||
typedef IntUnOp = int Function(int);
|
||||
doThings(bool isLeaf) {
|
||||
Pointer<NativeFunction<Int8UnOp>> p = Pointer.fromAddress(1337);
|
||||
IntUnOp f = p.asFunction(isLeaf:isLeaf);
|
||||
f(8);
|
||||
}
|
||||
''', [
|
||||
error(FfiCode.ARGUMENT_MUST_BE_A_CONSTANT, 201, 27),
|
||||
]);
|
||||
}
|
||||
|
||||
test_LookupFunctionIsLeaf() async {
|
||||
await assertErrorsInCode(r'''
|
||||
import 'dart:ffi';
|
||||
typedef Int8UnOp = Int8 Function(Int8);
|
||||
typedef IntUnOp = int Function(int);
|
||||
doThings(bool isLeaf) {
|
||||
DynamicLibrary l = DynamicLibrary.open("my_lib");
|
||||
l.lookupFunction<Int8UnOp, IntUnOp>("timesFour", isLeaf:isLeaf);
|
||||
}
|
||||
''', [
|
||||
error(FfiCode.ARGUMENT_MUST_BE_A_CONSTANT, 174, 63),
|
||||
]);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,77 @@
|
|||
// Copyright (c) 2021, 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.
|
||||
|
||||
import 'package:analyzer/src/dart/error/ffi_code.dart';
|
||||
import 'package:test_reflective_loader/test_reflective_loader.dart';
|
||||
|
||||
import '../dart/resolution/context_collection_resolution.dart';
|
||||
|
||||
main() {
|
||||
defineReflectiveSuite(() {
|
||||
defineReflectiveTests(LeafCallMustNotUseHandle);
|
||||
});
|
||||
}
|
||||
|
||||
@reflectiveTest
|
||||
class LeafCallMustNotUseHandle extends PubPackageResolutionTest {
|
||||
test_AsFunctionReturnsHandle() async {
|
||||
await assertErrorsInCode(r'''
|
||||
import 'dart:ffi';
|
||||
typedef NativeReturnsHandle = Handle Function();
|
||||
typedef ReturnsHandle = Object Function();
|
||||
doThings() {
|
||||
Pointer<NativeFunction<NativeReturnsHandle>> p = Pointer.fromAddress(1337);
|
||||
ReturnsHandle f = p.asFunction(isLeaf:true);
|
||||
f();
|
||||
}
|
||||
''', [
|
||||
error(FfiCode.LEAF_CALL_MUST_NOT_RETURN_HANDLE, 222, 25),
|
||||
]);
|
||||
}
|
||||
|
||||
test_AsFunctionTakesHandle() async {
|
||||
await assertErrorsInCode(r'''
|
||||
import 'dart:ffi';
|
||||
typedef NativeTakesHandle = Void Function(Handle);
|
||||
typedef TakesHandle = void Function(Object);
|
||||
class MyClass {}
|
||||
doThings() {
|
||||
Pointer<NativeFunction<NativeTakesHandle>> p = Pointer.fromAddress(1337);
|
||||
TakesHandle f = p.asFunction(isLeaf:true);
|
||||
f(MyClass());
|
||||
}
|
||||
''', [
|
||||
error(FfiCode.LEAF_CALL_MUST_NOT_TAKE_HANDLE, 239, 25),
|
||||
]);
|
||||
}
|
||||
|
||||
test_LookupFunctionReturnsHandle() async {
|
||||
await assertErrorsInCode(r'''
|
||||
import 'dart:ffi';
|
||||
typedef NativeReturnsHandle = Handle Function();
|
||||
typedef ReturnsHandle = Object Function();
|
||||
doThings() {
|
||||
DynamicLibrary l = DynamicLibrary.open("my_lib");
|
||||
l.lookupFunction<NativeReturnsHandle, ReturnsHandle>("timesFour", isLeaf:true);
|
||||
}
|
||||
''', [
|
||||
error(FfiCode.LEAF_CALL_MUST_NOT_RETURN_HANDLE, 195, 19),
|
||||
]);
|
||||
}
|
||||
|
||||
test_LookupFunctionTakesHandle() async {
|
||||
await assertErrorsInCode(r'''
|
||||
import 'dart:ffi';
|
||||
typedef NativeTakesHandle = Void Function(Handle);
|
||||
typedef TakesHandle = void Function(Object);
|
||||
class MyClass {}
|
||||
doThings() {
|
||||
DynamicLibrary l = DynamicLibrary.open("my_lib");
|
||||
l.lookupFunction<NativeTakesHandle, TakesHandle>("timesFour", isLeaf:true);
|
||||
}
|
||||
''', [
|
||||
error(FfiCode.LEAF_CALL_MUST_NOT_TAKE_HANDLE, 216, 17),
|
||||
]);
|
||||
}
|
||||
}
|
|
@ -17,6 +17,7 @@ import 'ambiguous_import_test.dart' as ambiguous_import;
|
|||
import 'ambiguous_set_or_map_literal_test.dart' as ambiguous_set_or_map_literal;
|
||||
import 'annotation_on_pointer_field_test.dart' as annotation_on_pointer_field;
|
||||
import 'annotation_syntax_test.dart' as annotation_syntax;
|
||||
import 'argument_must_be_a_constant_test.dart' as argument_must_be_a_constant;
|
||||
import 'argument_type_not_assignable_test.dart' as argument_type_not_assignable;
|
||||
import 'argument_type_not_assignable_to_error_handler_test.dart'
|
||||
as argument_type_not_assignable_to_error_handler;
|
||||
|
@ -188,6 +189,8 @@ import 'extra_positional_arguments_test.dart' as extra_positional_arguments;
|
|||
import 'extra_size_annotation_carray_test.dart' as extra_size_annotation_carray;
|
||||
import 'ffi_array_multi_non_positive_input_test.dart'
|
||||
as ffi_array_multi_non_positive_input_test;
|
||||
import 'ffi_leaf_call_must_not_use_handle_test.dart'
|
||||
as ffi_leaf_call_must_not_use_handle;
|
||||
import 'field_in_struct_with_initializer_test.dart'
|
||||
as field_in_struct_with_initializer;
|
||||
import 'field_initialized_by_multiple_initializers_test.dart'
|
||||
|
@ -700,6 +703,7 @@ main() {
|
|||
ambiguous_set_or_map_literal.main();
|
||||
annotation_on_pointer_field.main();
|
||||
annotation_syntax.main();
|
||||
argument_must_be_a_constant.main();
|
||||
argument_type_not_assignable.main();
|
||||
argument_type_not_assignable_to_error_handler.main();
|
||||
assert_in_redirecting_constructor.main();
|
||||
|
@ -816,6 +820,7 @@ main() {
|
|||
extra_positional_arguments.main();
|
||||
extra_size_annotation_carray.main();
|
||||
ffi_array_multi_non_positive_input_test.main();
|
||||
ffi_leaf_call_must_not_use_handle.main();
|
||||
field_in_struct_with_initializer.main();
|
||||
field_initialized_by_multiple_initializers.main();
|
||||
final_initialized_in_declaration_and_constructor.main();
|
||||
|
|
|
@ -52,11 +52,14 @@ export '../fasta/fasta_codes.dart'
|
|||
LocatedMessage,
|
||||
messageFfiExceptionalReturnNull,
|
||||
messageFfiExpectedConstant,
|
||||
messageFfiLeafCallMustNotReturnHandle,
|
||||
messageFfiLeafCallMustNotTakeHandle,
|
||||
messageFfiPackedAnnotationAlignment,
|
||||
messageNonPositiveArrayDimensions,
|
||||
noLength,
|
||||
templateFfiDartTypeMismatch,
|
||||
templateFfiEmptyStruct,
|
||||
templateFfiExpectedConstantArg,
|
||||
templateFfiExpectedExceptionalReturn,
|
||||
templateFfiExpectedNoExceptionalReturn,
|
||||
templateFfiExtendsOrImplementsSealedClass,
|
||||
|
|
|
@ -326,6 +326,7 @@ FfiDartTypeMismatch/analyzerCode: Fail
|
|||
FfiEmptyStruct/analyzerCode: Fail
|
||||
FfiExceptionalReturnNull/analyzerCode: Fail
|
||||
FfiExpectedConstant/analyzerCode: Fail
|
||||
FfiExpectedConstantArg/analyzerCode: Fail
|
||||
FfiExpectedExceptionalReturn/analyzerCode: Fail
|
||||
FfiExpectedNoExceptionalReturn/analyzerCode: Fail
|
||||
FfiExtendsOrImplementsSealedClass/analyzerCode: Fail
|
||||
|
@ -334,6 +335,8 @@ FfiFieldCyclic/analyzerCode: Fail
|
|||
FfiFieldInitializer/analyzerCode: Fail
|
||||
FfiFieldNoAnnotation/analyzerCode: Fail
|
||||
FfiFieldNull/analyzerCode: Fail
|
||||
FfiLeafCallMustNotReturnHandle/analyzerCode: Fail
|
||||
FfiLeafCallMustNotTakeHandle/analyzerCode: Fail
|
||||
FfiNotStatic/analyzerCode: Fail
|
||||
FfiPackedAnnotation/analyzerCode: Fail
|
||||
FfiPackedAnnotationAlignment/analyzerCode: Fail
|
||||
|
|
|
@ -4501,6 +4501,21 @@ FfiExceptionalReturnNull:
|
|||
template: "Exceptional return value must not be null."
|
||||
external: test/ffi_test.dart
|
||||
|
||||
FfiExpectedConstantArg:
|
||||
# Used by dart:ffi
|
||||
template: "Argument '#name' must be a constant."
|
||||
external: test/ffi_test.dart
|
||||
|
||||
FfiLeafCallMustNotTakeHandle:
|
||||
# Used by dart:ffi
|
||||
template: "FFI leaf call must not have Handle argument types."
|
||||
external: test/ffi_test.dart
|
||||
|
||||
FfiLeafCallMustNotReturnHandle:
|
||||
# Used by dart:ffi
|
||||
template: "FFI leaf call must not have Handle return type."
|
||||
external: test/ffi_test.dart
|
||||
|
||||
SpreadTypeMismatch:
|
||||
template: "Unexpected type '#type' of a spread. Expected 'dynamic' or an Iterable."
|
||||
script:
|
||||
|
|
|
@ -221,6 +221,7 @@ class FfiTransformer extends Transformer {
|
|||
final Library ffiLibrary;
|
||||
final Class allocatorClass;
|
||||
final Class nativeFunctionClass;
|
||||
final Class handleClass;
|
||||
final Class opaqueClass;
|
||||
final Class arrayClass;
|
||||
final Class arraySizeClass;
|
||||
|
@ -324,6 +325,7 @@ class FfiTransformer extends Transformer {
|
|||
ffiLibrary = index.getLibrary('dart:ffi'),
|
||||
allocatorClass = index.getClass('dart:ffi', 'Allocator'),
|
||||
nativeFunctionClass = index.getClass('dart:ffi', 'NativeFunction'),
|
||||
handleClass = index.getClass('dart:ffi', 'Handle'),
|
||||
opaqueClass = index.getClass('dart:ffi', 'Opaque'),
|
||||
arrayClass = index.getClass('dart:ffi', 'Array'),
|
||||
arraySizeClass = index.getClass('dart:ffi', '_ArraySize'),
|
||||
|
|
|
@ -8,7 +8,10 @@ import 'package:front_end/src/api_unstable/vm.dart'
|
|||
show
|
||||
messageFfiExceptionalReturnNull,
|
||||
messageFfiExpectedConstant,
|
||||
messageFfiLeafCallMustNotReturnHandle,
|
||||
messageFfiLeafCallMustNotTakeHandle,
|
||||
templateFfiDartTypeMismatch,
|
||||
templateFfiExpectedConstantArg,
|
||||
templateFfiExpectedExceptionalReturn,
|
||||
templateFfiExpectedNoExceptionalReturn,
|
||||
templateFfiExtendsOrImplementsSealedClass,
|
||||
|
@ -176,12 +179,14 @@ class _FfiUseSiteTransformer extends FfiTransformer {
|
|||
}
|
||||
}
|
||||
} else if (target == lookupFunctionMethod) {
|
||||
final DartType nativeType = InterfaceType(
|
||||
final nativeType = InterfaceType(
|
||||
nativeFunctionClass, Nullability.legacy, [node.arguments.types[0]]);
|
||||
final DartType dartType = node.arguments.types[1];
|
||||
|
||||
_ensureNativeTypeValid(nativeType, node);
|
||||
_ensureNativeTypeToDartType(nativeType, dartType, node);
|
||||
_ensureIsLeafIsConst(node);
|
||||
_ensureLeafCallDoesNotUseHandles(nativeType, node);
|
||||
|
||||
final replacement = _replaceLookupFunction(node);
|
||||
|
||||
|
@ -197,20 +202,27 @@ class _FfiUseSiteTransformer extends FfiTransformer {
|
|||
}
|
||||
return replacement;
|
||||
} else if (target == asFunctionMethod) {
|
||||
final DartType dartType = node.arguments.types[1];
|
||||
final dartType = node.arguments.types[1];
|
||||
final DartType nativeType = InterfaceType(
|
||||
nativeFunctionClass, Nullability.legacy, [node.arguments.types[0]]);
|
||||
|
||||
_ensureNativeTypeValid(nativeType, node);
|
||||
_ensureNativeTypeToDartType(nativeType, dartType, node);
|
||||
_ensureIsLeafIsConst(node);
|
||||
_ensureLeafCallDoesNotUseHandles(nativeType, node);
|
||||
|
||||
final DartType nativeSignature =
|
||||
(nativeType as InterfaceType).typeArguments[0];
|
||||
|
||||
bool isLeaf = _getIsLeafBoolean(node);
|
||||
if (isLeaf == null) {
|
||||
isLeaf = false;
|
||||
}
|
||||
|
||||
// Inline function body to make all type arguments instatiated.
|
||||
final replacement = StaticInvocation(
|
||||
asFunctionInternal,
|
||||
Arguments([node.arguments.positional[0]],
|
||||
Arguments([node.arguments.positional[0], BoolLiteral(isLeaf)],
|
||||
types: [dartType, nativeSignature]));
|
||||
|
||||
if (dartType is FunctionType) {
|
||||
|
@ -411,12 +423,12 @@ class _FfiUseSiteTransformer extends FfiTransformer {
|
|||
Expression _replaceLookupFunction(StaticInvocation node) {
|
||||
// The generated code looks like:
|
||||
//
|
||||
// _asFunctionInternal<DS, NS>(lookup<NativeFunction<NS>>(symbolName))
|
||||
|
||||
// _asFunctionInternal<DS, NS>(lookup<NativeFunction<NS>>(symbolName),
|
||||
// isLeaf)
|
||||
final DartType nativeSignature = node.arguments.types[0];
|
||||
final DartType dartSignature = node.arguments.types[1];
|
||||
|
||||
final Arguments args = Arguments([
|
||||
final Arguments lookupArgs = Arguments([
|
||||
node.arguments.positional[1]
|
||||
], types: [
|
||||
InterfaceType(nativeFunctionClass, Nullability.legacy, [nativeSignature])
|
||||
|
@ -425,11 +437,18 @@ class _FfiUseSiteTransformer extends FfiTransformer {
|
|||
final Expression lookupResult = MethodInvocation(
|
||||
node.arguments.positional[0],
|
||||
Name("lookup"),
|
||||
args,
|
||||
lookupArgs,
|
||||
libraryLookupMethod);
|
||||
|
||||
return StaticInvocation(asFunctionInternal,
|
||||
Arguments([lookupResult], types: [dartSignature, nativeSignature]));
|
||||
bool isLeaf = _getIsLeafBoolean(node);
|
||||
if (isLeaf == null) {
|
||||
isLeaf = false;
|
||||
}
|
||||
|
||||
return StaticInvocation(
|
||||
asFunctionInternal,
|
||||
Arguments([lookupResult, BoolLiteral(isLeaf)],
|
||||
types: [dartSignature, nativeSignature]));
|
||||
}
|
||||
|
||||
// We need to rewrite calls to 'fromFunction' into two calls, representing the
|
||||
|
@ -818,6 +837,72 @@ class _FfiUseSiteTransformer extends FfiTransformer {
|
|||
throw _FfiStaticTypeError();
|
||||
}
|
||||
}
|
||||
|
||||
// Returns
|
||||
// - `true` if leaf
|
||||
// - `false` if not leaf
|
||||
// - `null` if the expression is not valid (e.g. non-const bool, null)
|
||||
bool _getIsLeafBoolean(StaticInvocation node) {
|
||||
for (final named in node.arguments.named) {
|
||||
if (named.name == 'isLeaf') {
|
||||
final expr = named.value;
|
||||
if (expr is BoolLiteral) {
|
||||
return expr.value;
|
||||
} else if (expr is ConstantExpression) {
|
||||
final constant = expr.constant;
|
||||
if (constant is BoolConstant) {
|
||||
return constant.value;
|
||||
}
|
||||
}
|
||||
// isLeaf is passed some invalid value.
|
||||
return null;
|
||||
}
|
||||
}
|
||||
// isLeaf defaults to false.
|
||||
return false;
|
||||
}
|
||||
|
||||
void _ensureIsLeafIsConst(StaticInvocation node) {
|
||||
final isLeaf = _getIsLeafBoolean(node);
|
||||
if (isLeaf == null) {
|
||||
diagnosticReporter.report(
|
||||
templateFfiExpectedConstantArg.withArguments('isLeaf'),
|
||||
node.fileOffset,
|
||||
1,
|
||||
node.location.file);
|
||||
// Throw so we don't get another error about not replacing
|
||||
// `lookupFunction`, which will shadow the above error.
|
||||
throw _FfiStaticTypeError();
|
||||
}
|
||||
}
|
||||
|
||||
void _ensureLeafCallDoesNotUseHandles(
|
||||
InterfaceType nativeType, StaticInvocation node) {
|
||||
// Handles are only disallowed for leaf calls.
|
||||
final isLeaf = _getIsLeafBoolean(node);
|
||||
if (isLeaf == null || isLeaf == false) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if return type is Handle.
|
||||
final functionType = nativeType.typeArguments[0];
|
||||
if (functionType is FunctionType) {
|
||||
final returnType = functionType.returnType;
|
||||
if (returnType is InterfaceType) {
|
||||
if (returnType.classNode == handleClass) {
|
||||
diagnosticReporter.report(messageFfiLeafCallMustNotReturnHandle,
|
||||
node.fileOffset, 1, node.location.file);
|
||||
}
|
||||
}
|
||||
// Check if any of the argument types are Handle.
|
||||
for (InterfaceType param in functionType.positionalParameters) {
|
||||
if (param.classNode == handleClass) {
|
||||
diagnosticReporter.report(messageFfiLeafCallMustNotTakeHandle,
|
||||
node.fileOffset, 1, node.location.file);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Used internally for abnormal control flow to prevent cascading error
|
||||
|
|
|
@ -67,7 +67,7 @@ static method testLookupFunctionReturn() → void {
|
|||
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"));
|
||||
} =>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"), false);
|
||||
final self::Struct1* struct1 = [@vm.call-site-attributes.metadata=receiverType:#lib::Struct1* Function()*] function1(){() →* self::Struct1*};
|
||||
core::print(struct1);
|
||||
}
|
||||
|
@ -75,7 +75,7 @@ static method testAsFunctionReturn() → void {
|
|||
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);
|
||||
} =>ffi::_asFunctionInternal<() →* self::Struct2*, () →* self::Struct2*>(pointer, false);
|
||||
final self::Struct2* struct2 = [@vm.call-site-attributes.metadata=receiverType:#lib::Struct2* Function()*] function2(){() →* self::Struct2*};
|
||||
core::print(struct2);
|
||||
}
|
||||
|
@ -90,12 +90,12 @@ static method testFromFunctionArgument() → void {
|
|||
}
|
||||
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"));
|
||||
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"), false);
|
||||
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);
|
||||
final (self::Struct6*) →* void function6 = [@vm.inferred-type.metadata=dart.core::_Closure?] ffi::_asFunctionInternal<(self::Struct6*) →* void, (self::Struct6*) →* ffi::Void*>(pointer, false);
|
||||
core::print(function6);
|
||||
}
|
||||
static method returnStruct7() → self::Struct7* {
|
||||
|
|
|
@ -109,6 +109,13 @@ DART_EXPORT void Regress37069(uint64_t a,
|
|||
Dart_ExecuteInternalCommand("gc-now", nullptr);
|
||||
}
|
||||
|
||||
DART_EXPORT uint8_t IsThreadInGenerated() {
|
||||
return Dart_ExecuteInternalCommand("is-thread-in-generated", nullptr) !=
|
||||
nullptr
|
||||
? 1
|
||||
: 0;
|
||||
}
|
||||
|
||||
#if !defined(HOST_OS_WINDOWS)
|
||||
DART_EXPORT void* UnprotectCodeOtherThread(void* isolate,
|
||||
std::condition_variable* var,
|
||||
|
@ -276,6 +283,35 @@ DART_EXPORT intptr_t TestCallbackWrongIsolate(void (*fn)()) {
|
|||
return ExpectAbort(fn);
|
||||
}
|
||||
|
||||
DART_EXPORT intptr_t TestCallbackLeaf(void (*fn)()) {
|
||||
#if defined(DEBUG)
|
||||
// Calling a callback from a leaf call will crash on T->IsAtSafepoint().
|
||||
return ExpectAbort(fn);
|
||||
#else
|
||||
// The above will only crash in debug as ASSERTS are disabled in all other
|
||||
// build modes.
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
void CallDebugName() {
|
||||
Dart_DebugName();
|
||||
}
|
||||
|
||||
DART_EXPORT intptr_t TestLeafCallApi(void (*fn)()) {
|
||||
// This should be fine since it's a simple function that returns a const
|
||||
// string. Though any API call should be considered unsafe from leaf calls.
|
||||
Dart_VersionString();
|
||||
#if defined(DEBUG)
|
||||
// This will fail because it requires running in DARTSCOPE.
|
||||
return ExpectAbort(&CallDebugName);
|
||||
#else
|
||||
// The above will only crash in debug as ASSERTS are disabled in all other
|
||||
// build modes.
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif // defined(TARGET_OS_LINUX)
|
||||
|
||||
DART_EXPORT void IGH_MsanUnpoison(void* start, intptr_t length) {
|
||||
|
|
|
@ -62,7 +62,7 @@ DEFINE_NATIVE_ENTRY(Ffi_storePointer, 0, 3) {
|
|||
}
|
||||
|
||||
// Static invocations to this method are translated directly in streaming FGB.
|
||||
DEFINE_NATIVE_ENTRY(Ffi_asFunctionInternal, 2, 1) {
|
||||
DEFINE_NATIVE_ENTRY(Ffi_asFunctionInternal, 2, 2) {
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
|
|
|
@ -406,7 +406,7 @@ namespace dart {
|
|||
V(Ffi_storePointer, 3) \
|
||||
V(Ffi_address, 1) \
|
||||
V(Ffi_fromAddress, 1) \
|
||||
V(Ffi_asFunctionInternal, 1) \
|
||||
V(Ffi_asFunctionInternal, 2) \
|
||||
V(Ffi_nativeCallbackFunction, 2) \
|
||||
V(Ffi_pointerFromFunction, 1) \
|
||||
V(Ffi_dl_open, 1) \
|
||||
|
|
|
@ -6435,25 +6435,31 @@ Representation FfiCallInstr::RequiredInputRepresentation(intptr_t idx) const {
|
|||
|
||||
#define Z zone_
|
||||
|
||||
LocationSummary* FfiCallInstr::MakeLocationSummary(Zone* zone,
|
||||
bool is_optimizing) const {
|
||||
LocationSummary* FfiCallInstr::MakeLocationSummaryInternal(
|
||||
Zone* zone,
|
||||
bool is_optimizing,
|
||||
const Register temp) const {
|
||||
// The temporary register needs to be callee-saved and not an argument
|
||||
// register.
|
||||
ASSERT(((1 << CallingConventions::kFfiAnyNonAbiRegister) &
|
||||
CallingConventions::kArgumentRegisters) == 0);
|
||||
|
||||
constexpr intptr_t kNumTemps = 2;
|
||||
// TODO(dartbug.com/45468): Investigate whether we can avoid spilling
|
||||
// registers across ffi leaf calls by not using `kCall` here.
|
||||
LocationSummary* summary = new (zone) LocationSummary(
|
||||
zone, /*num_inputs=*/InputCount(),
|
||||
/*num_temps=*/temp == kNoRegister ? 2 : 3, LocationSummary::kCall);
|
||||
|
||||
LocationSummary* summary = new (zone)
|
||||
LocationSummary(zone, /*num_inputs=*/InputCount(),
|
||||
/*num_temps=*/kNumTemps, LocationSummary::kCall);
|
||||
|
||||
const Register temp0 = CallingConventions::kSecondNonArgumentRegister;
|
||||
const Register temp1 = CallingConventions::kFfiAnyNonAbiRegister;
|
||||
const Register temp0 = CallingConventions::kFfiAnyNonAbiRegister;
|
||||
const Register temp1 = CallingConventions::kSecondNonArgumentRegister;
|
||||
ASSERT(temp0 != temp1);
|
||||
summary->set_temp(0, Location::RegisterLocation(temp0));
|
||||
summary->set_temp(1, Location::RegisterLocation(temp1));
|
||||
|
||||
if (temp != kNoRegister) {
|
||||
summary->set_temp(2, Location::RegisterLocation(temp));
|
||||
}
|
||||
|
||||
summary->set_in(TargetAddressIndex(),
|
||||
Location::RegisterLocation(
|
||||
CallingConventions::kFirstNonArgumentRegister));
|
||||
|
@ -6476,14 +6482,13 @@ LocationSummary* FfiCallInstr::MakeLocationSummary(Zone* zone,
|
|||
return summary;
|
||||
}
|
||||
|
||||
void FfiCallInstr::EmitParamMoves(FlowGraphCompiler* compiler) {
|
||||
void FfiCallInstr::EmitParamMoves(FlowGraphCompiler* compiler,
|
||||
const Register saved_fp,
|
||||
const Register temp) {
|
||||
if (compiler::Assembler::EmittingComments()) {
|
||||
__ Comment("EmitParamMoves");
|
||||
}
|
||||
|
||||
const Register saved_fp = locs()->temp(0).reg();
|
||||
const Register temp = locs()->temp(1).reg();
|
||||
|
||||
// Moves for return pointer.
|
||||
const auto& return_location =
|
||||
marshaller_.Location(compiler::ffi::kResultIndex);
|
||||
|
@ -6591,7 +6596,9 @@ void FfiCallInstr::EmitParamMoves(FlowGraphCompiler* compiler) {
|
|||
}
|
||||
}
|
||||
|
||||
void FfiCallInstr::EmitReturnMoves(FlowGraphCompiler* compiler) {
|
||||
void FfiCallInstr::EmitReturnMoves(FlowGraphCompiler* compiler,
|
||||
const Register temp0,
|
||||
const Register temp1) {
|
||||
__ Comment("EmitReturnMoves");
|
||||
|
||||
const auto& returnLocation =
|
||||
|
@ -6611,16 +6618,17 @@ void FfiCallInstr::EmitReturnMoves(FlowGraphCompiler* compiler) {
|
|||
ASSERT(returnLocation.payload_type().IsCompound());
|
||||
ASSERT(marshaller_.PassTypedData());
|
||||
|
||||
const Register temp0 = TMP != kNoRegister ? TMP : locs()->temp(0).reg();
|
||||
const Register temp1 = locs()->temp(1).reg();
|
||||
ASSERT(temp0 != temp1);
|
||||
|
||||
// Get the typed data pointer which we have pinned to a stack slot.
|
||||
const Location typed_data_loc = locs()->in(TypedDataIndex());
|
||||
ASSERT(typed_data_loc.IsStackSlot());
|
||||
ASSERT(typed_data_loc.base_reg() == FPREG);
|
||||
__ LoadMemoryValue(temp0, FPREG, 0);
|
||||
__ LoadMemoryValue(temp0, temp0, typed_data_loc.ToStackSlotOffset());
|
||||
// If this is a leaf call there is no extra call frame to step through.
|
||||
if (is_leaf_) {
|
||||
__ LoadMemoryValue(temp0, FPREG, typed_data_loc.ToStackSlotOffset());
|
||||
} else {
|
||||
__ LoadMemoryValue(temp0, FPREG, 0);
|
||||
__ LoadMemoryValue(temp0, temp0, typed_data_loc.ToStackSlotOffset());
|
||||
}
|
||||
__ LoadField(
|
||||
temp0,
|
||||
compiler::FieldAddress(
|
||||
|
|
|
@ -5106,12 +5106,14 @@ class FfiCallInstr : public Definition {
|
|||
public:
|
||||
FfiCallInstr(Zone* zone,
|
||||
intptr_t deopt_id,
|
||||
const compiler::ffi::CallMarshaller& marshaller)
|
||||
const compiler::ffi::CallMarshaller& marshaller,
|
||||
bool is_leaf)
|
||||
: Definition(deopt_id),
|
||||
zone_(zone),
|
||||
marshaller_(marshaller),
|
||||
inputs_(marshaller.NumDefinitions() + 1 +
|
||||
(marshaller.PassTypedData() ? 1 : 0)) {
|
||||
(marshaller.PassTypedData() ? 1 : 0)),
|
||||
is_leaf_(is_leaf) {
|
||||
inputs_.FillWith(
|
||||
nullptr, 0,
|
||||
marshaller.NumDefinitions() + 1 + (marshaller.PassTypedData() ? 1 : 0));
|
||||
|
@ -5162,14 +5164,27 @@ class FfiCallInstr : public Definition {
|
|||
private:
|
||||
virtual void RawSetInputAt(intptr_t i, Value* value) { inputs_[i] = value; }
|
||||
|
||||
void EmitParamMoves(FlowGraphCompiler* compiler);
|
||||
void EmitReturnMoves(FlowGraphCompiler* compiler);
|
||||
LocationSummary* MakeLocationSummaryInternal(Zone* zone,
|
||||
bool is_optimizing,
|
||||
const Register temp) const;
|
||||
|
||||
// Clobbers both given registers.
|
||||
// `saved_fp` is used as the frame base to rebase off of.
|
||||
void EmitParamMoves(FlowGraphCompiler* compiler,
|
||||
const Register saved_fp,
|
||||
const Register temp);
|
||||
// Clobbers both given temp registers.
|
||||
void EmitReturnMoves(FlowGraphCompiler* compiler,
|
||||
const Register temp0,
|
||||
const Register temp1);
|
||||
|
||||
Zone* const zone_;
|
||||
const compiler::ffi::CallMarshaller& marshaller_;
|
||||
|
||||
GrowableArray<Value*> inputs_;
|
||||
|
||||
bool is_leaf_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(FfiCallInstr);
|
||||
};
|
||||
|
||||
|
|
|
@ -1402,85 +1402,114 @@ void NativeCallInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
|
|||
__ Drop(ArgumentCount()); // Drop the arguments.
|
||||
}
|
||||
|
||||
LocationSummary* FfiCallInstr::MakeLocationSummary(Zone* zone,
|
||||
bool is_optimizing) const {
|
||||
return MakeLocationSummaryInternal(zone, is_optimizing, R0);
|
||||
}
|
||||
|
||||
void FfiCallInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
|
||||
const Register saved_fp = locs()->temp(0).reg();
|
||||
const Register temp = locs()->temp(1).reg();
|
||||
// For regular calls, this holds the FP for rebasing the original locations
|
||||
// during EmitParamMoves.
|
||||
// For leaf calls, this holds the SP used to restore the pre-aligned SP after
|
||||
// the call.
|
||||
const Register saved_fp_or_sp = locs()->temp(0).reg();
|
||||
RELEASE_ASSERT((CallingConventions::kCalleeSaveCpuRegisters &
|
||||
(1 << saved_fp_or_sp)) != 0);
|
||||
const Register temp1 = locs()->temp(1).reg();
|
||||
const Register temp2 = locs()->temp(2).reg();
|
||||
const Register branch = locs()->in(TargetAddressIndex()).reg();
|
||||
|
||||
// Save frame pointer because we're going to update it when we enter the exit
|
||||
// frame.
|
||||
__ mov(saved_fp, compiler::Operand(FPREG));
|
||||
// Ensure these are callee-saved register and are preserved across the call.
|
||||
ASSERT((CallingConventions::kCalleeSaveCpuRegisters &
|
||||
(1 << saved_fp_or_sp)) != 0);
|
||||
// temp doesn't need to be preserved.
|
||||
|
||||
// Make a space to put the return address.
|
||||
__ PushImmediate(0);
|
||||
__ mov(saved_fp_or_sp,
|
||||
is_leaf_ ? compiler::Operand(SPREG) : compiler::Operand(FPREG));
|
||||
|
||||
// We need to create a dummy "exit frame". It will have a null code object.
|
||||
__ LoadObject(CODE_REG, Object::null_object());
|
||||
__ set_constant_pool_allowed(false);
|
||||
__ EnterDartFrame(0, /*load_pool_pointer=*/false);
|
||||
if (!is_leaf_) {
|
||||
// Make a space to put the return address.
|
||||
__ PushImmediate(0);
|
||||
|
||||
// Reserve space for arguments and align frame before entering C++ world.
|
||||
// We need to create a dummy "exit frame". It will have a null code object.
|
||||
__ LoadObject(CODE_REG, Object::null_object());
|
||||
__ set_constant_pool_allowed(false);
|
||||
__ EnterDartFrame(0, /*load_pool_pointer=*/false);
|
||||
}
|
||||
|
||||
// Reserve space for the arguments that go on the stack (if any), then align.
|
||||
__ ReserveAlignedFrameSpace(marshaller_.RequiredStackSpaceInBytes());
|
||||
|
||||
EmitParamMoves(compiler);
|
||||
EmitParamMoves(compiler, is_leaf_ ? FPREG : saved_fp_or_sp, temp1);
|
||||
|
||||
if (compiler::Assembler::EmittingComments()) {
|
||||
__ Comment("Call");
|
||||
__ Comment(is_leaf_ ? "Leaf Call" : "Call");
|
||||
}
|
||||
// We need to copy the return address up into the dummy stack frame so the
|
||||
// stack walker will know which safepoint to use.
|
||||
__ mov(TMP, compiler::Operand(PC));
|
||||
__ str(TMP, compiler::Address(FPREG, kSavedCallerPcSlotFromFp *
|
||||
compiler::target::kWordSize));
|
||||
|
||||
// For historical reasons, the PC on ARM points 8 bytes past the current
|
||||
// instruction. Therefore we emit the metadata here, 8 bytes (2 instructions)
|
||||
// after the original mov.
|
||||
compiler->EmitCallsiteMetadata(InstructionSource(), deopt_id(),
|
||||
UntaggedPcDescriptors::Kind::kOther, locs(),
|
||||
env());
|
||||
|
||||
// Update information in the thread object and enter a safepoint.
|
||||
if (CanExecuteGeneratedCodeInSafepoint()) {
|
||||
__ LoadImmediate(temp, compiler::target::Thread::exit_through_ffi());
|
||||
__ TransitionGeneratedToNative(branch, FPREG, temp, saved_fp,
|
||||
/*enter_safepoint=*/true);
|
||||
|
||||
if (is_leaf_) {
|
||||
__ blx(branch);
|
||||
|
||||
// Update information in the thread object and leave the safepoint.
|
||||
__ TransitionNativeToGenerated(saved_fp, temp, /*leave_safepoint=*/true);
|
||||
} else {
|
||||
// We cannot trust that this code will be executable within a safepoint.
|
||||
// Therefore we delegate the responsibility of entering/exiting the
|
||||
// safepoint to a stub which in the VM isolate's heap, which will never lose
|
||||
// execute permission.
|
||||
__ ldr(TMP,
|
||||
compiler::Address(
|
||||
THR, compiler::target::Thread::
|
||||
call_native_through_safepoint_entry_point_offset()));
|
||||
// We need to copy the return address up into the dummy stack frame so the
|
||||
// stack walker will know which safepoint to use.
|
||||
__ mov(temp1, compiler::Operand(PC));
|
||||
__ str(temp1, compiler::Address(FPREG, kSavedCallerPcSlotFromFp *
|
||||
compiler::target::kWordSize));
|
||||
|
||||
// Calls R8 in a safepoint and clobbers R4 and NOTFP.
|
||||
ASSERT(branch == R8 && temp == R4);
|
||||
static_assert((kReservedCpuRegisters & (1 << NOTFP)) != 0,
|
||||
"NOTFP should be a reserved register");
|
||||
__ blx(TMP);
|
||||
// For historical reasons, the PC on ARM points 8 bytes past the current
|
||||
// instruction. Therefore we emit the metadata here, 8 bytes
|
||||
// (2 instructions) after the original mov.
|
||||
compiler->EmitCallsiteMetadata(InstructionSource(), deopt_id(),
|
||||
UntaggedPcDescriptors::Kind::kOther, locs(),
|
||||
env());
|
||||
|
||||
// Update information in the thread object and enter a safepoint.
|
||||
if (CanExecuteGeneratedCodeInSafepoint()) {
|
||||
__ LoadImmediate(temp1, compiler::target::Thread::exit_through_ffi());
|
||||
__ TransitionGeneratedToNative(branch, FPREG, temp1, saved_fp_or_sp,
|
||||
/*enter_safepoint=*/true);
|
||||
|
||||
__ blx(branch);
|
||||
|
||||
// Update information in the thread object and leave the safepoint.
|
||||
__ TransitionNativeToGenerated(saved_fp_or_sp, temp1,
|
||||
/*leave_safepoint=*/true);
|
||||
} else {
|
||||
// We cannot trust that this code will be executable within a safepoint.
|
||||
// Therefore we delegate the responsibility of entering/exiting the
|
||||
// safepoint to a stub which in the VM isolate's heap, which will never
|
||||
// lose execute permission.
|
||||
__ ldr(temp1,
|
||||
compiler::Address(
|
||||
THR, compiler::target::Thread::
|
||||
call_native_through_safepoint_entry_point_offset()));
|
||||
|
||||
// Calls R8 in a safepoint and clobbers R4 and NOTFP.
|
||||
ASSERT(branch == R8);
|
||||
static_assert((kReservedCpuRegisters & (1 << NOTFP)) != 0,
|
||||
"NOTFP should be a reserved register");
|
||||
__ blx(temp1);
|
||||
}
|
||||
|
||||
// Restore the global object pool after returning from runtime (old space is
|
||||
// moving, so the GOP could have been relocated).
|
||||
if (FLAG_precompiled_mode && FLAG_use_bare_instructions) {
|
||||
__ SetupGlobalPoolAndDispatchTable();
|
||||
}
|
||||
}
|
||||
|
||||
// Restore the global object pool after returning from runtime (old space is
|
||||
// moving, so the GOP could have been relocated).
|
||||
if (FLAG_precompiled_mode && FLAG_use_bare_instructions) {
|
||||
__ SetupGlobalPoolAndDispatchTable();
|
||||
EmitReturnMoves(compiler, temp1, temp2);
|
||||
|
||||
if (is_leaf_) {
|
||||
// Restore the pre-aligned SP.
|
||||
__ mov(SPREG, compiler::Operand(saved_fp_or_sp));
|
||||
} else {
|
||||
// Leave dummy exit frame.
|
||||
__ LeaveDartFrame();
|
||||
__ set_constant_pool_allowed(true);
|
||||
|
||||
// Instead of returning to the "fake" return address, we just pop it.
|
||||
__ PopRegister(temp1);
|
||||
}
|
||||
|
||||
EmitReturnMoves(compiler);
|
||||
|
||||
// Leave dummy exit frame.
|
||||
__ LeaveDartFrame();
|
||||
__ set_constant_pool_allowed(true);
|
||||
|
||||
// Instead of returning to the "fake" return address, we just pop it.
|
||||
__ PopRegister(TMP);
|
||||
}
|
||||
|
||||
// Keep in sync with NativeEntryInstr::EmitNativeCode.
|
||||
|
|
|
@ -1228,46 +1228,48 @@ void NativeCallInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
|
|||
__ Drop(ArgumentCount()); // Drop the arguments.
|
||||
}
|
||||
|
||||
LocationSummary* FfiCallInstr::MakeLocationSummary(Zone* zone,
|
||||
bool is_optimizing) const {
|
||||
return MakeLocationSummaryInternal(zone, is_optimizing, R11);
|
||||
}
|
||||
|
||||
void FfiCallInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
|
||||
const Register saved_fp = locs()->temp(0).reg();
|
||||
const Register temp = locs()->temp(1).reg();
|
||||
// For regular calls, this holds the FP for rebasing the original locations
|
||||
// during EmitParamMoves.
|
||||
// For leaf calls, this holds the SP used to restore the pre-aligned SP after
|
||||
// the call.
|
||||
const Register saved_fp_or_sp = locs()->temp(0).reg();
|
||||
RELEASE_ASSERT((CallingConventions::kCalleeSaveCpuRegisters &
|
||||
(1 << saved_fp_or_sp)) != 0);
|
||||
const Register temp1 = locs()->temp(1).reg();
|
||||
const Register temp2 = locs()->temp(2).reg();
|
||||
const Register branch = locs()->in(TargetAddressIndex()).reg();
|
||||
|
||||
// Save frame pointer because we're going to update it when we enter the exit
|
||||
// frame.
|
||||
__ mov(saved_fp, FPREG);
|
||||
// Ensure these are callee-saved register and are preserved across the call.
|
||||
ASSERT((CallingConventions::kCalleeSaveCpuRegisters &
|
||||
(1 << saved_fp_or_sp)) != 0);
|
||||
// temps don't need to be preserved.
|
||||
|
||||
// We need to create a dummy "exit frame". It will share the same pool pointer
|
||||
// but have a null code object.
|
||||
__ LoadObject(CODE_REG, Object::null_object());
|
||||
__ set_constant_pool_allowed(false);
|
||||
__ EnterDartFrame(0, PP);
|
||||
__ mov(saved_fp_or_sp, is_leaf_ ? SPREG : FPREG);
|
||||
|
||||
// Make space for arguments and align the frame.
|
||||
if (!is_leaf_) {
|
||||
// We need to create a dummy "exit frame". It will share the same pool
|
||||
// pointer but have a null code object.
|
||||
__ LoadObject(CODE_REG, Object::null_object());
|
||||
__ set_constant_pool_allowed(false);
|
||||
__ EnterDartFrame(0, PP);
|
||||
}
|
||||
|
||||
// Reserve space for the arguments that go on the stack (if any), then align.
|
||||
__ ReserveAlignedFrameSpace(marshaller_.RequiredStackSpaceInBytes());
|
||||
|
||||
EmitParamMoves(compiler);
|
||||
EmitParamMoves(compiler, is_leaf_ ? FPREG : saved_fp_or_sp, temp1);
|
||||
|
||||
if (compiler::Assembler::EmittingComments()) {
|
||||
__ Comment("Call");
|
||||
__ Comment(is_leaf_ ? "Leaf Call" : "Call");
|
||||
}
|
||||
// We need to copy a dummy return address up into the dummy stack frame so the
|
||||
// stack walker will know which safepoint to use.
|
||||
//
|
||||
// ADR loads relative to itself, so add kInstrSize to point to the next
|
||||
// instruction.
|
||||
__ adr(temp, compiler::Immediate(Instr::kInstrSize));
|
||||
compiler->EmitCallsiteMetadata(
|
||||
source(), deopt_id(), UntaggedPcDescriptors::Kind::kOther, locs(), env());
|
||||
|
||||
__ StoreToOffset(temp, FPREG, kSavedCallerPcSlotFromFp * kWordSize);
|
||||
|
||||
if (CanExecuteGeneratedCodeInSafepoint()) {
|
||||
// Update information in the thread object and enter a safepoint.
|
||||
__ LoadImmediate(temp, compiler::target::Thread::exit_through_ffi());
|
||||
__ TransitionGeneratedToNative(branch, FPREG, temp,
|
||||
/*enter_safepoint=*/true);
|
||||
|
||||
if (is_leaf_) {
|
||||
// We are entering runtime code, so the C stack pointer must be restored
|
||||
// from the stack limit to the top of the stack.
|
||||
__ mov(R25, CSP);
|
||||
|
@ -1278,39 +1280,75 @@ void FfiCallInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
|
|||
// Restore the Dart stack pointer.
|
||||
__ mov(SP, CSP);
|
||||
__ mov(CSP, R25);
|
||||
|
||||
// Update information in the thread object and leave the safepoint.
|
||||
__ TransitionNativeToGenerated(temp, /*leave_safepoint=*/true);
|
||||
} else {
|
||||
// We cannot trust that this code will be executable within a safepoint.
|
||||
// Therefore we delegate the responsibility of entering/exiting the
|
||||
// safepoint to a stub which in the VM isolate's heap, which will never lose
|
||||
// execute permission.
|
||||
__ ldr(TMP,
|
||||
compiler::Address(
|
||||
THR, compiler::target::Thread::
|
||||
call_native_through_safepoint_entry_point_offset()));
|
||||
// We need to copy a dummy return address up into the dummy stack frame so
|
||||
// the stack walker will know which safepoint to use.
|
||||
//
|
||||
// ADR loads relative to itself, so add kInstrSize to point to the next
|
||||
// instruction.
|
||||
__ adr(temp1, compiler::Immediate(Instr::kInstrSize));
|
||||
compiler->EmitCallsiteMetadata(source(), deopt_id(),
|
||||
UntaggedPcDescriptors::Kind::kOther, locs(),
|
||||
env());
|
||||
|
||||
// Calls R9 and clobbers R19 (along with volatile registers).
|
||||
ASSERT(branch == R9 && temp == R19);
|
||||
__ blr(TMP);
|
||||
__ StoreToOffset(temp1, FPREG, kSavedCallerPcSlotFromFp * kWordSize);
|
||||
|
||||
if (CanExecuteGeneratedCodeInSafepoint()) {
|
||||
// Update information in the thread object and enter a safepoint.
|
||||
__ LoadImmediate(temp1, compiler::target::Thread::exit_through_ffi());
|
||||
__ TransitionGeneratedToNative(branch, FPREG, temp1,
|
||||
/*enter_safepoint=*/true);
|
||||
|
||||
// We are entering runtime code, so the C stack pointer must be restored
|
||||
// from the stack limit to the top of the stack.
|
||||
__ mov(R25, CSP);
|
||||
__ mov(CSP, SP);
|
||||
|
||||
__ blr(branch);
|
||||
|
||||
// Restore the Dart stack pointer.
|
||||
__ mov(SP, CSP);
|
||||
__ mov(CSP, R25);
|
||||
|
||||
// Update information in the thread object and leave the safepoint.
|
||||
__ TransitionNativeToGenerated(temp1, /*leave_safepoint=*/true);
|
||||
} else {
|
||||
// We cannot trust that this code will be executable within a safepoint.
|
||||
// Therefore we delegate the responsibility of entering/exiting the
|
||||
// safepoint to a stub which in the VM isolate's heap, which will never
|
||||
// lose execute permission.
|
||||
__ ldr(temp1,
|
||||
compiler::Address(
|
||||
THR, compiler::target::Thread::
|
||||
call_native_through_safepoint_entry_point_offset()));
|
||||
|
||||
// Calls R9 and clobbers R19 (along with volatile registers).
|
||||
ASSERT(branch == R9);
|
||||
__ blr(temp1);
|
||||
}
|
||||
|
||||
// Refresh pinned registers values (inc. write barrier mask and null
|
||||
// object).
|
||||
__ RestorePinnedRegisters();
|
||||
}
|
||||
|
||||
// Refresh pinned registers values (inc. write barrier mask and null object).
|
||||
__ RestorePinnedRegisters();
|
||||
EmitReturnMoves(compiler, temp1, temp2);
|
||||
|
||||
EmitReturnMoves(compiler);
|
||||
if (is_leaf_) {
|
||||
// Restore the pre-aligned SP.
|
||||
__ mov(SPREG, saved_fp_or_sp);
|
||||
} else {
|
||||
// Although PP is a callee-saved register, it may have been moved by the GC.
|
||||
__ LeaveDartFrame(compiler::kRestoreCallerPP);
|
||||
|
||||
// Although PP is a callee-saved register, it may have been moved by the GC.
|
||||
__ LeaveDartFrame(compiler::kRestoreCallerPP);
|
||||
// Restore the global object pool after returning from runtime (old space is
|
||||
// moving, so the GOP could have been relocated).
|
||||
if (FLAG_precompiled_mode && FLAG_use_bare_instructions) {
|
||||
__ SetupGlobalPoolAndDispatchTable();
|
||||
}
|
||||
|
||||
// Restore the global object pool after returning from runtime (old space is
|
||||
// moving, so the GOP could have been relocated).
|
||||
if (FLAG_precompiled_mode && FLAG_use_bare_instructions) {
|
||||
__ SetupGlobalPoolAndDispatchTable();
|
||||
__ set_constant_pool_allowed(true);
|
||||
}
|
||||
|
||||
__ set_constant_pool_allowed(true);
|
||||
}
|
||||
|
||||
// Keep in sync with NativeEntryInstr::EmitNativeCode.
|
||||
|
|
|
@ -999,57 +999,88 @@ void NativeCallInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
|
|||
__ Drop(ArgumentCount()); // Drop the arguments.
|
||||
}
|
||||
|
||||
LocationSummary* FfiCallInstr::MakeLocationSummary(Zone* zone,
|
||||
bool is_optimizing) const {
|
||||
return MakeLocationSummaryInternal(zone, is_optimizing, kNoRegister);
|
||||
}
|
||||
|
||||
void FfiCallInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
|
||||
const Register saved_fp = locs()->temp(0).reg();
|
||||
// For regular calls, this holds the FP for rebasing the original locations
|
||||
// during EmitParamMoves.
|
||||
// For leaf calls, this holds the SP used to restore the pre-aligned SP after
|
||||
// the call.
|
||||
const Register saved_fp_or_sp = locs()->temp(0).reg();
|
||||
const Register temp = locs()->temp(1).reg();
|
||||
const Register branch = locs()->in(TargetAddressIndex()).reg();
|
||||
|
||||
// Save frame pointer because we're going to update it when we enter the exit
|
||||
// frame.
|
||||
__ movl(saved_fp, FPREG);
|
||||
// Ensure these are callee-saved register and are preserved across the call.
|
||||
ASSERT((CallingConventions::kCalleeSaveCpuRegisters &
|
||||
(1 << saved_fp_or_sp)) != 0);
|
||||
// temp doesn't need to be preserved.
|
||||
|
||||
// Make a space to put the return address.
|
||||
__ pushl(compiler::Immediate(0));
|
||||
__ movl(saved_fp_or_sp, is_leaf_ ? SPREG : FPREG);
|
||||
|
||||
// We need to create a dummy "exit frame". It will have a null code object.
|
||||
__ LoadObject(CODE_REG, Object::null_object());
|
||||
__ EnterDartFrame(marshaller_.RequiredStackSpaceInBytes());
|
||||
intptr_t stack_required = marshaller_.RequiredStackSpaceInBytes();
|
||||
|
||||
// Align frame before entering C++ world.
|
||||
if (OS::ActivationFrameAlignment() > 1) {
|
||||
__ andl(SPREG, compiler::Immediate(~(OS::ActivationFrameAlignment() - 1)));
|
||||
if (is_leaf_) {
|
||||
// For leaf calls we need to leave space at the bottom for the pre-align SP.
|
||||
stack_required += compiler::target::kWordSize;
|
||||
} else {
|
||||
// Make a space to put the return address.
|
||||
__ pushl(compiler::Immediate(0));
|
||||
|
||||
// We need to create a dummy "exit frame". It will have a null code object.
|
||||
__ LoadObject(CODE_REG, Object::null_object());
|
||||
__ EnterDartFrame(0);
|
||||
}
|
||||
|
||||
EmitParamMoves(compiler);
|
||||
// Reserve space for the arguments that go on the stack (if any), then align.
|
||||
__ ReserveAlignedFrameSpace(stack_required);
|
||||
|
||||
EmitParamMoves(compiler, is_leaf_ ? FPREG : saved_fp_or_sp, temp);
|
||||
|
||||
if (is_leaf_) {
|
||||
// We store the pre-align SP at a fixed offset from the final SP.
|
||||
// Pushing before alignment would mean its placement would vary with how
|
||||
// much the frame was unaligned.
|
||||
__ movl(compiler::Address(SPREG, marshaller_.RequiredStackSpaceInBytes()),
|
||||
saved_fp_or_sp);
|
||||
}
|
||||
|
||||
if (compiler::Assembler::EmittingComments()) {
|
||||
__ Comment("Call");
|
||||
__ Comment(is_leaf_ ? "Leaf Call" : "Call");
|
||||
}
|
||||
// We need to copy a dummy return address up into the dummy stack frame so the
|
||||
// stack walker will know which safepoint to use. Unlike X64, there's no
|
||||
// PC-relative 'leaq' available, so we have do a trick with 'call'.
|
||||
compiler::Label get_pc;
|
||||
__ call(&get_pc);
|
||||
compiler->EmitCallsiteMetadata(InstructionSource(), deopt_id(),
|
||||
UntaggedPcDescriptors::Kind::kOther, locs(),
|
||||
env());
|
||||
__ Bind(&get_pc);
|
||||
__ popl(temp);
|
||||
__ movl(compiler::Address(FPREG, kSavedCallerPcSlotFromFp * kWordSize), temp);
|
||||
|
||||
ASSERT(!CanExecuteGeneratedCodeInSafepoint());
|
||||
// We cannot trust that this code will be executable within a safepoint.
|
||||
// Therefore we delegate the responsibility of entering/exiting the
|
||||
// safepoint to a stub which in the VM isolate's heap, which will never lose
|
||||
// execute permission.
|
||||
__ movl(temp,
|
||||
compiler::Address(
|
||||
THR, compiler::target::Thread::
|
||||
call_native_through_safepoint_entry_point_offset()));
|
||||
if (is_leaf_) {
|
||||
__ call(branch);
|
||||
} else {
|
||||
// We need to copy a dummy return address up into the dummy stack frame so
|
||||
// the stack walker will know which safepoint to use. Unlike X64, there's no
|
||||
// PC-relative 'leaq' available, so we have do a trick with 'call'.
|
||||
compiler::Label get_pc;
|
||||
__ call(&get_pc);
|
||||
compiler->EmitCallsiteMetadata(InstructionSource(), deopt_id(),
|
||||
UntaggedPcDescriptors::Kind::kOther, locs(),
|
||||
env());
|
||||
__ Bind(&get_pc);
|
||||
__ popl(temp);
|
||||
__ movl(compiler::Address(FPREG, kSavedCallerPcSlotFromFp * kWordSize),
|
||||
temp);
|
||||
|
||||
// Calls EAX within a safepoint and clobbers EBX.
|
||||
ASSERT(temp == EBX && branch == EAX);
|
||||
__ call(temp);
|
||||
ASSERT(!CanExecuteGeneratedCodeInSafepoint());
|
||||
// We cannot trust that this code will be executable within a safepoint.
|
||||
// Therefore we delegate the responsibility of entering/exiting the
|
||||
// safepoint to a stub which in the VM isolate's heap, which will never lose
|
||||
// execute permission.
|
||||
__ movl(temp,
|
||||
compiler::Address(
|
||||
THR, compiler::target::Thread::
|
||||
call_native_through_safepoint_entry_point_offset()));
|
||||
|
||||
// Calls EAX within a safepoint and clobbers EBX.
|
||||
ASSERT(branch == EAX);
|
||||
__ call(temp);
|
||||
}
|
||||
|
||||
// Restore the stack when a struct by value is returned into memory pointed
|
||||
// to by a pointer that is passed into the function.
|
||||
|
@ -1061,10 +1092,10 @@ void FfiCallInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
|
|||
__ subl(SPREG, compiler::Immediate(compiler::target::kWordSize));
|
||||
}
|
||||
|
||||
// The x86 calling convention requires floating point values to be returned on
|
||||
// the "floating-point stack" (aka. register ST0). We don't use the
|
||||
// floating-point stack in Dart, so we need to move the return value back into
|
||||
// an XMM register.
|
||||
// The x86 calling convention requires floating point values to be returned
|
||||
// on the "floating-point stack" (aka. register ST0). We don't use the
|
||||
// floating-point stack in Dart, so we need to move the return value back
|
||||
// into an XMM register.
|
||||
if (representation() == kUnboxedDouble) {
|
||||
__ fstpl(compiler::Address(SPREG, -kDoubleSize));
|
||||
__ movsd(XMM0, compiler::Address(SPREG, -kDoubleSize));
|
||||
|
@ -1073,13 +1104,20 @@ void FfiCallInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
|
|||
__ movss(XMM0, compiler::Address(SPREG, -kFloatSize));
|
||||
}
|
||||
|
||||
EmitReturnMoves(compiler);
|
||||
// Pass both registers for use as clobbered temp registers.
|
||||
EmitReturnMoves(compiler, saved_fp_or_sp, temp);
|
||||
|
||||
// Leave dummy exit frame.
|
||||
__ LeaveFrame();
|
||||
if (is_leaf_) {
|
||||
// Restore pre-align SP. Was stored right before the first stack argument.
|
||||
__ movl(SPREG,
|
||||
compiler::Address(SPREG, marshaller_.RequiredStackSpaceInBytes()));
|
||||
} else {
|
||||
// Leave dummy exit frame.
|
||||
__ LeaveFrame();
|
||||
|
||||
// Instead of returning to the "fake" return address, we just pop it.
|
||||
__ popl(temp);
|
||||
// Instead of returning to the "fake" return address, we just pop it.
|
||||
__ popl(temp);
|
||||
}
|
||||
}
|
||||
|
||||
// Keep in sync with NativeReturnInstr::EmitNativeCode.
|
||||
|
|
|
@ -1188,82 +1188,118 @@ void NativeCallInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
|
|||
__ Drop(ArgumentCount()); // Drop the arguments.
|
||||
}
|
||||
|
||||
LocationSummary* FfiCallInstr::MakeLocationSummary(Zone* zone,
|
||||
bool is_optimizing) const {
|
||||
// Use R10 as a temp register. We can't use RDI, RSI, RDX, R8, R9 as they are
|
||||
// arg registers, and R11 is TMP.
|
||||
return MakeLocationSummaryInternal(zone, is_optimizing, R10);
|
||||
}
|
||||
|
||||
void FfiCallInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
|
||||
const Register saved_fp = locs()->temp(0).reg();
|
||||
// For leaf calls, this holds the SP used to restore the pre-aligned SP after
|
||||
// the call.
|
||||
// Note: R12 doubles as CODE_REG, which gets clobbered during frame setup in
|
||||
// regular calls.
|
||||
const Register saved_sp = locs()->temp(0).reg();
|
||||
// For regular calls, this holds the FP for rebasing the original locations
|
||||
// during EmitParamMoves.
|
||||
const Register saved_fp = locs()->temp(1).reg();
|
||||
const Register temp = locs()->temp(2).reg();
|
||||
const Register target_address = locs()->in(TargetAddressIndex()).reg();
|
||||
|
||||
// Save frame pointer because we're going to update it when we enter the exit
|
||||
// frame.
|
||||
__ movq(saved_fp, FPREG);
|
||||
// Ensure these are callee-saved register and are preserved across the call.
|
||||
ASSERT((CallingConventions::kCalleeSaveCpuRegisters & (1 << saved_sp)) != 0);
|
||||
ASSERT((CallingConventions::kCalleeSaveCpuRegisters & (1 << saved_fp)) != 0);
|
||||
// temp doesn't need to be preserved.
|
||||
|
||||
// Make a space to put the return address.
|
||||
__ pushq(compiler::Immediate(0));
|
||||
|
||||
// We need to create a dummy "exit frame". It will share the same pool pointer
|
||||
// but have a null code object.
|
||||
__ LoadObject(CODE_REG, Object::null_object());
|
||||
__ set_constant_pool_allowed(false);
|
||||
__ EnterDartFrame(marshaller_.RequiredStackSpaceInBytes(), PP);
|
||||
|
||||
// Align frame before entering C++ world.
|
||||
if (OS::ActivationFrameAlignment() > 1) {
|
||||
__ andq(SPREG, compiler::Immediate(~(OS::ActivationFrameAlignment() - 1)));
|
||||
}
|
||||
|
||||
EmitParamMoves(compiler);
|
||||
|
||||
// We need to copy a dummy return address up into the dummy stack frame so the
|
||||
// stack walker will know which safepoint to use. RIP points to the *next*
|
||||
// instruction, so 'AddressRIPRelative' loads the address of the following
|
||||
// 'movq'.
|
||||
__ leaq(TMP, compiler::Address::AddressRIPRelative(0));
|
||||
compiler->EmitCallsiteMetadata(InstructionSource(), deopt_id(),
|
||||
UntaggedPcDescriptors::Kind::kOther, locs(),
|
||||
env());
|
||||
__ movq(compiler::Address(FPREG, kSavedCallerPcSlotFromFp * kWordSize), TMP);
|
||||
|
||||
if (CanExecuteGeneratedCodeInSafepoint()) {
|
||||
// Update information in the thread object and enter a safepoint.
|
||||
__ movq(TMP,
|
||||
compiler::Immediate(compiler::target::Thread::exit_through_ffi()));
|
||||
__ TransitionGeneratedToNative(target_address, FPREG, TMP,
|
||||
/*enter_safepoint=*/true);
|
||||
|
||||
__ CallCFunction(target_address, /*restore_rsp=*/true);
|
||||
|
||||
// Update information in the thread object and leave the safepoint.
|
||||
__ TransitionNativeToGenerated(/*leave_safepoint=*/true);
|
||||
if (is_leaf_) {
|
||||
__ movq(saved_sp, SPREG);
|
||||
} else {
|
||||
// We cannot trust that this code will be executable within a safepoint.
|
||||
// Therefore we delegate the responsibility of entering/exiting the
|
||||
// safepoint to a stub which in the VM isolate's heap, which will never lose
|
||||
// execute permission.
|
||||
__ movq(TMP,
|
||||
compiler::Address(
|
||||
THR, compiler::target::Thread::
|
||||
call_native_through_safepoint_entry_point_offset()));
|
||||
__ movq(saved_fp, FPREG);
|
||||
// Make a space to put the return address.
|
||||
__ pushq(compiler::Immediate(0));
|
||||
|
||||
// Calls RBX within a safepoint.
|
||||
ASSERT(saved_fp == RBX);
|
||||
__ movq(RBX, target_address);
|
||||
__ call(TMP);
|
||||
// We need to create a dummy "exit frame". It will share the same pool
|
||||
// pointer but have a null code object.
|
||||
__ LoadObject(CODE_REG, Code::null_object());
|
||||
__ set_constant_pool_allowed(false);
|
||||
__ EnterDartFrame(0, PP);
|
||||
}
|
||||
|
||||
EmitReturnMoves(compiler);
|
||||
// Reserve space for the arguments that go on the stack (if any), then align.
|
||||
__ ReserveAlignedFrameSpace(marshaller_.RequiredStackSpaceInBytes());
|
||||
|
||||
// Although PP is a callee-saved register, it may have been moved by the GC.
|
||||
__ LeaveDartFrame(compiler::kRestoreCallerPP);
|
||||
|
||||
// Restore the global object pool after returning from runtime (old space is
|
||||
// moving, so the GOP could have been relocated).
|
||||
if (FLAG_precompiled_mode && FLAG_use_bare_instructions) {
|
||||
__ movq(PP, compiler::Address(THR, Thread::global_object_pool_offset()));
|
||||
if (is_leaf_) {
|
||||
EmitParamMoves(compiler, FPREG, saved_fp);
|
||||
} else {
|
||||
EmitParamMoves(compiler, saved_fp, saved_sp);
|
||||
}
|
||||
|
||||
__ set_constant_pool_allowed(true);
|
||||
if (compiler::Assembler::EmittingComments()) {
|
||||
__ Comment(is_leaf_ ? "Leaf Call" : "Call");
|
||||
}
|
||||
|
||||
// Instead of returning to the "fake" return address, we just pop it.
|
||||
__ popq(TMP);
|
||||
if (is_leaf_) {
|
||||
__ CallCFunction(target_address, /*restore_rsp=*/true);
|
||||
} else {
|
||||
// We need to copy a dummy return address up into the dummy stack frame so
|
||||
// the stack walker will know which safepoint to use. RIP points to the
|
||||
// *next* instruction, so 'AddressRIPRelative' loads the address of the
|
||||
// following 'movq'.
|
||||
__ leaq(temp, compiler::Address::AddressRIPRelative(0));
|
||||
compiler->EmitCallsiteMetadata(InstructionSource(), deopt_id(),
|
||||
UntaggedPcDescriptors::Kind::kOther, locs(),
|
||||
env());
|
||||
__ movq(compiler::Address(FPREG, kSavedCallerPcSlotFromFp * kWordSize),
|
||||
temp);
|
||||
|
||||
if (CanExecuteGeneratedCodeInSafepoint()) {
|
||||
// Update information in the thread object and enter a safepoint.
|
||||
__ movq(temp, compiler::Immediate(
|
||||
compiler::target::Thread::exit_through_ffi()));
|
||||
|
||||
__ TransitionGeneratedToNative(target_address, FPREG, temp,
|
||||
/*enter_safepoint=*/true);
|
||||
|
||||
__ CallCFunction(target_address, /*restore_rsp=*/true);
|
||||
|
||||
// Update information in the thread object and leave the safepoint.
|
||||
__ TransitionNativeToGenerated(/*leave_safepoint=*/true);
|
||||
} else {
|
||||
// We cannot trust that this code will be executable within a safepoint.
|
||||
// Therefore we delegate the responsibility of entering/exiting the
|
||||
// safepoint to a stub which is in the VM isolate's heap, which will never
|
||||
// lose execute permission.
|
||||
__ movq(temp,
|
||||
compiler::Address(
|
||||
THR, compiler::target::Thread::
|
||||
call_native_through_safepoint_entry_point_offset()));
|
||||
|
||||
// Calls RBX within a safepoint. RBX and R12 are clobbered.
|
||||
__ movq(RBX, target_address);
|
||||
__ call(temp);
|
||||
}
|
||||
}
|
||||
|
||||
// Pass the `saved_fp` reg. as a temp to clobber since we're done with it.
|
||||
EmitReturnMoves(compiler, temp, saved_fp);
|
||||
|
||||
if (is_leaf_) {
|
||||
// Restore the pre-aligned SP.
|
||||
__ movq(SPREG, saved_sp);
|
||||
} else {
|
||||
// Although PP is a callee-saved register, it may have been moved by the GC.
|
||||
__ LeaveDartFrame(compiler::kRestoreCallerPP);
|
||||
// Restore the global object pool after returning from runtime (old space is
|
||||
// moving, so the GOP could have been relocated).
|
||||
if (FLAG_precompiled_mode && FLAG_use_bare_instructions) {
|
||||
__ movq(PP, compiler::Address(THR, Thread::global_object_pool_offset()));
|
||||
}
|
||||
__ set_constant_pool_allowed(true);
|
||||
|
||||
// Instead of returning to the "fake" return address, we just pop it.
|
||||
__ popq(temp);
|
||||
}
|
||||
}
|
||||
|
||||
// Keep in sync with NativeReturnInstr::EmitNativeCode.
|
||||
|
|
|
@ -15,7 +15,8 @@ namespace ffi {
|
|||
|
||||
// TODO(dartbug.com/36607): Cache the trampolines.
|
||||
FunctionPtr TrampolineFunction(const FunctionType& dart_signature,
|
||||
const FunctionType& c_signature) {
|
||||
const FunctionType& c_signature,
|
||||
bool is_leaf) {
|
||||
Thread* thread = Thread::Current();
|
||||
Zone* zone = thread->zone();
|
||||
String& name = String::Handle(zone, Symbols::New(thread, "FfiTrampoline"));
|
||||
|
@ -54,6 +55,8 @@ FunctionPtr TrampolineFunction(const FunctionType& dart_signature,
|
|||
signature ^= ClassFinalizer::FinalizeType(signature);
|
||||
function.set_signature(signature);
|
||||
|
||||
function.SetFfiIsLeaf(is_leaf);
|
||||
|
||||
return function.ptr();
|
||||
}
|
||||
|
||||
|
|
|
@ -20,7 +20,8 @@ namespace compiler {
|
|||
namespace ffi {
|
||||
|
||||
FunctionPtr TrampolineFunction(const FunctionType& dart_signature,
|
||||
const FunctionType& c_signature);
|
||||
const FunctionType& c_signature,
|
||||
bool is_leaf);
|
||||
|
||||
} // namespace ffi
|
||||
|
||||
|
|
|
@ -980,7 +980,8 @@ Fragment BaseFlowGraphBuilder::Box(Representation from) {
|
|||
}
|
||||
|
||||
Fragment BaseFlowGraphBuilder::BuildFfiAsFunctionInternalCall(
|
||||
const TypeArguments& signatures) {
|
||||
const TypeArguments& signatures,
|
||||
bool is_leaf) {
|
||||
ASSERT(signatures.IsInstantiated());
|
||||
ASSERT(signatures.Length() == 2);
|
||||
|
||||
|
@ -990,7 +991,8 @@ Fragment BaseFlowGraphBuilder::BuildFfiAsFunctionInternalCall(
|
|||
ASSERT(dart_type.IsFunctionType() && native_type.IsFunctionType());
|
||||
const Function& target =
|
||||
Function::ZoneHandle(compiler::ffi::TrampolineFunction(
|
||||
FunctionType::Cast(dart_type), FunctionType::Cast(native_type)));
|
||||
FunctionType::Cast(dart_type), FunctionType::Cast(native_type),
|
||||
is_leaf));
|
||||
|
||||
Fragment code;
|
||||
// Store the pointer in the context, we cannot load the untagged address
|
||||
|
|
|
@ -362,7 +362,8 @@ class BaseFlowGraphBuilder {
|
|||
// Builds the graph for an invocation of '_asFunctionInternal'.
|
||||
//
|
||||
// 'signatures' contains the pair [<dart signature>, <native signature>].
|
||||
Fragment BuildFfiAsFunctionInternalCall(const TypeArguments& signatures);
|
||||
Fragment BuildFfiAsFunctionInternalCall(const TypeArguments& signatures,
|
||||
bool is_leaf);
|
||||
|
||||
Fragment AllocateObject(TokenPosition position,
|
||||
const Class& klass,
|
||||
|
|
|
@ -5667,20 +5667,32 @@ Fragment StreamingFlowGraphBuilder::BuildNativeEffect() {
|
|||
|
||||
Fragment StreamingFlowGraphBuilder::BuildFfiAsFunctionInternal() {
|
||||
const intptr_t argc = ReadUInt(); // Read argument count.
|
||||
ASSERT(argc == 1); // Pointer.
|
||||
ASSERT(argc == 2); // Pointer, isLeaf.
|
||||
const intptr_t list_length = ReadListLength(); // Read types list length.
|
||||
ASSERT(list_length == 2); // Dart signature, then native signature.
|
||||
const TypeArguments& type_arguments =
|
||||
T.BuildTypeArguments(list_length); // Read types.
|
||||
ASSERT(list_length == 2); // Dart signature, then native signature
|
||||
// Read types.
|
||||
const TypeArguments& type_arguments = T.BuildTypeArguments(list_length);
|
||||
Fragment code;
|
||||
const intptr_t positional_count =
|
||||
ReadListLength(); // Read positional argument count.
|
||||
ASSERT(positional_count == 1);
|
||||
// Read positional argument count.
|
||||
const intptr_t positional_count = ReadListLength();
|
||||
ASSERT(positional_count == 2);
|
||||
code += BuildExpression(); // Build first positional argument (pointer).
|
||||
const intptr_t named_args_len =
|
||||
ReadListLength(); // Skip empty named arguments list.
|
||||
|
||||
// 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();
|
||||
|
||||
// Skip (empty) named arguments list.
|
||||
const intptr_t named_args_len = ReadListLength();
|
||||
ASSERT(named_args_len == 0);
|
||||
code += B->BuildFfiAsFunctionInternalCall(type_arguments);
|
||||
|
||||
code += B->BuildFfiAsFunctionInternalCall(type_arguments, is_leaf);
|
||||
return code;
|
||||
}
|
||||
|
||||
|
|
|
@ -384,7 +384,8 @@ Fragment FlowGraphBuilder::FfiCall(
|
|||
Fragment body;
|
||||
|
||||
FfiCallInstr* const call =
|
||||
new (Z) FfiCallInstr(Z, GetNextDeoptId(), marshaller);
|
||||
new (Z) FfiCallInstr(Z, GetNextDeoptId(), marshaller,
|
||||
parsed_function_->function().FfiIsLeaf());
|
||||
|
||||
for (intptr_t i = call->InputCount() - 1; i >= 0; --i) {
|
||||
call->SetInputAt(i, Pop());
|
||||
|
|
|
@ -164,7 +164,7 @@ namespace dart {
|
|||
V(_WeakProperty, set:value, WeakProperty_setValue, 0x8b2bafab) \
|
||||
V(::, _classRangeCheck, ClassRangeCheck, 0x00269620) \
|
||||
V(::, _abi, FfiAbi, 0x7c4ab775) \
|
||||
V(::, _asFunctionInternal, FfiAsFunctionInternal, 0xbbcb235a) \
|
||||
V(::, _asFunctionInternal, FfiAsFunctionInternal, 0x92ae104f) \
|
||||
V(::, _nativeCallbackFunction, FfiNativeCallbackFunction, 0x3ff5ae9c) \
|
||||
V(::, _nativeEffect, NativeEffect, 0x61e00b59) \
|
||||
V(::, _loadInt8, FfiLoadInt8, 0x0f04dfd6) \
|
||||
|
|
|
@ -489,7 +489,7 @@ static constexpr dart::compiler::target::word
|
|||
static constexpr dart::compiler::target::word ExternalTypedData_InstanceSize =
|
||||
12;
|
||||
static constexpr dart::compiler::target::word FfiTrampolineData_InstanceSize =
|
||||
24;
|
||||
28;
|
||||
static constexpr dart::compiler::target::word Field_InstanceSize = 60;
|
||||
static constexpr dart::compiler::target::word Float32x4_InstanceSize = 24;
|
||||
static constexpr dart::compiler::target::word Float64x2_InstanceSize = 24;
|
||||
|
@ -1569,7 +1569,7 @@ static constexpr dart::compiler::target::word
|
|||
static constexpr dart::compiler::target::word ExternalTypedData_InstanceSize =
|
||||
12;
|
||||
static constexpr dart::compiler::target::word FfiTrampolineData_InstanceSize =
|
||||
24;
|
||||
28;
|
||||
static constexpr dart::compiler::target::word Field_InstanceSize = 60;
|
||||
static constexpr dart::compiler::target::word Float32x4_InstanceSize = 24;
|
||||
static constexpr dart::compiler::target::word Float64x2_InstanceSize = 24;
|
||||
|
@ -3738,7 +3738,7 @@ static constexpr dart::compiler::target::word
|
|||
static constexpr dart::compiler::target::word ExternalTypedData_InstanceSize =
|
||||
12;
|
||||
static constexpr dart::compiler::target::word FfiTrampolineData_InstanceSize =
|
||||
24;
|
||||
28;
|
||||
static constexpr dart::compiler::target::word Field_InstanceSize = 60;
|
||||
static constexpr dart::compiler::target::word Float32x4_InstanceSize = 24;
|
||||
static constexpr dart::compiler::target::word Float64x2_InstanceSize = 24;
|
||||
|
@ -4806,7 +4806,7 @@ static constexpr dart::compiler::target::word
|
|||
static constexpr dart::compiler::target::word ExternalTypedData_InstanceSize =
|
||||
12;
|
||||
static constexpr dart::compiler::target::word FfiTrampolineData_InstanceSize =
|
||||
24;
|
||||
28;
|
||||
static constexpr dart::compiler::target::word Field_InstanceSize = 60;
|
||||
static constexpr dart::compiler::target::word Float32x4_InstanceSize = 24;
|
||||
static constexpr dart::compiler::target::word Float64x2_InstanceSize = 24;
|
||||
|
@ -7018,7 +7018,7 @@ static constexpr dart::compiler::target::word
|
|||
static constexpr dart::compiler::target::word
|
||||
AOT_ExternalTypedData_InstanceSize = 12;
|
||||
static constexpr dart::compiler::target::word
|
||||
AOT_FfiTrampolineData_InstanceSize = 24;
|
||||
AOT_FfiTrampolineData_InstanceSize = 28;
|
||||
static constexpr dart::compiler::target::word AOT_Field_InstanceSize = 48;
|
||||
static constexpr dart::compiler::target::word AOT_Float32x4_InstanceSize = 24;
|
||||
static constexpr dart::compiler::target::word AOT_Float64x2_InstanceSize = 24;
|
||||
|
@ -10049,7 +10049,7 @@ static constexpr dart::compiler::target::word
|
|||
static constexpr dart::compiler::target::word
|
||||
AOT_ExternalTypedData_InstanceSize = 12;
|
||||
static constexpr dart::compiler::target::word
|
||||
AOT_FfiTrampolineData_InstanceSize = 24;
|
||||
AOT_FfiTrampolineData_InstanceSize = 28;
|
||||
static constexpr dart::compiler::target::word AOT_Field_InstanceSize = 48;
|
||||
static constexpr dart::compiler::target::word AOT_Float32x4_InstanceSize = 24;
|
||||
static constexpr dart::compiler::target::word AOT_Float64x2_InstanceSize = 24;
|
||||
|
|
|
@ -246,6 +246,12 @@ DART_EXPORT void* Dart_ExecuteInternalCommand(const char* command, void* arg) {
|
|||
IsolateGroup::Current()->heap()->CollectAllGarbage();
|
||||
return nullptr;
|
||||
|
||||
} else if (strcmp(command, "is-thread-in-generated") == 0) {
|
||||
if (Thread::Current()->execution_state() == Thread::kThreadInGenerated) {
|
||||
return reinterpret_cast<void*>(1);
|
||||
}
|
||||
return nullptr;
|
||||
|
||||
} else if (strcmp(command, "is-mutator-in-native") == 0) {
|
||||
Isolate* const isolate = reinterpret_cast<Isolate*>(arg);
|
||||
if (isolate->mutator_thread()->execution_state_cross_thread_for_testing() ==
|
||||
|
|
|
@ -7535,6 +7535,20 @@ void Function::SetFfiCallbackId(int32_t value) const {
|
|||
FfiTrampolineData::Cast(obj).set_callback_id(value);
|
||||
}
|
||||
|
||||
bool Function::FfiIsLeaf() const {
|
||||
ASSERT(IsFfiTrampoline());
|
||||
const Object& obj = Object::Handle(untag()->data());
|
||||
ASSERT(!obj.IsNull());
|
||||
return FfiTrampolineData::Cast(obj).is_leaf();
|
||||
}
|
||||
|
||||
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());
|
||||
const Object& obj = Object::Handle(data());
|
||||
|
@ -10295,6 +10309,10 @@ void FfiTrampolineData::set_callback_id(int32_t callback_id) const {
|
|||
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());
|
||||
|
@ -10307,6 +10325,7 @@ FfiTrampolineDataPtr FfiTrampolineData::New() {
|
|||
Heap::kOld, FfiTrampolineData::ContainsCompressedPointers());
|
||||
FfiTrampolineDataPtr data = static_cast<FfiTrampolineDataPtr>(raw);
|
||||
data->untag()->callback_id_ = 0;
|
||||
data->untag()->is_leaf_ = false;
|
||||
return data;
|
||||
}
|
||||
|
||||
|
|
|
@ -2554,6 +2554,12 @@ class Function : public Object {
|
|||
// Can only be called on FFI trampolines.
|
||||
void SetFfiCallbackId(int32_t value) const;
|
||||
|
||||
// Can only be called on FFI trampolines.
|
||||
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;
|
||||
|
@ -3869,6 +3875,9 @@ class FfiTrampolineData : public Object {
|
|||
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);
|
||||
|
|
|
@ -1463,6 +1463,9 @@ class UntaggedFfiTrampolineData : public UntaggedObject {
|
|||
// Will be 0 for non-callbacks. Check 'callback_target_' to determine if this
|
||||
// is a callback or not.
|
||||
uint32_t callback_id_;
|
||||
|
||||
// Whether this is a leaf call - i.e. one that doesn't call back into Dart.
|
||||
bool is_leaf_;
|
||||
};
|
||||
|
||||
class UntaggedField : public UntaggedObject {
|
||||
|
|
|
@ -90,6 +90,16 @@ main() {
|
|||
print(result.runtimeType);
|
||||
}
|
||||
|
||||
{
|
||||
// As a leaf call.
|
||||
BinaryOp sumPlus42Leaf = ffiTestFunctions
|
||||
.lookupFunction<NativeBinaryOp, BinaryOp>("SumPlus42", isLeaf: true);
|
||||
|
||||
final result = sumPlus42Leaf(3, 17);
|
||||
print(result);
|
||||
print(result.runtimeType);
|
||||
}
|
||||
|
||||
{
|
||||
// Various size arguments.
|
||||
QuadOp intComputation = ffiTestFunctions
|
||||
|
|
|
@ -92,6 +92,16 @@ main() {
|
|||
print(result.runtimeType);
|
||||
}
|
||||
|
||||
{
|
||||
// As a leaf call.
|
||||
BinaryOp sumPlus42Leaf = ffiTestFunctions
|
||||
.lookupFunction<NativeBinaryOp, BinaryOp>("SumPlus42", isLeaf: true);
|
||||
|
||||
final result = sumPlus42Leaf(3, 17);
|
||||
print(result);
|
||||
print(result.runtimeType);
|
||||
}
|
||||
|
||||
{
|
||||
// Various size arguments.
|
||||
QuadOp intComputation = ffiTestFunctions
|
||||
|
|
|
@ -52,7 +52,7 @@ class DynamicLibrary {
|
|||
|
||||
extension DynamicLibraryExtension on DynamicLibrary {
|
||||
@patch
|
||||
DS lookupFunction<NS extends Function, DS extends Function>(
|
||||
String symbolName) =>
|
||||
DS lookupFunction<NS extends Function, DS extends Function>(String symbolName,
|
||||
{bool isLeaf: false}) =>
|
||||
throw UnsupportedError("The body is inlined in the frontend.");
|
||||
}
|
||||
|
|
|
@ -38,7 +38,8 @@ Pointer<T> _fromAddress<T extends NativeType>(int ptr) native "Ffi_fromAddress";
|
|||
// this function.
|
||||
@pragma("vm:recognized", "other")
|
||||
DS _asFunctionInternal<DS extends Function, NS extends Function>(
|
||||
Pointer<NativeFunction<NS>> ptr) native "Ffi_asFunctionInternal";
|
||||
Pointer<NativeFunction<NS>> ptr,
|
||||
bool isLeaf) native "Ffi_asFunctionInternal";
|
||||
|
||||
dynamic _asExternalTypedData(Pointer ptr, int count)
|
||||
native "Ffi_asExternalTypedData";
|
||||
|
@ -336,7 +337,7 @@ Pointer<Pointer<S>> _elementAtPointer<S extends NativeType>(
|
|||
extension NativeFunctionPointer<NF extends Function>
|
||||
on Pointer<NativeFunction<NF>> {
|
||||
@patch
|
||||
DF asFunction<DF extends Function>() =>
|
||||
DF asFunction<DF extends Function>({bool isLeaf: false}) =>
|
||||
throw UnsupportedError("The body is inlined in the frontend.");
|
||||
}
|
||||
|
||||
|
|
|
@ -76,5 +76,6 @@ extension DynamicLibraryExtension on DynamicLibrary {
|
|||
/// int Function(int, int)>('add');
|
||||
/// ```
|
||||
external F lookupFunction<T extends Function, F extends Function>(
|
||||
String symbolName);
|
||||
String symbolName,
|
||||
{bool isLeaf: false});
|
||||
}
|
||||
|
|
|
@ -149,7 +149,8 @@ extension NativeFunctionPointer<NF extends Function>
|
|||
on Pointer<NativeFunction<NF>> {
|
||||
/// Convert to Dart function, automatically marshalling the arguments
|
||||
/// and return value.
|
||||
external DF asFunction<@DartRepresentationOf("NF") DF extends Function>();
|
||||
external DF asFunction<@DartRepresentationOf("NF") DF extends Function>(
|
||||
{bool isLeaf: false});
|
||||
}
|
||||
|
||||
//
|
||||
|
|
|
@ -17,13 +17,19 @@ class CallbackTest {
|
|||
final String name;
|
||||
final Pointer callback;
|
||||
final void Function() afterCallbackChecks;
|
||||
final bool isLeaf;
|
||||
|
||||
CallbackTest(this.name, this.callback) : afterCallbackChecks = noChecks {}
|
||||
CallbackTest.withCheck(this.name, this.callback, this.afterCallbackChecks) {}
|
||||
CallbackTest(this.name, this.callback, {this.isLeaf: false})
|
||||
: afterCallbackChecks = noChecks {}
|
||||
CallbackTest.withCheck(this.name, this.callback, this.afterCallbackChecks,
|
||||
{this.isLeaf: false}) {}
|
||||
|
||||
void run() {
|
||||
final NativeCallbackTestFn tester = ffiTestFunctions
|
||||
.lookupFunction<NativeCallbackTest, NativeCallbackTestFn>("Test$name");
|
||||
final NativeCallbackTestFn tester = isLeaf
|
||||
? ffiTestFunctions.lookupFunction<NativeCallbackTest,
|
||||
NativeCallbackTestFn>("Test$name", isLeaf: true)
|
||||
: ffiTestFunctions.lookupFunction<NativeCallbackTest,
|
||||
NativeCallbackTestFn>("Test$name", isLeaf: false);
|
||||
|
||||
final int testCode = tester(callback);
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -19,18 +19,22 @@ import 'very_large_struct.dart';
|
|||
typedef NativeCoordinateOp = Pointer<Coordinate> Function(Pointer<Coordinate>);
|
||||
|
||||
void main() {
|
||||
testFunctionWithStruct();
|
||||
testFunctionWithStructArray();
|
||||
testFunctionWithVeryLargeStruct();
|
||||
for (final isLeaf in [false, true]) {
|
||||
testFunctionWithStruct(isLeaf: isLeaf);
|
||||
testFunctionWithStructArray(isLeaf: isLeaf);
|
||||
testFunctionWithVeryLargeStruct(isLeaf: isLeaf);
|
||||
}
|
||||
}
|
||||
|
||||
DynamicLibrary ffiTestFunctions = dlopenPlatformSpecific("ffi_test_functions");
|
||||
|
||||
/// pass a struct to a c function and get a struct as return value
|
||||
void testFunctionWithStruct() {
|
||||
void testFunctionWithStruct({bool isLeaf: false}) {
|
||||
Pointer<NativeFunction<NativeCoordinateOp>> p1 =
|
||||
ffiTestFunctions.lookup("TransposeCoordinate");
|
||||
NativeCoordinateOp f1 = p1.asFunction();
|
||||
NativeCoordinateOp f1 =
|
||||
(isLeaf ? p1.asFunction(isLeaf: true) : p1.asFunction(isLeaf: false));
|
||||
;
|
||||
|
||||
final c1 = calloc<Coordinate>()
|
||||
..ref.x = 10.0
|
||||
|
@ -54,10 +58,12 @@ void testFunctionWithStruct() {
|
|||
}
|
||||
|
||||
/// pass an array of structs to a c funtion
|
||||
void testFunctionWithStructArray() {
|
||||
void testFunctionWithStructArray({bool isLeaf: false}) {
|
||||
Pointer<NativeFunction<NativeCoordinateOp>> p1 =
|
||||
ffiTestFunctions.lookup("CoordinateElemAt1");
|
||||
NativeCoordinateOp f1 = p1.asFunction();
|
||||
NativeCoordinateOp f1 =
|
||||
(isLeaf ? p1.asFunction(isLeaf: true) : p1.asFunction(isLeaf: false));
|
||||
;
|
||||
|
||||
final coordinateArray = calloc<Coordinate>(3);
|
||||
Coordinate c1 = coordinateArray[0];
|
||||
|
@ -83,10 +89,12 @@ void testFunctionWithStructArray() {
|
|||
typedef VeryLargeStructSum = int Function(Pointer<VeryLargeStruct>);
|
||||
typedef NativeVeryLargeStructSum = Int64 Function(Pointer<VeryLargeStruct>);
|
||||
|
||||
void testFunctionWithVeryLargeStruct() {
|
||||
void testFunctionWithVeryLargeStruct({bool isLeaf: false}) {
|
||||
Pointer<NativeFunction<NativeVeryLargeStructSum>> p1 =
|
||||
ffiTestFunctions.lookup("SumVeryLargeStruct");
|
||||
VeryLargeStructSum f = p1.asFunction();
|
||||
VeryLargeStructSum f =
|
||||
(isLeaf ? p1.asFunction(isLeaf: true) : p1.asFunction(isLeaf: false));
|
||||
;
|
||||
|
||||
final vlsArray = calloc<VeryLargeStruct>(2);
|
||||
VeryLargeStruct vls1 = vlsArray[0];
|
||||
|
|
|
@ -537,7 +537,7 @@ extension on CompositeType {
|
|||
}
|
||||
|
||||
extension on FunctionType {
|
||||
String get dartCallCode {
|
||||
String dartCallCode({bool isLeaf: false}) {
|
||||
final a = ArgumentValueAssigner();
|
||||
final assignValues = arguments.assignValueStatements(a);
|
||||
final argumentFrees = arguments.dartFreeStatements();
|
||||
|
@ -561,17 +561,19 @@ extension on FunctionType {
|
|||
break;
|
||||
}
|
||||
|
||||
final namePostfix = isLeaf ? "Leaf" : "";
|
||||
return """
|
||||
final $dartName =
|
||||
ffiTestFunctions.lookupFunction<$dartCType, $dartType>("$cName");
|
||||
final $dartName$namePostfix =
|
||||
ffiTestFunctions.lookupFunction<$dartCType, $dartType>(
|
||||
"$cName"${isLeaf ? ", isLeaf:true" : ""});
|
||||
|
||||
${reason.makeDartDocComment()}
|
||||
void $dartTestName() {
|
||||
void $dartTestName$namePostfix() {
|
||||
${arguments.dartAllocateStatements()}
|
||||
|
||||
${assignValues}
|
||||
|
||||
final result = $dartName($argumentNames);
|
||||
final result = $dartName$namePostfix($argumentNames);
|
||||
|
||||
print("result = \$result");
|
||||
|
||||
|
@ -886,11 +888,13 @@ void writeDartCallTest() {
|
|||
void main() {
|
||||
for (int i = 0; i < 10; ++i) {
|
||||
${functions.map((e) => "${e.dartTestName}();").join("\n")}
|
||||
${functions.map((e) => "${e.dartTestName}Leaf();").join("\n")}
|
||||
}
|
||||
}
|
||||
""");
|
||||
buffer.writeAll(compounds.map((e) => e.dartClass(nnbd)));
|
||||
buffer.writeAll(functions.map((e) => e.dartCallCode));
|
||||
buffer.writeAll(functions.map((e) => e.dartCallCode(isLeaf: false)));
|
||||
buffer.writeAll(functions.map((e) => e.dartCallCode(isLeaf: true)));
|
||||
|
||||
final path = callTestPath(nnbd);
|
||||
File(path).writeAsStringSync(buffer.toString());
|
||||
|
|
61
tests/ffi/vmspecific_leaf_call_test.dart
Normal file
61
tests/ffi/vmspecific_leaf_call_test.dart
Normal file
|
@ -0,0 +1,61 @@
|
|||
// Copyright (c) 2021, 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.
|
||||
//
|
||||
// SharedObjects=ffi_test_functions
|
||||
|
||||
import 'dart:ffi';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:expect/expect.dart';
|
||||
|
||||
import 'dylib_utils.dart';
|
||||
import 'ffi_test_helpers.dart';
|
||||
import 'callback_tests_utils.dart';
|
||||
|
||||
DynamicLibrary ffiTestFunctions = dlopenPlatformSpecific("ffi_test_functions");
|
||||
|
||||
testLeafCall() {
|
||||
// Note: This test currently fails on Windows AOT: https://dartbug.com/40579
|
||||
// Regular calls should transition generated -> native.
|
||||
final isThreadInGenerated = ffiTestFunctions.lookupFunction<
|
||||
Int8 Function(), int Function()>("IsThreadInGenerated");
|
||||
Expect.equals(0, isThreadInGenerated());
|
||||
// Leaf calls should remain in generated state.
|
||||
final isThreadInGeneratedLeaf = ffiTestFunctions.lookupFunction<
|
||||
Int8 Function(), int Function()>("IsThreadInGenerated", isLeaf: true);
|
||||
Expect.equals(1, isThreadInGeneratedLeaf());
|
||||
}
|
||||
|
||||
testLeafCallApi() {
|
||||
// Note: This will only crash as expected in debug build mode. In other modes
|
||||
// it's effectively skip.
|
||||
final f = ffiTestFunctions.lookupFunction<
|
||||
Void Function(), void Function()>("TestLeafCallApi", isLeaf: true);
|
||||
// Calling Dart_.. API is unsafe from leaf calls since we explicitly haven't
|
||||
// made the generated -> native transition.
|
||||
f();
|
||||
}
|
||||
|
||||
void nop() {}
|
||||
|
||||
testCallbackLeaf() {
|
||||
// This should crash with "expected: T->IsAtSafepoint()", since it's unsafe to
|
||||
// do callbacks from leaf calls (otherwise they wouldn't be leaf calls).
|
||||
// Note: This will only crash as expected in debug build mode. In other modes
|
||||
// it's effectively skip.
|
||||
CallbackTest("CallbackLeaf", Pointer.fromFunction<Void Function()>(nop),
|
||||
isLeaf:true).run();
|
||||
}
|
||||
|
||||
main() {
|
||||
testLeafCall(); //# 01: ok
|
||||
// These tests terminate the process after successful completion, so we have
|
||||
// to run them separately.
|
||||
//
|
||||
// Since they use signal handlers they only run on Linux.
|
||||
if (Platform.isLinux && !const bool.fromEnvironment("dart.vm.product")) {
|
||||
testLeafCallApi(); //# 02: ok
|
||||
testCallbackLeaf(); //# 03: ok
|
||||
}
|
||||
}
|
|
@ -61,6 +61,12 @@ void main() {
|
|||
testSizeOfHandle();
|
||||
testElementAtGeneric();
|
||||
testElementAtNativeType();
|
||||
testLookupFunctionIsLeafMustBeConst();
|
||||
testAsFunctionIsLeafMustBeConst();
|
||||
testLookupFunctionTakesHandle();
|
||||
testAsFunctionTakesHandle();
|
||||
testLookupFunctionReturnsHandle();
|
||||
testAsFunctionReturnsHandle();
|
||||
}
|
||||
|
||||
typedef Int8UnOp = Int8 Function(Int8);
|
||||
|
@ -705,6 +711,44 @@ class TestStruct1405 extends Struct {
|
|||
external Pointer<Uint8> notEmpty;
|
||||
}
|
||||
|
||||
void testLookupFunctionIsLeafMustBeConst() {
|
||||
bool notAConst = false;
|
||||
DynamicLibrary l = dlopenPlatformSpecific("ffi_test_dynamic_library");
|
||||
l.lookupFunction<NativeDoubleUnOp, DoubleUnOp>("timesFour", isLeaf:notAConst); //# 1500: compile-time error
|
||||
}
|
||||
|
||||
void testAsFunctionIsLeafMustBeConst() {
|
||||
bool notAConst = false;
|
||||
Pointer<NativeFunction<Int8UnOp>> p = Pointer.fromAddress(1337);
|
||||
IntUnOp f = p.asFunction(isLeaf:notAConst); //# 1501: compile-time error
|
||||
}
|
||||
|
||||
typedef NativeTakesHandle = Void Function(Handle);
|
||||
typedef TakesHandle = void Function(Object);
|
||||
|
||||
void testLookupFunctionTakesHandle() {
|
||||
DynamicLibrary l = dlopenPlatformSpecific("ffi_test_dynamic_library");
|
||||
l.lookupFunction<NativeTakesHandle, TakesHandle>("takesHandle", isLeaf:true); //# 1502: compile-time error
|
||||
}
|
||||
|
||||
void testAsFunctionTakesHandle() {
|
||||
Pointer<NativeFunction<NativeTakesHandle>> p = Pointer.fromAddress(1337); //# 1503: compile-time error
|
||||
TakesHandle f = p.asFunction(isLeaf:true); //# 1503: compile-time error
|
||||
}
|
||||
|
||||
typedef NativeReturnsHandle = Handle Function();
|
||||
typedef ReturnsHandle = Object Function();
|
||||
|
||||
void testLookupFunctionReturnsHandle() {
|
||||
DynamicLibrary l = dlopenPlatformSpecific("ffi_test_dynamic_library");
|
||||
l.lookupFunction<NativeReturnsHandle, ReturnsHandle>("returnsHandle", isLeaf:true); //# 1504: compile-time error
|
||||
}
|
||||
|
||||
void testAsFunctionReturnsHandle() {
|
||||
Pointer<NativeFunction<NativeReturnsHandle>> p = Pointer.fromAddress(1337); //# 1505: compile-time error
|
||||
ReturnsHandle f = p.asFunction(isLeaf:true); //# 1505: compile-time error
|
||||
}
|
||||
|
||||
@Packed(1)
|
||||
class TestStruct1600 extends Struct {
|
||||
external Pointer<Uint8> notEmpty;
|
||||
|
|
|
@ -19,13 +19,19 @@ class CallbackTest {
|
|||
final String name;
|
||||
final Pointer callback;
|
||||
final void Function() afterCallbackChecks;
|
||||
final bool isLeaf;
|
||||
|
||||
CallbackTest(this.name, this.callback) : afterCallbackChecks = noChecks {}
|
||||
CallbackTest.withCheck(this.name, this.callback, this.afterCallbackChecks) {}
|
||||
CallbackTest(this.name, this.callback, {this.isLeaf: false})
|
||||
: afterCallbackChecks = noChecks {}
|
||||
CallbackTest.withCheck(this.name, this.callback, this.afterCallbackChecks,
|
||||
{this.isLeaf: false}) {}
|
||||
|
||||
void run() {
|
||||
final NativeCallbackTestFn tester = ffiTestFunctions
|
||||
.lookupFunction<NativeCallbackTest, NativeCallbackTestFn>("Test$name");
|
||||
final NativeCallbackTestFn tester = isLeaf
|
||||
? ffiTestFunctions.lookupFunction<NativeCallbackTest,
|
||||
NativeCallbackTestFn>("Test$name", isLeaf: true)
|
||||
: ffiTestFunctions.lookupFunction<NativeCallbackTest,
|
||||
NativeCallbackTestFn>("Test$name", isLeaf: false);
|
||||
|
||||
final int testCode = tester(callback);
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -21,18 +21,21 @@ import 'very_large_struct.dart';
|
|||
typedef NativeCoordinateOp = Pointer<Coordinate> Function(Pointer<Coordinate>);
|
||||
|
||||
void main() {
|
||||
testFunctionWithStruct();
|
||||
testFunctionWithStructArray();
|
||||
testFunctionWithVeryLargeStruct();
|
||||
for (final isLeaf in [false, true]) {
|
||||
testFunctionWithStruct(isLeaf: isLeaf);
|
||||
testFunctionWithStructArray(isLeaf: isLeaf);
|
||||
testFunctionWithVeryLargeStruct(isLeaf: isLeaf);
|
||||
}
|
||||
}
|
||||
|
||||
DynamicLibrary ffiTestFunctions = dlopenPlatformSpecific("ffi_test_functions");
|
||||
|
||||
/// pass a struct to a c function and get a struct as return value
|
||||
void testFunctionWithStruct() {
|
||||
void testFunctionWithStruct({bool isLeaf: false}) {
|
||||
Pointer<NativeFunction<NativeCoordinateOp>> p1 =
|
||||
ffiTestFunctions.lookup("TransposeCoordinate");
|
||||
NativeCoordinateOp f1 = p1.asFunction();
|
||||
NativeCoordinateOp f1 =
|
||||
(isLeaf ? p1.asFunction(isLeaf: true) : p1.asFunction(isLeaf: false));
|
||||
|
||||
final c1 = calloc<Coordinate>()
|
||||
..ref.x = 10.0
|
||||
|
@ -56,10 +59,11 @@ void testFunctionWithStruct() {
|
|||
}
|
||||
|
||||
/// pass an array of structs to a c funtion
|
||||
void testFunctionWithStructArray() {
|
||||
void testFunctionWithStructArray({bool isLeaf: false}) {
|
||||
Pointer<NativeFunction<NativeCoordinateOp>> p1 =
|
||||
ffiTestFunctions.lookup("CoordinateElemAt1");
|
||||
NativeCoordinateOp f1 = p1.asFunction();
|
||||
NativeCoordinateOp f1 =
|
||||
(isLeaf ? p1.asFunction(isLeaf: true) : p1.asFunction(isLeaf: false));
|
||||
|
||||
final coordinateArray = calloc<Coordinate>(3);
|
||||
Coordinate c1 = coordinateArray[0];
|
||||
|
@ -85,10 +89,11 @@ void testFunctionWithStructArray() {
|
|||
typedef VeryLargeStructSum = int Function(Pointer<VeryLargeStruct>);
|
||||
typedef NativeVeryLargeStructSum = Int64 Function(Pointer<VeryLargeStruct>);
|
||||
|
||||
void testFunctionWithVeryLargeStruct() {
|
||||
void testFunctionWithVeryLargeStruct({bool isLeaf: false}) {
|
||||
Pointer<NativeFunction<NativeVeryLargeStructSum>> p1 =
|
||||
ffiTestFunctions.lookup("SumVeryLargeStruct");
|
||||
VeryLargeStructSum f = p1.asFunction();
|
||||
VeryLargeStructSum f =
|
||||
(isLeaf ? p1.asFunction(isLeaf: true) : p1.asFunction(isLeaf: false));
|
||||
|
||||
final vlsArray = calloc<VeryLargeStruct>(2);
|
||||
VeryLargeStruct vls1 = vlsArray[0];
|
||||
|
|
|
@ -539,7 +539,7 @@ extension on CompositeType {
|
|||
}
|
||||
|
||||
extension on FunctionType {
|
||||
String get dartCallCode {
|
||||
String dartCallCode({bool isLeaf: false}) {
|
||||
final a = ArgumentValueAssigner();
|
||||
final assignValues = arguments.assignValueStatements(a);
|
||||
final argumentFrees = arguments.dartFreeStatements();
|
||||
|
@ -563,17 +563,19 @@ extension on FunctionType {
|
|||
break;
|
||||
}
|
||||
|
||||
final namePostfix = isLeaf ? "Leaf" : "";
|
||||
return """
|
||||
final $dartName =
|
||||
ffiTestFunctions.lookupFunction<$dartCType, $dartType>("$cName");
|
||||
final ${dartName}$namePostfix =
|
||||
ffiTestFunctions.lookupFunction<$dartCType, $dartType>(
|
||||
"$cName"${isLeaf ? ", isLeaf:true" : ""});
|
||||
|
||||
${reason.makeDartDocComment()}
|
||||
void $dartTestName() {
|
||||
void ${dartTestName}$namePostfix() {
|
||||
${arguments.dartAllocateStatements()}
|
||||
|
||||
${assignValues}
|
||||
|
||||
final result = $dartName($argumentNames);
|
||||
final result = ${dartName}$namePostfix($argumentNames);
|
||||
|
||||
print("result = \$result");
|
||||
|
||||
|
@ -888,11 +890,13 @@ void writeDartCallTest() {
|
|||
void main() {
|
||||
for (int i = 0; i < 10; ++i) {
|
||||
${functions.map((e) => "${e.dartTestName}();").join("\n")}
|
||||
${functions.map((e) => "${e.dartTestName}Leaf();").join("\n")}
|
||||
}
|
||||
}
|
||||
""");
|
||||
buffer.writeAll(compounds.map((e) => e.dartClass(nnbd)));
|
||||
buffer.writeAll(functions.map((e) => e.dartCallCode));
|
||||
buffer.writeAll(functions.map((e) => e.dartCallCode(isLeaf: false)));
|
||||
buffer.writeAll(functions.map((e) => e.dartCallCode(isLeaf: true)));
|
||||
|
||||
final path = callTestPath(nnbd);
|
||||
File(path).writeAsStringSync(buffer.toString());
|
||||
|
|
60
tests/ffi_2/vmspecific_leaf_call_test.dart
Normal file
60
tests/ffi_2/vmspecific_leaf_call_test.dart
Normal file
|
@ -0,0 +1,60 @@
|
|||
// Copyright (c) 2021, 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.
|
||||
//
|
||||
// SharedObjects=ffi_test_functions
|
||||
|
||||
import 'dart:ffi';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:expect/expect.dart';
|
||||
|
||||
import 'dylib_utils.dart';
|
||||
import 'ffi_test_helpers.dart';
|
||||
import 'callback_tests_utils.dart';
|
||||
|
||||
DynamicLibrary ffiTestFunctions = dlopenPlatformSpecific("ffi_test_functions");
|
||||
|
||||
testLeafCall() {
|
||||
// Regular calls should transition generated -> native.
|
||||
final isThreadInGenerated = ffiTestFunctions.lookupFunction<
|
||||
Int8 Function(), int Function()>("IsThreadInGenerated");
|
||||
Expect.equals(0, isThreadInGenerated());
|
||||
// Leaf calls should remain in generated state.
|
||||
final isThreadInGeneratedLeaf = ffiTestFunctions.lookupFunction<
|
||||
Int8 Function(), int Function()>("IsThreadInGenerated", isLeaf: true);
|
||||
Expect.equals(1, isThreadInGeneratedLeaf());
|
||||
}
|
||||
|
||||
testLeafCallApi() {
|
||||
// Note: This will only crash as expected in debug build mode. In other modes
|
||||
// it's effectively skip.
|
||||
final f = ffiTestFunctions.lookupFunction<
|
||||
Void Function(), void Function()>("TestLeafCallApi", isLeaf: true);
|
||||
// Calling Dart_.. API is unsafe from leaf calls since we explicitly haven't
|
||||
// made the generated -> native transition.
|
||||
f();
|
||||
}
|
||||
|
||||
void nop() {}
|
||||
|
||||
testCallbackLeaf() {
|
||||
// This should crash with "expected: T->IsAtSafepoint()", since it's unsafe to
|
||||
// do callbacks from leaf calls (otherwise they wouldn't be leaf calls).
|
||||
// Note: This will only crash as expected in debug build mode. In other modes
|
||||
// it's effectively skip.
|
||||
CallbackTest("CallbackLeaf", Pointer.fromFunction<Void Function()>(nop),
|
||||
isLeaf:true).run();
|
||||
}
|
||||
|
||||
main() {
|
||||
testLeafCall(); //# 01: ok
|
||||
// These tests terminate the process after successful completion, so we have
|
||||
// to run them separately.
|
||||
//
|
||||
// Since they use signal handlers they only run on Linux.
|
||||
if (Platform.isLinux && !const bool.fromEnvironment("dart.vm.product")) {
|
||||
testLeafCallApi(); //# 02: ok
|
||||
testCallbackLeaf(); //# 03: ok
|
||||
}
|
||||
}
|
|
@ -63,6 +63,12 @@ void main() {
|
|||
testSizeOfHandle();
|
||||
testElementAtGeneric();
|
||||
testElementAtNativeType();
|
||||
testLookupFunctionIsLeafMustBeConst();
|
||||
testAsFunctionIsLeafMustBeConst();
|
||||
testLookupFunctionTakesHandle();
|
||||
testAsFunctionTakesHandle();
|
||||
testLookupFunctionReturnsHandle();
|
||||
testAsFunctionReturnsHandle();
|
||||
}
|
||||
|
||||
typedef Int8UnOp = Int8 Function(Int8);
|
||||
|
@ -705,6 +711,44 @@ class TestStruct1405 extends Struct {
|
|||
Pointer<Uint8> notEmpty;
|
||||
}
|
||||
|
||||
void testLookupFunctionIsLeafMustBeConst() {
|
||||
bool notAConst = false;
|
||||
DynamicLibrary l = dlopenPlatformSpecific("ffi_test_dynamic_library");
|
||||
l.lookupFunction<NativeDoubleUnOp, DoubleUnOp>("timesFour", isLeaf:notAConst); //# 1500: compile-time error
|
||||
}
|
||||
|
||||
void testAsFunctionIsLeafMustBeConst() {
|
||||
bool notAConst = false;
|
||||
Pointer<NativeFunction<Int8UnOp>> p = Pointer.fromAddress(1337);
|
||||
IntUnOp f = p.asFunction(isLeaf:notAConst); //# 1501: compile-time error
|
||||
}
|
||||
|
||||
typedef NativeTakesHandle = Void Function(Handle);
|
||||
typedef TakesHandle = void Function(Object);
|
||||
|
||||
void testLookupFunctionTakesHandle() {
|
||||
DynamicLibrary l = dlopenPlatformSpecific("ffi_test_dynamic_library");
|
||||
l.lookupFunction<NativeTakesHandle, TakesHandle>("takesHandle", isLeaf:true); //# 1502: compile-time error
|
||||
}
|
||||
|
||||
void testAsFunctionTakesHandle() {
|
||||
Pointer<NativeFunction<NativeTakesHandle>> p = Pointer.fromAddress(1337); //# 1503: compile-time error
|
||||
TakesHandle f = p.asFunction(isLeaf:true); //# 1503: compile-time error
|
||||
}
|
||||
|
||||
typedef NativeReturnsHandle = Handle Function();
|
||||
typedef ReturnsHandle = Object Function();
|
||||
|
||||
void testLookupFunctionReturnsHandle() {
|
||||
DynamicLibrary l = dlopenPlatformSpecific("ffi_test_dynamic_library");
|
||||
l.lookupFunction<NativeReturnsHandle, ReturnsHandle>("returnsHandle", isLeaf:true); //# 1504: compile-time error
|
||||
}
|
||||
|
||||
void testAsFunctionReturnsHandle() {
|
||||
Pointer<NativeFunction<NativeReturnsHandle>> p = Pointer.fromAddress(1337); //# 1505: compile-time error
|
||||
ReturnsHandle f = p.asFunction(isLeaf:true); //# 1505: compile-time error
|
||||
}
|
||||
|
||||
@Packed(1)
|
||||
class TestStruct1600 extends Struct {
|
||||
Pointer<Uint8> notEmpty;
|
||||
|
|
Loading…
Reference in a new issue