// 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. #include "lib/ffi.h" #include "include/dart_api.h" #include "platform/globals.h" #include "vm/bootstrap_natives.h" #include "vm/class_finalizer.h" #include "vm/compiler/assembler/assembler.h" #include "vm/compiler/ffi.h" #include "vm/compiler/jit/compiler.h" #include "vm/exceptions.h" #include "vm/log.h" #include "vm/native_arguments.h" #include "vm/native_entry.h" #include "vm/object.h" #include "vm/object_store.h" #include "vm/symbols.h" namespace dart { // The following functions are runtime checks on type arguments. // Some checks are also performed in kernel transformation, these are asserts. // Some checks are only performed at runtime to allow for generic code, these // throw ArgumentExceptions. static void ThrowTypeArgumentError(const AbstractType& type_arg, const char* expected) { const String& error = String::Handle(String::NewFormatted( "Type argument (%s) should be a %s", String::Handle(type_arg.UserVisibleName()).ToCString(), expected)); Exceptions::ThrowArgumentError(error); } static bool IsPointerType(const AbstractType& type) { // Do a fast check for predefined types. classid_t type_cid = type.type_class_id(); if (RawObject::IsFfiPointerClassId(type_cid)) { return true; } // Do a slow check for subtyping. const Class& pointer_class = Class::Handle(Isolate::Current()->object_store()->ffi_pointer_class()); AbstractType& pointer_type = AbstractType::Handle(pointer_class.DeclarationType()); pointer_type = pointer_type.InstantiateFrom(Object::null_type_arguments(), Object::null_type_arguments(), kNoneFree, NULL, Heap::kNew); ASSERT(pointer_type.IsInstantiated()); ASSERT(type.IsInstantiated()); return type.IsSubtypeOf(pointer_type, Heap::kNew); } static bool IsConcreteNativeType(const AbstractType& type) { // Do a fast check for predefined types. classid_t type_cid = type.type_class_id(); if (RawObject::IsFfiNativeTypeTypeClassId(type_cid)) { return false; } if (RawObject::IsFfiTypeClassId(type_cid)) { return true; } // Do a slow check for subtyping. const Class& native_type_class = Class::Handle( Isolate::Current()->object_store()->ffi_native_type_class()); AbstractType& native_type_type = AbstractType::Handle(native_type_class.DeclarationType()); return type.IsSubtypeOf(native_type_type, Heap::kNew); } static void CheckIsConcreteNativeType(const AbstractType& type) { if (!IsConcreteNativeType(type)) { ThrowTypeArgumentError(type, "concrete sub type of NativeType"); } } static bool IsNativeFunction(const AbstractType& type_arg) { classid_t type_cid = type_arg.type_class_id(); return RawObject::IsFfiTypeNativeFunctionClassId(type_cid); } static void CheckSized(const AbstractType& type_arg) { classid_t type_cid = type_arg.type_class_id(); if (RawObject::IsFfiTypeVoidClassId(type_cid) || RawObject::IsFfiTypeNativeFunctionClassId(type_cid)) { const String& error = String::Handle(String::NewFormatted( "%s does not have a predefined size (@unsized). " "Unsized NativeTypes do not support [sizeOf] because their size " "is unknown. " "Consequently, [allocate], [Pointer.load], [Pointer.store], and " "[Pointer.elementAt] are not available.", String::Handle(type_arg.UserVisibleName()).ToCString())); Exceptions::ThrowArgumentError(error); } } // Checks that a dart type correspond to a [NativeType]. // Because this is checked already in a kernel transformation, it does not throw // an ArgumentException but a boolean which should be asserted. // // [Int8] -> [int] // [Int16] -> [int] // [Int32] -> [int] // [Int64] -> [int] // [Uint8] -> [int] // [Uint16] -> [int] // [Uint32] -> [int] // [Uint64] -> [int] // [IntPtr] -> [int] // [Double] -> [double] // [Float] -> [double] // [Pointer] -> [Pointer] // T extends [Pointer] -> T // [NativeFunction] S1 Function(S2, S3) // where DartRepresentationOf(Tn) -> Sn static bool DartAndCTypeCorrespond(const AbstractType& native_type, const AbstractType& dart_type) { classid_t native_type_cid = native_type.type_class_id(); if (RawObject::IsFfiTypeIntClassId(native_type_cid)) { return dart_type.IsSubtypeOf(AbstractType::Handle(Type::IntType()), Heap::kNew); } if (RawObject::IsFfiTypeDoubleClassId(native_type_cid)) { return dart_type.IsSubtypeOf(AbstractType::Handle(Type::Double()), Heap::kNew); } if (RawObject::IsFfiPointerClassId(native_type_cid)) { return native_type.Equals(dart_type) || dart_type.IsNullType(); } if (RawObject::IsFfiTypeNativeFunctionClassId(native_type_cid)) { if (!dart_type.IsFunctionType()) { return false; } TypeArguments& nativefunction_type_args = TypeArguments::Handle(native_type.arguments()); AbstractType& nativefunction_type_arg = AbstractType::Handle(nativefunction_type_args.TypeAt(0)); if (!nativefunction_type_arg.IsFunctionType()) { return false; } Function& dart_function = Function::Handle(((Type&)dart_type).signature()); if (dart_function.NumTypeParameters() != 0 || dart_function.HasOptionalPositionalParameters() || dart_function.HasOptionalNamedParameters()) { return false; } Function& nativefunction_function = Function::Handle(((Type&)nativefunction_type_arg).signature()); if (nativefunction_function.NumTypeParameters() != 0 || nativefunction_function.HasOptionalPositionalParameters() || nativefunction_function.HasOptionalNamedParameters()) { return false; } if (!(dart_function.NumParameters() == nativefunction_function.NumParameters())) { return false; } if (!DartAndCTypeCorrespond( AbstractType::Handle(nativefunction_function.result_type()), AbstractType::Handle(dart_function.result_type()))) { return false; } for (intptr_t i = 0; i < dart_function.NumParameters(); i++) { if (!DartAndCTypeCorrespond( AbstractType::Handle(nativefunction_function.ParameterTypeAt(i)), AbstractType::Handle(dart_function.ParameterTypeAt(i)))) { return false; } } } return true; } // The following functions are runtime checks on arguments. // Note that expected_from and expected_to are inclusive. static void CheckRange(const Integer& argument_value, intptr_t expected_from, intptr_t expected_to, const char* argument_name) { int64_t value = argument_value.AsInt64Value(); if (value < expected_from || expected_to < value) { Exceptions::ThrowRangeError(argument_name, argument_value, expected_from, expected_to); } } static const Pointer& AsPointer(const Instance& instance) { if (!instance.IsPointer()) { const String& error = String::Handle(String::NewFormatted( "Expected a Pointer object but found %s", instance.ToCString())); Exceptions::ThrowArgumentError(error); } return Pointer::Cast(instance); } static const Integer& AsInteger(const Instance& instance) { if (!instance.IsInteger()) { const String& error = String::Handle(String::NewFormatted( "Expected an int but found %s", instance.ToCString())); Exceptions::ThrowArgumentError(error); } return Integer::Cast(instance); } static const Double& AsDouble(const Instance& instance) { if (!instance.IsDouble()) { const String& error = String::Handle(String::NewFormatted( "Expected a double but found %s", instance.ToCString())); Exceptions::ThrowArgumentError(error); } return Double::Cast(instance); } // The remainder of this file implements the dart:ffi native methods. DEFINE_NATIVE_ENTRY(Ffi_allocate, 1, 1) { // TODO(dacoharkes): When we have a way of determining the size of structs in // the VM, change the signature so we can allocate structs, subtype of // Pointer. https://github.com/dart-lang/sdk/issues/35782 GET_NATIVE_TYPE_ARGUMENT(type_arg, arguments->NativeTypeArgAt(0)); CheckIsConcreteNativeType(type_arg); CheckSized(type_arg); GET_NON_NULL_NATIVE_ARGUMENT(Integer, argCount, arguments->NativeArgAt(0)); int64_t count = argCount.AsInt64Value(); classid_t type_cid = type_arg.type_class_id(); int64_t max_count = INTPTR_MAX / compiler::ffi::ElementSizeInBytes(type_cid); CheckRange(argCount, 1, max_count, "count"); size_t size = compiler::ffi::ElementSizeInBytes(type_cid) * count; uint64_t memory = reinterpret_cast(malloc(size)); if (memory == 0) { const String& error = String::Handle(String::NewFormatted( "allocating (%" Pd ") bytes of memory failed", size)); Exceptions::ThrowArgumentError(error); } RawPointer* result = Pointer::New( type_arg, Integer::Handle(zone, Integer::NewFromUint64(memory))); return result; } DEFINE_NATIVE_ENTRY(Ffi_fromAddress, 1, 1) { GET_NATIVE_TYPE_ARGUMENT(type_arg, arguments->NativeTypeArgAt(0)); TypeArguments& type_args = TypeArguments::Handle(type_arg.arguments()); AbstractType& native_type = AbstractType::Handle( type_args.TypeAtNullSafe(Pointer::kNativeTypeArgPos)); CheckIsConcreteNativeType(native_type); GET_NON_NULL_NATIVE_ARGUMENT(Integer, arg_ptr, arguments->NativeArgAt(0)); // TODO(dacoharkes): should this return NULL if address is 0? // https://github.com/dart-lang/sdk/issues/35756 RawPointer* result = Pointer::New(native_type, arg_ptr, type_arg.type_class_id()); return result; } DEFINE_NATIVE_ENTRY(Ffi_elementAt, 0, 2) { GET_NON_NULL_NATIVE_ARGUMENT(Pointer, pointer, arguments->NativeArgAt(0)); GET_NON_NULL_NATIVE_ARGUMENT(Integer, index, arguments->NativeArgAt(1)); AbstractType& pointer_type_arg = AbstractType::Handle(zone, pointer.type_argument()); CheckSized(pointer_type_arg); classid_t class_id = pointer_type_arg.type_class_id(); Integer& address = Integer::Handle(zone, pointer.GetCMemoryAddress()); address = Integer::New(address.AsInt64Value() + index.AsInt64Value() * compiler::ffi::ElementSizeInBytes(class_id)); RawPointer* result = Pointer::New(pointer_type_arg, address); return result; } DEFINE_NATIVE_ENTRY(Ffi_offsetBy, 0, 2) { GET_NON_NULL_NATIVE_ARGUMENT(Pointer, pointer, arguments->NativeArgAt(0)); GET_NON_NULL_NATIVE_ARGUMENT(Integer, offset, arguments->NativeArgAt(1)); AbstractType& pointer_type_arg = AbstractType::Handle(pointer.type_argument()); intptr_t address = Integer::Handle(zone, pointer.GetCMemoryAddress()).AsInt64Value() + offset.AsInt64Value(); RawPointer* result = Pointer::New( pointer_type_arg, Integer::Handle(zone, Integer::New(address))); return result; } DEFINE_NATIVE_ENTRY(Ffi_cast, 1, 1) { GET_NON_NULL_NATIVE_ARGUMENT(Pointer, pointer, arguments->NativeArgAt(0)); GET_NATIVE_TYPE_ARGUMENT(type_arg, arguments->NativeTypeArgAt(0)); TypeArguments& type_args = TypeArguments::Handle(type_arg.arguments()); AbstractType& native_type = AbstractType::Handle( type_args.TypeAtNullSafe(Pointer::kNativeTypeArgPos)); CheckIsConcreteNativeType(native_type); const Integer& address = Integer::Handle(zone, pointer.GetCMemoryAddress()); RawPointer* result = Pointer::New(native_type, address, type_arg.type_class_id()); return result; } DEFINE_NATIVE_ENTRY(Ffi_free, 0, 1) { GET_NON_NULL_NATIVE_ARGUMENT(Pointer, pointer, arguments->NativeArgAt(0)); const Integer& address = Integer::Handle(zone, pointer.GetCMemoryAddress()); free(reinterpret_cast(address.AsInt64Value())); pointer.SetCMemoryAddress(Integer::Handle(zone, Integer::New(0))); return Object::null(); } DEFINE_NATIVE_ENTRY(Ffi_address, 0, 1) { GET_NON_NULL_NATIVE_ARGUMENT(Pointer, pointer, arguments->NativeArgAt(0)); return pointer.GetCMemoryAddress(); } static RawInstance* BoxLoadPointer(Zone* zone, uint8_t* address, const AbstractType& instance_type_arg, intptr_t type_cid) { // TODO(dacoharkes): should this return NULL if addres is 0? // https://github.com/dart-lang/sdk/issues/35756 if (address == nullptr) { return Instance::null(); } AbstractType& type_arg = AbstractType::Handle(TypeArguments::Handle(instance_type_arg.arguments()) .TypeAt(Pointer::kNativeTypeArgPos)); return Pointer::New( type_arg, Integer::Handle(zone, Integer::New(reinterpret_cast(address))), type_cid); } static RawInstance* LoadValue(Zone* zone, uint8_t* address, const AbstractType& instance_type_arg) { classid_t type_cid = instance_type_arg.type_class_id(); switch (type_cid) { case kFfiInt8Cid: return Integer::New(*reinterpret_cast(address)); case kFfiInt16Cid: return Integer::New(*reinterpret_cast(address)); case kFfiInt32Cid: return Integer::New(*reinterpret_cast(address)); case kFfiInt64Cid: return Integer::New(*reinterpret_cast(address)); case kFfiUint8Cid: return Integer::NewFromUint64(*reinterpret_cast(address)); case kFfiUint16Cid: return Integer::NewFromUint64(*reinterpret_cast(address)); case kFfiUint32Cid: return Integer::NewFromUint64(*reinterpret_cast(address)); case kFfiUint64Cid: return Integer::NewFromUint64(*reinterpret_cast(address)); case kFfiIntPtrCid: return Integer::New(*reinterpret_cast(address)); case kFfiFloatCid: return Double::New(*reinterpret_cast(address)); case kFfiDoubleCid: return Double::New(*reinterpret_cast(address)); case kFfiPointerCid: default: ASSERT(IsPointerType(instance_type_arg)); return BoxLoadPointer(zone, *reinterpret_cast(address), instance_type_arg, type_cid); } } DEFINE_NATIVE_ENTRY(Ffi_load, 1, 1) { GET_NON_NULL_NATIVE_ARGUMENT(Pointer, pointer, arguments->NativeArgAt(0)); GET_NATIVE_TYPE_ARGUMENT(type_arg, arguments->NativeTypeArgAt(0)); AbstractType& pointer_type_arg = AbstractType::Handle(pointer.type_argument()); CheckSized(pointer_type_arg); ASSERT(DartAndCTypeCorrespond(pointer_type_arg, type_arg)); uint8_t* address = reinterpret_cast( Integer::Handle(pointer.GetCMemoryAddress()).AsInt64Value()); return LoadValue(zone, address, pointer_type_arg); } static void StoreValue(Zone* zone, const Pointer& pointer, classid_t type_cid, const Instance& new_value) { uint8_t* address = reinterpret_cast( Integer::Handle(pointer.GetCMemoryAddress()).AsInt64Value()); AbstractType& pointer_type_arg = AbstractType::Handle(pointer.type_argument()); switch (type_cid) { case kFfiInt8Cid: *reinterpret_cast(address) = AsInteger(new_value).AsInt64Value(); break; case kFfiInt16Cid: *reinterpret_cast(address) = AsInteger(new_value).AsInt64Value(); break; case kFfiInt32Cid: *reinterpret_cast(address) = AsInteger(new_value).AsInt64Value(); break; case kFfiInt64Cid: *reinterpret_cast(address) = AsInteger(new_value).AsInt64Value(); break; case kFfiUint8Cid: *reinterpret_cast(address) = AsInteger(new_value).AsInt64Value(); break; case kFfiUint16Cid: *reinterpret_cast(address) = AsInteger(new_value).AsInt64Value(); break; case kFfiUint32Cid: *reinterpret_cast(address) = AsInteger(new_value).AsInt64Value(); break; case kFfiUint64Cid: *reinterpret_cast(address) = AsInteger(new_value).AsInt64Value(); break; case kFfiIntPtrCid: *reinterpret_cast(address) = AsInteger(new_value).AsInt64Value(); break; case kFfiFloatCid: *reinterpret_cast(address) = AsDouble(new_value).value(); break; case kFfiDoubleCid: *reinterpret_cast(address) = AsDouble(new_value).value(); break; case kFfiPointerCid: default: { ASSERT(IsPointerType(pointer_type_arg)); intptr_t new_value_unwrapped = 0; if (!new_value.IsNull()) { ASSERT(new_value.IsPointer()); new_value_unwrapped = Integer::Handle(AsPointer(new_value).GetCMemoryAddress()) .AsInt64Value(); // TODO(dacoharkes): should this return NULL if addres is 0? // https://github.com/dart-lang/sdk/issues/35756 } *reinterpret_cast(address) = new_value_unwrapped; } break; } } DEFINE_NATIVE_ENTRY(Ffi_store, 0, 2) { GET_NON_NULL_NATIVE_ARGUMENT(Pointer, pointer, arguments->NativeArgAt(0)); GET_NATIVE_ARGUMENT(Instance, new_value, arguments->NativeArgAt(1)); AbstractType& arg_type = AbstractType::Handle(new_value.GetType(Heap::kNew)); AbstractType& pointer_type_arg = AbstractType::Handle(pointer.type_argument()); CheckSized(pointer_type_arg); ASSERT(DartAndCTypeCorrespond(pointer_type_arg, arg_type)); classid_t type_cid = pointer_type_arg.type_class_id(); StoreValue(zone, pointer, type_cid, new_value); return Object::null(); } DEFINE_NATIVE_ENTRY(Ffi_sizeOf, 1, 0) { GET_NATIVE_TYPE_ARGUMENT(type_arg, arguments->NativeTypeArgAt(0)); CheckIsConcreteNativeType(type_arg); CheckSized(type_arg); classid_t type_cid = type_arg.type_class_id(); return Smi::New(compiler::ffi::ElementSizeInBytes(type_cid)); } // TODO(dacoharkes): Cache the trampolines. // We can possibly address simultaniously with 'precaching' in AOT. static RawFunction* TrampolineFunction(const Function& dart_signature, const Function& c_signature) { Thread* thread = Thread::Current(); Zone* zone = thread->zone(); String& name = String::ZoneHandle(Symbols::New(Thread::Current(), "FfiTrampoline")); const Library& lib = Library::Handle(Library::FfiLibrary()); const Class& owner_class = Class::Handle(lib.toplevel_class()); Function& function = Function::Handle(zone, Function::New(name, RawFunction::kFfiTrampoline, /*is_static=*/true, /*is_const=*/false, /*is_abstract=*/false, /*is_external=*/false, /*is_native=*/false, owner_class, TokenPosition::kMinSource)); function.set_is_debuggable(false); function.set_num_fixed_parameters(dart_signature.num_fixed_parameters()); function.set_result_type(AbstractType::Handle(dart_signature.result_type())); function.set_parameter_types(Array::Handle(dart_signature.parameter_types())); // The signature function won't have any names for the parameters. We need to // assign unique names for scope building and error messages. const intptr_t num_params = dart_signature.num_fixed_parameters(); const Array& parameter_names = Array::Handle(Array::New(num_params)); for (intptr_t i = 0; i < num_params; ++i) { if (i == 0) { name = Symbols::ClosureParameter().raw(); } else { name = Symbols::NewFormatted(thread, ":ffiParam%" Pd, i); } parameter_names.SetAt(i, name); } function.set_parameter_names(parameter_names); function.SetFfiCSignature(c_signature); return function.raw(); } DEFINE_NATIVE_ENTRY(Ffi_asFunction, 1, 1) { GET_NON_NULL_NATIVE_ARGUMENT(Pointer, pointer, arguments->NativeArgAt(0)); AbstractType& pointer_type_arg = AbstractType::Handle(pointer.type_argument()); ASSERT(IsNativeFunction(pointer_type_arg)); GET_NATIVE_TYPE_ARGUMENT(type_arg, arguments->NativeTypeArgAt(0)); ASSERT(DartAndCTypeCorrespond(pointer_type_arg, type_arg)); Function& dart_signature = Function::Handle(Type::Cast(type_arg).signature()); TypeArguments& nativefunction_type_args = TypeArguments::Handle(pointer_type_arg.arguments()); AbstractType& nativefunction_type_arg = AbstractType::Handle(nativefunction_type_args.TypeAt(0)); Function& c_signature = Function::Handle(Type::Cast(nativefunction_type_arg).signature()); Function& function = Function::Handle(TrampolineFunction(dart_signature, c_signature)); // Set the c function pointer in the context of the closure rather than in // the function so that we can reuse the function for each c function with // the same signature. Context& context = Context::Handle(Context::New(1)); context.SetAt(0, Integer::Handle(zone, pointer.GetCMemoryAddress())); RawClosure* raw_closure = Closure::New(Object::null_type_arguments(), Object::null_type_arguments(), function, context, Heap::kOld); return raw_closure; } // Generates assembly to trampoline from native code into Dart. static uword CompileNativeCallback(const Function& c_signature, const Function& dart_target) { #if defined(DART_PRECOMPILED_RUNTIME) || defined(DART_PRECOMPILER) UNREACHABLE(); #elif defined(TARGET_ARCH_DBC) // https://github.com/dart-lang/sdk/issues/35774 // FFI is supported, but callbacks are not. Exceptions::ThrowUnsupportedError( "FFI callbacks are not yet supported on DBC."); #else Thread* const thread = Thread::Current(); const int32_t callback_id = thread->AllocateFfiCallbackId(); // Create a new Function named 'FfiCallback' and stick it in the 'dart:ffi' // library. Note that these functions will never be invoked by Dart, so it // doesn't matter that they all have the same name. Zone* const Z = thread->zone(); const String& name = String::ZoneHandle(Symbols::New(Thread::Current(), "FfiCallback")); const Library& lib = Library::Handle(Library::FfiLibrary()); const Class& owner_class = Class::Handle(lib.toplevel_class()); const Function& function = Function::Handle(Z, Function::New(name, RawFunction::kFfiTrampoline, /*is_static=*/true, /*is_const=*/false, /*is_abstract=*/false, /*is_external=*/false, /*is_native=*/false, owner_class, TokenPosition::kMinSource)); function.set_is_debuggable(false); // Set callback-specific fields which the flow-graph builder needs to generate // the body. function.SetFfiCSignature(c_signature); function.SetFfiCallbackId(callback_id); function.SetFfiCallbackTarget(dart_target); // We compile the callback immediately because we need to return a pointer to // the entry-point. Native calls do not use patching like Dart calls, so we // cannot compile it lazily. const Object& result = Object::Handle(Z, Compiler::CompileOptimizedFunction(thread, function)); if (result.IsError()) { Exceptions::PropagateError(Error::Cast(result)); } ASSERT(result.IsCode()); const Code& code = Code::Cast(result); thread->SetFfiCallbackCode(callback_id, code); return code.EntryPoint(); #endif } DEFINE_NATIVE_ENTRY(Ffi_fromFunction, 1, 1) { GET_NATIVE_TYPE_ARGUMENT(type_arg, arguments->NativeTypeArgAt(0)); GET_NON_NULL_NATIVE_ARGUMENT(Closure, closure, arguments->NativeArgAt(0)); const Function& native_signature = Function::Handle(((Type&)type_arg).signature()); Function& func = Function::Handle(closure.function()); TypeArguments& type_args = TypeArguments::Handle(zone); type_args = TypeArguments::New(1); type_args.SetTypeAt(Pointer::kNativeTypeArgPos, type_arg); type_args = type_args.Canonicalize(); Class& native_function_class = Class::Handle( Isolate::Current()->class_table()->At(kFfiNativeFunctionCid)); native_function_class.EnsureIsFinalized(Thread::Current()); Type& native_function_type = Type::Handle( Type::New(native_function_class, type_args, TokenPosition::kNoSource)); native_function_type ^= ClassFinalizer::FinalizeType(Class::Handle(), native_function_type); native_function_type ^= native_function_type.Canonicalize(); // The FE verifies that the target of a 'fromFunction' is a static method, so // the value we see here must be a static tearoff. See ffi_use_sites.dart for // details. // // TODO(36748): Define hot-reload semantics of native callbacks. We may need // to look up the target by name. ASSERT(func.IsImplicitClosureFunction()); func = func.parent_function(); ASSERT(func.is_static()); const uword address = CompileNativeCallback(native_signature, func); const Pointer& result = Pointer::Handle(Pointer::New( native_function_type, Integer::Handle(zone, Integer::New(address)))); return result.raw(); } #if defined(TARGET_ARCH_DBC) void FfiMarshalledArguments::SetFunctionAddress(uint64_t value) const { data_[kOffsetFunctionAddress] = value; } static intptr_t ArgumentHostRegisterIndex(host::Register reg) { for (intptr_t i = 0; i < host::CallingConventions::kNumArgRegs; i++) { if (host::CallingConventions::ArgumentRegisters[i] == reg) { return i; } } UNREACHABLE(); } void FfiMarshalledArguments::SetRegister(host::Register reg, uint64_t value) const { const intptr_t reg_index = ArgumentHostRegisterIndex(reg); ASSERT(host::CallingConventions::ArgumentRegisters[reg_index] == reg); const intptr_t index = kOffsetRegisters + reg_index; data_[index] = value; } void FfiMarshalledArguments::SetFpuRegister(host::FpuRegister reg, uint64_t value) const { const intptr_t fpu_index = static_cast(reg); ASSERT(host::CallingConventions::FpuArgumentRegisters[fpu_index] == reg); const intptr_t index = kOffsetFpuRegisters + fpu_index; data_[index] = value; } void FfiMarshalledArguments::SetNumStackSlots(intptr_t num_args) const { data_[kOffsetNumStackSlots] = num_args; } void FfiMarshalledArguments::SetAlignmentMask(uint64_t alignment_mask) const { data_[kOffsetAlignmentMask] = alignment_mask; } intptr_t FfiMarshalledArguments::GetNumStackSlots() const { return data_[kOffsetNumStackSlots]; } void FfiMarshalledArguments::SetStackSlotValue(intptr_t index, uint64_t value) const { ASSERT(0 <= index && index < GetNumStackSlots()); data_[kOffsetStackSlotValues + index] = value; } uint64_t* FfiMarshalledArguments::New( const compiler::ffi::FfiSignatureDescriptor& signature, const uint64_t* arg_values) { const intptr_t num_stack_slots = signature.num_stack_slots(); const uint64_t alignment_mask = ~(OS::ActivationFrameAlignment() - 1); const intptr_t size = FfiMarshalledArguments::kOffsetStackSlotValues + num_stack_slots; uint64_t* data = Thread::Current()->GetFfiMarshalledArguments(size); const auto& descr = FfiMarshalledArguments(data); descr.SetFunctionAddress(arg_values[compiler::ffi::kFunctionAddressRegister]); const intptr_t num_args = signature.length(); descr.SetNumStackSlots(num_stack_slots); descr.SetAlignmentMask(alignment_mask); for (int i = 0; i < num_args; i++) { uint64_t arg_value = arg_values[compiler::ffi::kFirstArgumentRegister + i]; HostLocation loc = signature.LocationAt(i); // TODO(36809): For 32 bit, support pair locations. if (loc.IsRegister()) { descr.SetRegister(loc.reg(), arg_value); } else if (loc.IsFpuRegister()) { descr.SetFpuRegister(loc.fpu_reg(), arg_value); } else { ASSERT(loc.IsStackSlot() || loc.IsDoubleStackSlot()); ASSERT(loc.stack_index() < num_stack_slots); descr.SetStackSlotValue(loc.stack_index(), arg_value); } } return data; } #if defined(DEBUG) void FfiMarshalledArguments::Print() const { OS::PrintErr("FfiMarshalledArguments data_ 0x%" Pp "\n", reinterpret_cast(data_)); OS::PrintErr(" 00 0x%016" Px64 " (function address, int result)\n", data_[0]); for (intptr_t i = 0; i < host::CallingConventions::kNumArgRegs; i++) { const intptr_t index = kOffsetRegisters + i; const char* result_str = i == 0 ? ", float result" : ""; OS::PrintErr(" %02" Pd " 0x%016" Px64 " (%s%s)\n", index, data_[index], RegisterNames::RegisterName( host::CallingConventions::ArgumentRegisters[i]), result_str); } for (intptr_t i = 0; i < host::CallingConventions::kNumFpuArgRegs; i++) { const intptr_t index = kOffsetFpuRegisters + i; OS::PrintErr(" %02" Pd " 0x%016" Px64 " (%s)\n", index, data_[index], RegisterNames::FpuRegisterName( host::CallingConventions::FpuArgumentRegisters[i])); } const intptr_t alignment_mask = data_[kOffsetAlignmentMask]; OS::PrintErr(" %02" Pd " 0x%" Pp " (stack alignment mask)\n", kOffsetAlignmentMask, alignment_mask); const intptr_t num_stack_slots = data_[kOffsetNumStackSlots]; OS::PrintErr(" %02" Pd " 0x%" Pp " (number of stack slots)\n", kOffsetNumStackSlots, num_stack_slots); for (intptr_t i = 0; i < num_stack_slots; i++) { const intptr_t index = kOffsetStackSlotValues + i; OS::PrintErr(" %02" Pd " 0x%016" Px64 " (stack slot %" Pd ")\n", index, data_[index], i); } } #endif // defined(DEBUG) #endif // defined(TARGET_ARCH_DBC) } // namespace dart