[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");