[vm/ffi] Regression test for dartbug.com/37133
Also removed some redundant test code.
Change-Id: Idbb343d35592816814d473de2317324029481c6f
Cq-Include-Trybots: luci.dart.try:vm-ffi-android-debug-arm-try,vm-kernel-win-debug-x64-try,vm-kernel-win-debug-ia32-try,vm-kernel-mac-debug-x64-try,vm-kernel-reload-mac-debug-simdbc64-try
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/105590
Commit-Queue: Samir Jindel <sjindel@google.com>
Reviewed-by: Daco Harkes <dacoharkes@google.com>
Reviewed-by: Martin Kustermann <kustermann@google.com>
diff --git a/runtime/bin/BUILD.gn b/runtime/bin/BUILD.gn
index 5b5cacc..f8631d7 100644
--- a/runtime/bin/BUILD.gn
+++ b/runtime/bin/BUILD.gn
@@ -1028,7 +1028,7 @@
":dart",
]
sources = [
- "ffi_test_dynamic_library.cc",
+ "ffi_test/ffi_test_dynamic_library.cc",
]
include_dirs = [ ".." ]
defines = [
@@ -1050,8 +1050,13 @@
":dart",
]
sources = [
- "ffi_test_functions.cc",
+ "ffi_test/ffi_test_functions.cc",
]
+ if (is_win && current_cpu == "x64") {
+ sources += [ "ffi_test/clobber_x64_win.S" ]
+ } else if (!is_win) {
+ sources += [ "ffi_test/clobber_$current_cpu.S" ]
+ }
include_dirs = [ ".." ]
defines = [
# The only effect of DART_SHARED_LIB is to export the Dart API.
diff --git a/runtime/bin/ffi_test/clobber_arm.S b/runtime/bin/ffi_test/clobber_arm.S
new file mode 100644
index 0000000..9564a4a
--- /dev/null
+++ b/runtime/bin/ffi_test/clobber_arm.S
@@ -0,0 +1,29 @@
+.text
+
+.global ClobberAndCall
+ClobberAndCall:
+
+/* Save r3 to keep the stack aligned to 8 bytes. */
+stmdb sp!,{r3, r4, r5, r6, r10, lr}
+
+/* Arguments descriptor register. */
+mov r4, #1
+
+/* Pool pointer register. */
+mov r5, #1
+
+/* Code pointer register. */
+mov r6, #1
+
+/* Thread register. */
+mov r10, #1
+
+/* Clobber callee-saved registers. */
+mov r1, #1
+mov r2, #1
+mov r3, #1
+mov r12, #1
+
+blx r0
+
+ldm sp!,{r3, r4, r5, r6, r10, pc}
diff --git a/runtime/bin/ffi_test/clobber_arm64.S b/runtime/bin/ffi_test/clobber_arm64.S
new file mode 100644
index 0000000..111c8fa
--- /dev/null
+++ b/runtime/bin/ffi_test/clobber_arm64.S
@@ -0,0 +1,44 @@
+.text
+
+.global ClobberAndCall
+ClobberAndCall:
+
+/* Save link register register and thread register. Keep stack aligned to 16 bytes. */
+stp lr, x26, [sp, #-16]!
+mov lr, #1
+mov x26, #1
+
+/* Arguments descriptor register isn't callee-saved. */
+mov x4, #1
+
+/* Dart stack pointer, also volatile. */
+mov x15, #1
+
+/* Pool pointer register and code register. Keep stack aligned to 16 bytes. */
+stp x24, x27, [sp, #-16]!
+
+mov x24, #1
+mov x27, #1
+
+/* Clobber all other volatile registers. */
+mov x1, #1
+mov x2, #1
+mov x3, #1
+mov x4, #1
+mov x5, #1
+mov x6, #1
+mov x7, #1
+mov x8, #1
+mov x9, #1
+mov x10, #1
+mov x11, #1
+mov x12, #1
+mov x13, #1
+mov x14, #1
+
+blr x0
+
+ldp x24, x27, [sp], #16
+ldp lr, x26, [sp], #16
+
+blr lr
diff --git a/runtime/bin/ffi_test/clobber_x64.S b/runtime/bin/ffi_test/clobber_x64.S
new file mode 100644
index 0000000..a54aa73
--- /dev/null
+++ b/runtime/bin/ffi_test/clobber_x64.S
@@ -0,0 +1,44 @@
+.intel_syntax noprefix
+.text
+
+.globl _ClobberAndCall
+_ClobberAndCall:
+.globl ClobberAndCall
+ClobberAndCall:
+
+/* Clobber some significant registers and call the nullary function which is
+ passed in as the first argument. */
+
+/* Pool pointer register. */
+push r15
+mov r15, 1
+
+/* Thread register. */
+push r14
+mov r14, 1
+
+/* Code register. */
+push r12
+mov r12, 1
+
+/* Arguments descriptor register (volatile). */
+mov r10, 1
+
+/* Clobber all other volatile registers (except the argument). */
+mov rax, 1
+mov rcx, 1
+mov rdx, 1
+mov rsi, 1
+mov r8, 1
+mov r9, 1
+mov r11, 1
+
+/* Stack must be 16-byte aligned before the call. We save three registers above
+ to ensure this. */
+call rdi
+
+pop r12
+pop r14
+pop r15
+
+ret
diff --git a/runtime/bin/ffi_test/clobber_x64_win.S b/runtime/bin/ffi_test/clobber_x64_win.S
new file mode 100644
index 0000000..2301e15
--- /dev/null
+++ b/runtime/bin/ffi_test/clobber_x64_win.S
@@ -0,0 +1,41 @@
+.code
+
+ClobberAndCall proc
+
+;; Clobber some significant registers and call the nullary function which is
+;; passed in as the first argument.
+
+;; Pool pointer register.
+push r15
+mov r15, 1
+
+;; Thread register.
+push r14
+mov r14, 1
+
+;; Code register.
+push r12
+mov r12, 1
+
+;; Arguments descriptor register (volatile).
+mov r10, 1
+
+;; Clobber all other volatile registers (except the argument).
+mov rax, 1
+mov rcx, 1
+mov r8, 1
+mov r9, 1
+mov r11, 1
+
+;; Stack must be 16-byte aligned before the call. We save three registers above
+;; to ensure this.
+call rdx
+
+pop r12
+pop r14
+pop r15
+
+ret
+ClobberAndCall endp
+
+end
diff --git a/runtime/bin/ffi_test/clobber_x86.S b/runtime/bin/ffi_test/clobber_x86.S
new file mode 100644
index 0000000..d5de1ee
--- /dev/null
+++ b/runtime/bin/ffi_test/clobber_x86.S
@@ -0,0 +1,37 @@
+.intel_syntax noprefix
+.text
+
+.globl _ClobberAndCall
+_ClobberAndCall:
+.globl ClobberAndCall
+ClobberAndCall:
+
+/* Load the target function. */
+mov eax, [esp+0x4]
+
+/* Code register. */
+push edi
+mov edi, 1
+
+/* Thread register. */
+push esi
+mov esi, 1
+
+/* Arguments descriptor register (volatile). */
+mov edx, 1
+
+/* Clobber all other volatile registers. */
+mov ecx, 1
+mov edx, 1
+
+/* Align the frame to 16 bytes. */
+sub esp, 4
+
+call eax
+
+add esp, 4
+
+pop esi
+pop edi
+
+ret
diff --git a/runtime/bin/ffi_test_dynamic_library.cc b/runtime/bin/ffi_test/ffi_test_dynamic_library.cc
similarity index 100%
rename from runtime/bin/ffi_test_dynamic_library.cc
rename to runtime/bin/ffi_test/ffi_test_dynamic_library.cc
diff --git a/runtime/bin/ffi_test_functions.cc b/runtime/bin/ffi_test/ffi_test_functions.cc
similarity index 97%
rename from runtime/bin/ffi_test_functions.cc
rename to runtime/bin/ffi_test/ffi_test_functions.cc
index 48757cf..91c7856 100644
--- a/runtime/bin/ffi_test_functions.cc
+++ b/runtime/bin/ffi_test/ffi_test_functions.cc
@@ -523,25 +523,6 @@
Dart_ExecuteInternalCommand("gc-now");
}
-// Calls a Dart function to allocate 'count' objects.
-// Used for stress-testing GC when re-entering the API.
-DART_EXPORT void AllocateThroughDart() {
- Dart_EnterScope();
- Dart_Handle root = Dart_RootLibrary();
- Dart_Handle result = Dart_Invoke(
- root, Dart_NewStringFromCString("testAllocationsInDartHelper"), 0, NULL);
- const char* error;
- if (Dart_IsError(result)) {
- Dart_StringToCString(Dart_ToString(result), &error);
- fprintf(stderr, "Could not call 'testAllocationsInDartHelper': %s\n",
- error);
- Dart_DumpNativeStackTrace(nullptr);
- Dart_PrepareToAbort();
- abort();
- }
- Dart_ExitScope();
-}
-
////////////////////////////////////////////////////////////////////////////////
// Tests for callbacks.
@@ -663,6 +644,24 @@
return 0;
}
+// Defined in ffi_test_functions.S.
+//
+// Clobbers some registers with special meaning in Dart before re-entry, for
+// stress-testing. Not used on 32-bit Windows due to complications with Windows
+// "safeseh".
+#if defined(TARGET_OS_WINDOWS) && defined(HOST_ARCH_IA32)
+void ClobberAndCall(void (*fn)()) {
+ fn();
+}
+#else
+extern "C" void ClobberAndCall(void (*fn)());
+#endif
+
+DART_EXPORT int TestGC(void (*do_gc)()) {
+ ClobberAndCall(do_gc);
+ return 0;
+}
+
struct CallbackTestData {
int success;
void (*callback)();
diff --git a/runtime/bin/ffi_test/ffi_test_functions_helpers.S b/runtime/bin/ffi_test/ffi_test_functions_helpers.S
new file mode 100644
index 0000000..f78ef2a
--- /dev/null
+++ b/runtime/bin/ffi_test/ffi_test_functions_helpers.S
@@ -0,0 +1,98 @@
+#if defined(_M_X64) || defined(__x86_64__) /* HOST_ARCH_X64 */
+
+.intel_syntax noprefix
+.text
+
+#if defined(__linux__) || defined(__FreeBSD__) /* HOST_OS_LINUX */
+.globl ClobberAndCall
+.type ClobberAndCall, @function
+ClobberAndCall:
+#else /* HOST_OS_MACOS */
+.globl _ClobberAndCall
+_ClobberAndCall:
+#endif
+
+/* Clobber some significant registers and call the nullary function which is
+ passed in as the first argument. */
+
+/* Pool pointer register. */
+push r15
+mov r15, 1
+
+/* Thread register. */
+push r14
+mov r14, 1
+
+/* Code register. */
+push r12
+mov r12, 1
+
+/* Arguments descriptor register. */
+push r10
+mov r10, 1
+
+call rdi
+
+pop r10
+pop r12
+pop r14
+pop r15
+
+ret
+
+#elif defined(_M_IX86) || defined(__i386__) /* HOST_ARCH_IA32 */
+
+#elif defined(__aarch64__) /* HOST_ARCH_ARM64 */
+
+.text
+.global ClobberAndCall
+.type ClobberAndCall, %function
+ClobberAndCall:
+
+/* Save link register register and thread register. */
+stp lr, x26, [sp, #-16]!
+mov lr, #1
+mov x26, #1
+
+/* Arguments descriptor register isn't callee-saved. */
+mov x4, #1
+
+/* Pool pointer register and code register. */
+stp x24, x27, [sp, #-16]!
+
+mov x24, #1
+mov x27, #1
+
+blr x0
+
+ldp x24, x27, [sp], #16
+ldp lr, x26, [sp], #16
+
+blr lr
+
+#elif defined(__ARMEL__) /* HOST_ARCH_ARM */
+
+.text
+.global ClobberAndCall
+.type ClobberAndCall, %function
+ClobberAndCall:
+
+stmdb sp!,{r4, r5, r6, r10, lr}
+
+/* Arguments descriptor register. */
+mov r4, #1
+
+/* Pool pointer register. */
+mov r5, #1
+
+/* Code pointer register. */
+mov r6, #1
+
+/* Thread register. */
+mov r10, #1
+
+blx r0
+
+ldm sp!,{r4, r5, r6, r10, pc}
+
+#endif
diff --git a/runtime/vm/constants_arm.h b/runtime/vm/constants_arm.h
index 36ae1dc..48378f7 100644
--- a/runtime/vm/constants_arm.h
+++ b/runtime/vm/constants_arm.h
@@ -49,15 +49,15 @@
// iOS ABI
// See "iOS ABI Function Call Guide"
-// R0-R1: Argument / result / volatile
-// R2-R3: Argument / volatile
-// R4-R6: Preserved
-// R7: Frame pointer
-// R8-R9: Preserved
-// R12: Volatile
-// R13: Stack pointer
-// R14: Link register
-// R15: Program counter
+// R0-R1: Argument / result / volatile
+// R2-R3: Argument / volatile
+// R4-R6: Preserved
+// R7: Frame pointer
+// R8-R11: Preserved
+// R12: Volatile
+// R13: Stack pointer
+// R14: Link register
+// R15: Program counter
// Stack alignment: 4 bytes always, 4 bytes at public interfaces
// iOS passes floating point arguments in integer registers (softfp)
diff --git a/tests/ffi/ffi_test_helpers.dart b/tests/ffi/ffi_test_helpers.dart
new file mode 100644
index 0000000..5d14d4a
--- /dev/null
+++ b/tests/ffi/ffi_test_helpers.dart
@@ -0,0 +1,16 @@
+// Copyright (c) 2019, 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.
+//
+// Helpers for tests which trigger GC in delicate places.
+
+import 'dart:ffi' as ffi;
+import 'dylib_utils.dart';
+
+typedef NativeNullaryOp = ffi.Void Function();
+typedef NullaryOpVoid = void Function();
+
+final ffi.DynamicLibrary ffiTestFunctions =
+ dlopenPlatformSpecific("ffi_test_functions");
+final triggerGc = ffiTestFunctions
+ .lookupFunction<NativeNullaryOp, NullaryOpVoid>("TriggerGC");
diff --git a/tests/ffi/function_callbacks_test.dart b/tests/ffi/function_callbacks_test.dart
index fc2e46c..b3ddc9a 100644
--- a/tests/ffi/function_callbacks_test.dart
+++ b/tests/ffi/function_callbacks_test.dart
@@ -4,6 +4,7 @@
//
// Dart test program for testing dart:ffi function pointers with callbacks.
//
+// VMOptions=--enable-testing-pragmas
// SharedObjects=ffi_test_functions
library FfiTest;
@@ -15,6 +16,8 @@
import "package:expect/expect.dart";
+import 'ffi_test_helpers.dart';
+
typedef NativeCallbackTest = Int32 Function(Pointer);
typedef NativeCallbackTestFn = int Function(Pointer);
@@ -147,6 +150,10 @@
typedef ReturnVoid = Void Function();
void returnVoid() {}
+void testGC() {
+ triggerGc();
+}
+
final List<Test> testcases = [
Test("SimpleAddition", fromFunction<SimpleAdditionType>(simpleAddition)),
Test("IntComputation", fromFunction<IntComputationType>(intComputation)),
@@ -160,6 +167,7 @@
Test("Store", fromFunction<StoreType>(store)),
Test("NullPointers", fromFunction<NullPointersType>(nullPointers)),
Test("ReturnNull", fromFunction<ReturnNullType>(returnNull)),
+ Test("GC", fromFunction<ReturnVoid>(testGC)),
];
testCallbackWrongThread() =>
diff --git a/tests/ffi/function_gc_test.dart b/tests/ffi/function_gc_test.dart
index 93b014f..8fa794d 100644
--- a/tests/ffi/function_gc_test.dart
+++ b/tests/ffi/function_gc_test.dart
@@ -16,6 +16,7 @@
import 'dart:ffi' as ffi;
import 'dylib_utils.dart';
import "package:expect/expect.dart";
+import 'ffi_test_helpers.dart';
main() async {
testBoxInt64();
@@ -23,18 +24,13 @@
testBoxDouble();
testBoxPointer();
testAllocateInNative();
- testAllocateInDart();
testRegress37069();
}
-ffi.DynamicLibrary ffiTestFunctions =
- dlopenPlatformSpecific("ffi_test_functions");
-
typedef NativeNullaryOp64 = ffi.Int64 Function();
typedef NativeNullaryOp32 = ffi.Int32 Function();
typedef NativeNullaryOpDouble = ffi.Double Function();
typedef NativeNullaryOpPtr = ffi.Pointer<ffi.Void> Function();
-typedef NativeNullaryOp = ffi.Void Function();
typedef NativeUnaryOp = ffi.Void Function(ffi.Uint64);
typedef NativeUndenaryOp = ffi.Uint64 Function(
ffi.Uint64,
@@ -52,7 +48,6 @@
typedef NullaryOpDbl = double Function();
typedef NullaryOpPtr = ffi.Pointer<ffi.Void> Function();
typedef UnaryOp = void Function(int);
-typedef NullaryOpVoid = void Function();
typedef UndenaryOp = int Function(
int, int, int, int, int, int, int, int, int, int, int);
@@ -97,12 +92,10 @@
}
}
-final triggerGc = ffiTestFunctions
- .lookupFunction<NativeNullaryOp, NullaryOpVoid>("TriggerGC");
-
// Test GC in the FFI call path by calling a C function which triggers GC
// directly.
void testAllocateInNative() => triggerGc();
+
// This also works as a regression test for 37176.
final regress37069 = ffiTestFunctions
@@ -113,18 +106,3 @@
void testRegress37069() {
regress37069(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11);
}
-
-class C {
- final int i;
- C(this.i);
-}
-
-@pragma("vm:entry-point", "call")
-void testAllocationsInDartHelper() => triggerGc();
-
-final allocateThroughDart = ffiTestFunctions
- .lookupFunction<NativeNullaryOp, NullaryOpVoid>("AllocateThroughDart");
-
-// Test GC in the FFI call path by calling a C function which allocates by
-// calling back into Dart ('testAllocationsInDartHelper').
-void testAllocateInDart() => allocateThroughDart();
diff --git a/tests/ffi/gc_helpers.dart b/tests/ffi/gc_helpers.dart
new file mode 100644
index 0000000..5d14d4a
--- /dev/null
+++ b/tests/ffi/gc_helpers.dart
@@ -0,0 +1,16 @@
+// Copyright (c) 2019, 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.
+//
+// Helpers for tests which trigger GC in delicate places.
+
+import 'dart:ffi' as ffi;
+import 'dylib_utils.dart';
+
+typedef NativeNullaryOp = ffi.Void Function();
+typedef NullaryOpVoid = void Function();
+
+final ffi.DynamicLibrary ffiTestFunctions =
+ dlopenPlatformSpecific("ffi_test_functions");
+final triggerGc = ffiTestFunctions
+ .lookupFunction<NativeNullaryOp, NullaryOpVoid>("TriggerGC");