[vm] Support FFI on ARM64 and Android.
Change-Id: I33f3fb1dbf5a4aee4eaea08d0ca51b60114c8680
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/97109
Commit-Queue: Samir Jindel <sjindel@google.com>
Reviewed-by: Aart Bik <ajcbik@google.com>
Reviewed-by: Daco Harkes <dacoharkes@google.com>
diff --git a/runtime/lib/ffi_dynamic_library.cc b/runtime/lib/ffi_dynamic_library.cc
index af4a52e..4bd67e2 100644
--- a/runtime/lib/ffi_dynamic_library.cc
+++ b/runtime/lib/ffi_dynamic_library.cc
@@ -2,7 +2,8 @@
// 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.
-#if !defined(TARGET_OS_LINUX) && !defined(TARGET_OS_MACOS)
+#if !defined(TARGET_OS_LINUX) && !defined(TARGET_OS_MACOS) && \
+ !defined(TARGET_OS_ANDROID)
// TODO(dacoharkes): Implement dynamic libraries for other targets & merge the
// implementation with:
// - runtime/bin/extensions.h
@@ -20,7 +21,8 @@
namespace dart {
static void* LoadExtensionLibrary(const char* library_file) {
-#if defined(TARGET_OS_LINUX) || defined(TARGET_OS_MACOS)
+#if defined(TARGET_OS_LINUX) || defined(TARGET_OS_MACOS) || \
+ defined(TARGET_OS_ANDROID)
void* handle = dlopen(library_file, RTLD_LAZY);
if (handle == nullptr) {
char* error = dlerror();
@@ -68,7 +70,8 @@
}
static void* ResolveSymbol(void* handle, const char* symbol) {
-#if defined(TARGET_OS_LINUX) || defined(TARGET_OS_MACOS)
+#if defined(TARGET_OS_LINUX) || defined(TARGET_OS_MACOS) || \
+ defined(TARGET_OS_ANDROID)
dlerror(); // Clear any errors.
void* pointer = dlsym(handle, symbol);
if (pointer == nullptr) {
diff --git a/runtime/vm/compiler/backend/il.cc b/runtime/vm/compiler/backend/il.cc
index 5f1a3d6..d89e51c 100644
--- a/runtime/vm/compiler/backend/il.cc
+++ b/runtime/vm/compiler/backend/il.cc
@@ -5193,7 +5193,8 @@
set_native_c_function(native_function);
}
-#if defined(TARGET_ARCH_X64) || defined(TARGET_ARCH_IA32)
+#if defined(TARGET_ARCH_X64) || defined(TARGET_ARCH_ARM64) || \
+ defined(TARGET_ARCH_IA32)
#define Z zone_
@@ -5212,7 +5213,7 @@
ASSERT(((1 << CallingConventions::kFirstCalleeSavedCpuReg) &
CallingConventions::kArgumentRegisters) == 0);
-#if defined(TARGET_ARCH_IA32)
+#if defined(TARGET_ARCH_ARM64) || defined(TARGET_ARCH_IA32)
constexpr intptr_t kNumTemps = 2;
#else
constexpr intptr_t kNumTemps = 1;
@@ -5227,7 +5228,7 @@
CallingConventions::kFirstNonArgumentRegister));
summary->set_temp(0, Location::RegisterLocation(
CallingConventions::kSecondNonArgumentRegister));
-#if defined(TARGET_ARCH_IA32)
+#if defined(TARGET_ARCH_IA32) || defined(TARGET_ARCH_ARM64)
summary->set_temp(1, Location::RegisterLocation(
CallingConventions::kFirstCalleeSavedCpuReg));
#endif
diff --git a/runtime/vm/compiler/backend/il_arm64.cc b/runtime/vm/compiler/backend/il_arm64.cc
index 6a4649d..a7807d9 100644
--- a/runtime/vm/compiler/backend/il_arm64.cc
+++ b/runtime/vm/compiler/backend/il_arm64.cc
@@ -873,7 +873,102 @@
}
void FfiCallInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
- UNREACHABLE();
+ Register saved_fp = locs()->temp(0).reg();
+ Register temp = locs()->temp(1).reg();
+ 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);
+
+ // 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);
+
+ // Save exit frame information to enable stack walking as we are about
+ // to transition to Dart VM C++ code.
+ __ StoreToOffset(FPREG, THR,
+ compiler::target::Thread::top_exit_frame_info_offset());
+
+ // Make space for arguments and align the frame.
+ __ ReserveAlignedFrameSpace(compiler::ffi::NumStackSlots(arg_locations_) *
+ kWordSize);
+
+ for (intptr_t i = 0, n = NativeArgCount(); i < n; ++i) {
+ Location origin = locs()->in(i);
+ Location target = arg_locations_[i];
+
+ if (target.IsStackSlot()) {
+ if (origin.IsRegister()) {
+ __ StoreToOffset(origin.reg(), SPREG, target.ToStackSlotOffset());
+ } else if (origin.IsFpuRegister()) {
+ __ StoreDToOffset(origin.fpu_reg(), SPREG, target.ToStackSlotOffset());
+ } else if (origin.IsStackSlot() || origin.IsDoubleStackSlot()) {
+ // The base register cannot be SPREG because we've moved it.
+ ASSERT(origin.base_reg() == FPREG);
+ __ LoadFromOffset(TMP, saved_fp, origin.ToStackSlotOffset());
+ __ StoreToOffset(TMP, SPREG, target.ToStackSlotOffset());
+ }
+ } else {
+ ASSERT(origin.Equals(target));
+ }
+ }
+
+ // Mark that the thread is executing VM code.
+ __ StoreToOffset(branch, THR, compiler::target::Thread::vm_tag_offset());
+
+ // We need to copy the return address up into the dummy stack frame so the
+ // stack walker will know which safepoint to use.
+ const intptr_t call_sequence_start = __ CodeSize();
+
+ // 5 instructions, 4 bytes each.
+ constexpr intptr_t kCallSequenceLength = 5 * 4;
+
+ __ adr(temp, Immediate(kCallSequenceLength));
+ __ StoreToOffset(temp, FPREG, kSavedCallerPcSlotFromFp * kWordSize);
+
+ // We are entering runtime code, so the C stack pointer must be restored from
+ // the stack limit to the top of the stack. We cache the stack limit address
+ // in a callee-saved register.
+ __ mov(temp, CSP);
+ __ mov(CSP, SP);
+
+ __ blr(branch);
+
+ ASSERT(__ CodeSize() - call_sequence_start == kCallSequenceLength);
+
+ // Restore the Dart stack pointer and the saved C stack pointer.
+ __ mov(SP, CSP);
+ __ mov(CSP, temp);
+
+ compiler->EmitCallsiteMetadata(token_pos(), DeoptId::kNone,
+ RawPcDescriptors::Kind::kOther, locs());
+
+ // Mark that the thread is executing Dart code.
+ __ LoadImmediate(temp, VMTag::kDartCompiledTagId);
+ __ StoreToOffset(temp, THR, compiler::target::Thread::vm_tag_offset());
+
+ // Reset exit frame information in Isolate structure.
+ __ StoreToOffset(ZR, THR,
+ compiler::target::Thread::top_exit_frame_info_offset());
+
+ // Refresh write barrier mask.
+ __ ldr(BARRIER_MASK,
+ Address(THR, compiler::target::Thread::write_barrier_mask_offset()));
+
+ // 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) {
+ __ ldr(PP, Address(THR, Thread::global_object_pool_offset()));
+ __ sub(PP, PP, Operand(kHeapObjectTag)); // Pool in PP is untagged!
+ }
+
+ __ set_constant_pool_allowed(true);
}
LocationSummary* OneByteStringFromCharCodeInstr::MakeLocationSummary(
diff --git a/runtime/vm/compiler/ffi.cc b/runtime/vm/compiler/ffi.cc
index a92d033..0c75e40 100644
--- a/runtime/vm/compiler/ffi.cc
+++ b/runtime/vm/compiler/ffi.cc
@@ -11,7 +11,8 @@
namespace ffi {
-#if defined(TARGET_ARCH_X64) || defined(TARGET_ARCH_IA32)
+#if defined(TARGET_ARCH_X64) || defined(TARGET_ARCH_ARM64) || \
+ defined(TARGET_ARCH_IA32)
static const size_t kSizeUnknown = 0;
@@ -127,7 +128,7 @@
Location result = Location::RegisterLocation(
CallingConventions::ArgumentRegisters[cpu_regs_used]);
cpu_regs_used++;
- if (CallingConventions::kArgumentIntRegXorXmmReg) {
+ if (CallingConventions::kArgumentIntRegXorFpuReg) {
fpu_regs_used++;
}
return result;
@@ -135,11 +136,11 @@
break;
case kUnboxedFloat:
case kUnboxedDouble:
- if (fpu_regs_used < CallingConventions::kNumXmmArgRegs) {
+ if (fpu_regs_used < CallingConventions::kNumFpuArgRegs) {
Location result = Location::FpuRegisterLocation(
- CallingConventions::XmmArgumentRegisters[fpu_regs_used]);
+ CallingConventions::FpuArgumentRegisters[fpu_regs_used]);
fpu_regs_used++;
- if (CallingConventions::kArgumentIntRegXorXmmReg) {
+ if (CallingConventions::kArgumentIntRegXorFpuReg) {
cpu_regs_used++;
}
return result;
diff --git a/runtime/vm/compiler/frontend/kernel_to_il.cc b/runtime/vm/compiler/frontend/kernel_to_il.cc
index 169355a..59e1498 100644
--- a/runtime/vm/compiler/frontend/kernel_to_il.cc
+++ b/runtime/vm/compiler/frontend/kernel_to_il.cc
@@ -2485,7 +2485,8 @@
FlowGraph* FlowGraphBuilder::BuildGraphOfFfiTrampoline(
const Function& function) {
-#if !defined(TARGET_ARCH_X64) && !defined(TARGET_ARCH_IA32)
+#if !defined(TARGET_ARCH_X64) && !defined(TARGET_ARCH_ARM64) && \
+ !defined(TARGET_ARCH_IA32)
UNREACHABLE();
#else
graph_entry_ =
diff --git a/runtime/vm/constants_arm64.cc b/runtime/vm/constants_arm64.cc
new file mode 100644
index 0000000..0714ae3
--- /dev/null
+++ b/runtime/vm/constants_arm64.cc
@@ -0,0 +1,21 @@
+// 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.
+
+#if defined(TARGET_ARCH_ARM64)
+
+#include "vm/constants_arm64.h"
+
+namespace dart {
+
+const Register CallingConventions::ArgumentRegisters[] = {
+ R0, R1, R2, R3, R4, R5, R6, R7,
+};
+
+const FpuRegister CallingConventions::FpuArgumentRegisters[] = {
+ V0, V1, V2, V3, V4, V5, V6, V7,
+};
+
+} // namespace dart
+
+#endif
diff --git a/runtime/vm/constants_arm64.h b/runtime/vm/constants_arm64.h
index 0e2d6ea..fa592e1 100644
--- a/runtime/vm/constants_arm64.h
+++ b/runtime/vm/constants_arm64.h
@@ -137,6 +137,9 @@
typedef uint32_t RegList;
const RegList kAllCpuRegistersList = 0xFFFFFFFF;
+// See "Procedure Call Standard for the ARM 64-bit Architecture", document
+// number "ARM IHI 0055B", May 22 2013.
+
// C++ ABI call registers.
const RegList kAbiArgumentCpuRegs = (1 << R0) | (1 << R1) | (1 << R2) |
(1 << R3) | (1 << R4) | (1 << R5) |
@@ -173,6 +176,32 @@
constexpr int kStoreBufferWrapperSize = 32;
+#define R(REG) (1 << REG)
+
+class CallingConventions {
+ public:
+ static const intptr_t kArgumentRegisters = kAbiArgumentCpuRegs;
+ static const Register ArgumentRegisters[];
+ static const intptr_t kNumArgRegs = 8;
+
+ static const FpuRegister FpuArgumentRegisters[];
+ static const intptr_t kFpuArgumentRegisters =
+ R(V0) | R(V1) | R(V2) | R(V3) | R(V4) | R(V5) | R(V6) | R(V7);
+ static const intptr_t kNumFpuArgRegs = 8;
+
+ static const bool kArgumentIntRegXorFpuReg = false;
+
+ static constexpr Register kReturnReg = R0;
+ static constexpr Register kSecondReturnReg = kNoRegister;
+ static constexpr FpuRegister kReturnFpuReg = V0;
+
+ static constexpr Register kFirstCalleeSavedCpuReg = kAbiFirstPreservedCpuReg;
+ static constexpr Register kFirstNonArgumentRegister = R8;
+ static constexpr Register kSecondNonArgumentRegister = R9;
+};
+
+#undef R
+
static inline Register ConcreteRegister(Register r) {
return ((r == ZR) || (r == CSP)) ? R31 : r;
}
diff --git a/runtime/vm/constants_ia32.cc b/runtime/vm/constants_ia32.cc
index 16ae341..62c3f6a 100644
--- a/runtime/vm/constants_ia32.cc
+++ b/runtime/vm/constants_ia32.cc
@@ -8,13 +8,13 @@
namespace dart {
-// Although 'kArgumentRegisters' and 'kXmmArgumentRegisters' are both 0, we have
+// Although 'kArgumentRegisters' and 'kFpuArgumentRegisters' are both 0, we have
// to give these arrays at least one element to appease MSVC.
const Register CallingConventions::ArgumentRegisters[] = {
static_cast<Register>(0)};
-const XmmRegister CallingConventions::XmmArgumentRegisters[] = {
- static_cast<XmmRegister>(0)};
+const FpuRegister CallingConventions::FpuArgumentRegisters[] = {
+ static_cast<FpuRegister>(0)};
} // namespace dart
diff --git a/runtime/vm/constants_ia32.h b/runtime/vm/constants_ia32.h
index 43d3064..24e3783 100644
--- a/runtime/vm/constants_ia32.h
+++ b/runtime/vm/constants_ia32.h
@@ -125,17 +125,17 @@
static const intptr_t kArgumentRegisters = 0;
static const intptr_t kNumArgRegs = 0;
- static const XmmRegister XmmArgumentRegisters[];
+ static const XmmRegister FpuArgumentRegisters[];
static const intptr_t kXmmArgumentRegisters = 0;
- static const intptr_t kNumXmmArgRegs = 0;
+ static const intptr_t kNumFpuArgRegs = 0;
- static const bool kArgumentIntRegXorXmmReg = false;
+ static const bool kArgumentIntRegXorFpuReg = false;
static constexpr Register kReturnReg = EAX;
static constexpr Register kSecondReturnReg = EDX;
// Floating point values are returned on the "FPU stack" (in "ST" registers).
- static constexpr XmmRegister kReturnXmmReg = kNoXmmRegister;
+ static constexpr XmmRegister kReturnFpuReg = kNoXmmRegister;
static constexpr Register kFirstCalleeSavedCpuReg = EBX;
static constexpr Register kFirstNonArgumentRegister = EAX;
diff --git a/runtime/vm/constants_x64.cc b/runtime/vm/constants_x64.cc
index 397977e..3f38d9d 100644
--- a/runtime/vm/constants_x64.cc
+++ b/runtime/vm/constants_x64.cc
@@ -13,7 +13,7 @@
CallingConventions::kArg1Reg, CallingConventions::kArg2Reg,
CallingConventions::kArg3Reg, CallingConventions::kArg4Reg};
-const XmmRegister CallingConventions::XmmArgumentRegisters[] = {
+const XmmRegister CallingConventions::FpuArgumentRegisters[] = {
XmmRegister::XMM0, XmmRegister::XMM1, XmmRegister::XMM2, XmmRegister::XMM3};
#else
const Register CallingConventions::ArgumentRegisters[] = {
@@ -21,7 +21,7 @@
CallingConventions::kArg3Reg, CallingConventions::kArg4Reg,
CallingConventions::kArg5Reg, CallingConventions::kArg6Reg};
-const XmmRegister CallingConventions::XmmArgumentRegisters[] = {
+const XmmRegister CallingConventions::FpuArgumentRegisters[] = {
XmmRegister::XMM0, XmmRegister::XMM1, XmmRegister::XMM2, XmmRegister::XMM3,
XmmRegister::XMM4, XmmRegister::XMM5, XmmRegister::XMM6, XmmRegister::XMM7};
#endif
diff --git a/runtime/vm/constants_x64.h b/runtime/vm/constants_x64.h
index cbaedaa..3e8d0070 100644
--- a/runtime/vm/constants_x64.h
+++ b/runtime/vm/constants_x64.h
@@ -157,14 +157,14 @@
R(kArg1Reg) | R(kArg2Reg) | R(kArg3Reg) | R(kArg4Reg);
static const intptr_t kNumArgRegs = 4;
- static const XmmRegister XmmArgumentRegisters[];
- static const intptr_t kXmmArgumentRegisters =
+ static const XmmRegister FpuArgumentRegisters[];
+ static const intptr_t kFpuArgumentRegisters =
R(XMM0) | R(XMM1) | R(XMM2) | R(XMM3);
- static const intptr_t kNumXmmArgRegs = 4;
+ static const intptr_t kNumFpuArgRegs = 4;
// can ArgumentRegisters[i] and XmmArgumentRegisters[i] both be used at the
// same time? (Windows no, rest yes)
- static const bool kArgumentIntRegXorXmmReg = true;
+ static const bool kArgumentIntRegXorFpuReg = true;
static const intptr_t kShadowSpaceBytes = 4 * kWordSize;
@@ -203,15 +203,15 @@
R(kArg5Reg) | R(kArg6Reg);
static const intptr_t kNumArgRegs = 6;
- static const XmmRegister XmmArgumentRegisters[];
+ static const XmmRegister FpuArgumentRegisters[];
static const intptr_t kXmmArgumentRegisters = R(XMM0) | R(XMM1) | R(XMM2) |
R(XMM3) | R(XMM4) | R(XMM5) |
R(XMM6) | R(XMM7);
- static const intptr_t kNumXmmArgRegs = 8;
+ static const intptr_t kNumFpuArgRegs = 8;
// can ArgumentRegisters[i] and XmmArgumentRegisters[i] both be used at the
// same time? (Windows no, rest yes)
- static const bool kArgumentIntRegXorXmmReg = false;
+ static const bool kArgumentIntRegXorFpuReg = false;
static const intptr_t kShadowSpaceBytes = 0;
diff --git a/runtime/vm/dart_api_impl.h b/runtime/vm/dart_api_impl.h
index 0fa7622..202143e 100644
--- a/runtime/vm/dart_api_impl.h
+++ b/runtime/vm/dart_api_impl.h
@@ -297,12 +297,13 @@
static bool IsFfiEnabled() {
// dart:ffi is not implemented for the following configurations
-#if !defined(TARGET_ARCH_X64) && !defined(TARGET_ARCH_IA32)
+#if !defined(TARGET_ARCH_X64) && !defined(TARGET_ARCH_ARM64) && \
+ !defined(TARGET_ARCH_IA32)
// https://github.com/dart-lang/sdk/issues/35760 Arm32 && Android
// https://github.com/dart-lang/sdk/issues/35772 Arm64
return false;
#elif !defined(TARGET_OS_LINUX) && !defined(TARGET_OS_MACOS) && \
- !defined(TARGET_OS_WINDOWS)
+ !defined(TARGET_OS_ANDROID) && !defined(TARGET_OS_WINDOWS)
// https://github.com/dart-lang/sdk/issues/35760 Arm32 && Android
// https://github.com/dart-lang/sdk/issues/35772 Arm64
// https://github.com/dart-lang/sdk/issues/35773 DBC
diff --git a/runtime/vm/vm_sources.gni b/runtime/vm/vm_sources.gni
index 7fb6f1f..eadae4d 100644
--- a/runtime/vm/vm_sources.gni
+++ b/runtime/vm/vm_sources.gni
@@ -43,6 +43,7 @@
"compilation_trace.cc",
"compilation_trace.h",
"constants_arm.h",
+ "constants_arm64.cc",
"constants_arm64.h",
"constants_dbc.h",
"constants_ia32.h",
diff --git a/tests/standalone_2/ffi/dylib_utils.dart b/tests/standalone_2/ffi/dylib_utils.dart
index 1c924d4..fb8153a 100644
--- a/tests/standalone_2/ffi/dylib_utils.dart
+++ b/tests/standalone_2/ffi/dylib_utils.dart
@@ -7,7 +7,8 @@
String _platformPath(String name, {String path}) {
if (path == null) path = "";
- if (Platform.isLinux) return path + "lib" + name + ".so";
+ if (Platform.isLinux || Platform.isAndroid)
+ return path + "lib" + name + ".so";
if (Platform.isMacOS) return path + "lib" + name + ".dylib";
if (Platform.isWindows) return path + name + ".dll";
throw Exception("Platform not implemented");
diff --git a/tests/standalone_2/ffi/gc_helper.dart b/tests/standalone_2/ffi/gc_helper.dart
index 5e9431d..182e1df 100644
--- a/tests/standalone_2/ffi/gc_helper.dart
+++ b/tests/standalone_2/ffi/gc_helper.dart
@@ -12,8 +12,9 @@
abstract class GCWatcher {
factory GCWatcher() => _GCWatcherImpl();
factory GCWatcher.dummy() => _MockGCWatcher();
- factory GCWatcher.ifAvailable() =>
- Platform.isWindows ? GCWatcher.dummy() : GCWatcher();
+ factory GCWatcher.ifAvailable() => (Platform.isWindows || Platform.isAndroid)
+ ? GCWatcher.dummy()
+ : GCWatcher();
Future<int> size();
void dispose();
diff --git a/tests/standalone_2/standalone_2_kernel.status b/tests/standalone_2/standalone_2_kernel.status
index 75ee38a..fdbc2ab 100644
--- a/tests/standalone_2/standalone_2_kernel.status
+++ b/tests/standalone_2/standalone_2_kernel.status
@@ -244,11 +244,11 @@
io/web_socket_compression_test: Skip # Timeout
io/web_socket_test: Skip # Timeout
-[ $compiler != dartk || $mode == product || $arch != ia32 && $arch != x64 || $system != linux && $system != macos && $system != windows ]
+[ $compiler != dartk || $mode == product || $arch != arm64 && $arch != ia32 && $arch != x64 || $system != android && $system != linux && $system != macos && $system != windows ]
ffi/function_stress_test: SkipByDesign # FFI must be supported. Also requires --verbose-gc, which isn't included in product.
ffi/subtype_test: SkipByDesign # FFI must be supported. Also requires --verbose-gc, which isn't included in product.
-[ $arch != ia32 && $arch != x64 || $system != linux && $system != macos && $system != windows ]
+[ $arch != arm64 && $arch != ia32 && $arch != x64 || $system != android && $system != linux && $system != macos && $system != windows ]
ffi: Skip # ffi not yet supported on other systems than linux/macos/windows x64/ia32
[ $compiler != dartk && $compiler != dartkb && $compiler != dartkp || $compiler == dartkp && $system == windows ]