0889552856
In preparation of having `Union` besides `Struct`, renames all mentions of `Struct` to `Compound`. Bug: https://github.com/dart-lang/sdk/issues/38491 tools/test.py ffi ffi_2 TEST=tests/ffi(_2)/(.*)by_value_(*.)_test.dart Change-Id: I47124a95d67c5afc3da9b5f5c08d0be47908f2e0 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/194423 Reviewed-by: Aske Simon Christensen <askesc@google.com>
627 lines
22 KiB
C++
627 lines
22 KiB
C++
// Copyright (c) 2020, 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 "vm/compiler/ffi/marshaller.h"
|
|
|
|
#include "platform/assert.h"
|
|
#include "platform/globals.h"
|
|
#include "vm/compiler/ffi/frame_rebase.h"
|
|
#include "vm/compiler/ffi/native_calling_convention.h"
|
|
#include "vm/compiler/ffi/native_location.h"
|
|
#include "vm/compiler/ffi/native_type.h"
|
|
#include "vm/log.h"
|
|
#include "vm/raw_object.h"
|
|
#include "vm/stack_frame.h"
|
|
#include "vm/symbols.h"
|
|
|
|
namespace dart {
|
|
|
|
namespace compiler {
|
|
|
|
namespace ffi {
|
|
|
|
// Argument #0 is the function pointer.
|
|
const intptr_t kNativeParamsStartAt = 1;
|
|
|
|
// Representations of the arguments and return value of a C signature function.
|
|
static const NativeFunctionType& NativeFunctionSignature(
|
|
Zone* zone,
|
|
const FunctionType& c_signature) {
|
|
ASSERT(c_signature.NumOptionalParameters() == 0);
|
|
ASSERT(c_signature.NumOptionalPositionalParameters() == 0);
|
|
|
|
const intptr_t num_arguments =
|
|
c_signature.num_fixed_parameters() - kNativeParamsStartAt;
|
|
auto& argument_representations =
|
|
*new ZoneGrowableArray<const NativeType*>(zone, num_arguments);
|
|
for (intptr_t i = 0; i < num_arguments; i++) {
|
|
AbstractType& arg_type = AbstractType::Handle(
|
|
zone, c_signature.ParameterTypeAt(i + kNativeParamsStartAt));
|
|
const auto& rep = NativeType::FromAbstractType(zone, arg_type);
|
|
argument_representations.Add(&rep);
|
|
}
|
|
|
|
const auto& result_type =
|
|
AbstractType::Handle(zone, c_signature.result_type());
|
|
const auto& result_representation =
|
|
NativeType::FromAbstractType(zone, result_type);
|
|
|
|
const auto& result = *new (zone) NativeFunctionType(argument_representations,
|
|
result_representation);
|
|
return result;
|
|
}
|
|
|
|
BaseMarshaller::BaseMarshaller(Zone* zone, const Function& dart_signature)
|
|
: zone_(zone),
|
|
dart_signature_(dart_signature),
|
|
c_signature_(
|
|
FunctionType::ZoneHandle(zone, dart_signature.FfiCSignature())),
|
|
native_calling_convention_(NativeCallingConvention::FromSignature(
|
|
zone,
|
|
NativeFunctionSignature(zone_, c_signature_))) {
|
|
ASSERT(dart_signature_.IsZoneHandle());
|
|
}
|
|
|
|
AbstractTypePtr BaseMarshaller::CType(intptr_t arg_index) const {
|
|
if (arg_index == kResultIndex) {
|
|
return c_signature_.result_type();
|
|
}
|
|
|
|
// Skip #0 argument, the function pointer.
|
|
return c_signature_.ParameterTypeAt(arg_index + kNativeParamsStartAt);
|
|
}
|
|
|
|
bool BaseMarshaller::ContainsHandles() const {
|
|
return dart_signature_.FfiCSignatureContainsHandles();
|
|
}
|
|
|
|
intptr_t BaseMarshaller::NumDefinitions() const {
|
|
intptr_t total = 0;
|
|
for (intptr_t i = 0; i < num_args(); i++) {
|
|
total += NumDefinitions(i);
|
|
}
|
|
return total;
|
|
}
|
|
|
|
intptr_t BaseMarshaller::NumDefinitions(intptr_t arg_index) const {
|
|
if (ArgumentIndexIsReturn(arg_index)) {
|
|
return NumReturnDefinitions();
|
|
}
|
|
|
|
const auto& loc = Location(arg_index);
|
|
const auto& type = loc.payload_type();
|
|
|
|
if (type.IsPrimitive()) {
|
|
// All non-struct arguments are 1 definition in IL. Even 64 bit values
|
|
// on 32 bit architectures.
|
|
return 1;
|
|
}
|
|
|
|
ASSERT(type.IsCompound());
|
|
if (loc.IsMultiple()) {
|
|
// One IL definition for every nested location.
|
|
const auto& multiple = loc.AsMultiple();
|
|
return multiple.locations().length();
|
|
}
|
|
|
|
if (loc.IsPointerToMemory()) {
|
|
// For FFI calls, pass in TypedDataBase (1 IL definition) in IL, and copy
|
|
// contents to stack and pass pointer in right location in MC.
|
|
// For FFI callbacks, get the pointer in a NativeParameter and construct
|
|
// the TypedDataBase in IL.
|
|
return 1;
|
|
}
|
|
|
|
ASSERT(loc.IsStack());
|
|
// For stack, word size definitions in IL. In FFI calls passed in to the
|
|
// native call, in FFI callbacks read in separate NativeParams.
|
|
const intptr_t size_in_bytes = type.SizeInBytes();
|
|
const intptr_t num_defs =
|
|
Utils::RoundUp(size_in_bytes, compiler::target::kWordSize) /
|
|
compiler::target::kWordSize;
|
|
return num_defs;
|
|
}
|
|
|
|
intptr_t BaseMarshaller::NumReturnDefinitions() const {
|
|
// For FFI calls we always have 1 definition, because the IL instruction can
|
|
// only be 1 definition. We pass in a TypedDataBase in IL and fill it in
|
|
// machine code.
|
|
//
|
|
// For FFI callbacks we always have 1 definition. If it's a struct and the
|
|
// native ABI is passing a pointer, we copy to it in IL. If it's a multiple
|
|
// locations return value we copy the value in machine code because some
|
|
// native locations cannot be expressed in IL in Location.
|
|
return 1;
|
|
}
|
|
|
|
bool BaseMarshaller::ArgumentIndexIsReturn(intptr_t arg_index) const {
|
|
ASSERT(arg_index == kResultIndex || arg_index >= 0);
|
|
return arg_index == kResultIndex;
|
|
}
|
|
|
|
// Definitions in return value count down.
|
|
bool BaseMarshaller::DefinitionIndexIsReturn(intptr_t def_index_global) const {
|
|
return def_index_global <= kResultIndex;
|
|
}
|
|
|
|
intptr_t BaseMarshaller::ArgumentIndex(intptr_t def_index_global) const {
|
|
if (DefinitionIndexIsReturn(def_index_global)) {
|
|
const intptr_t def = DefinitionInArgument(def_index_global, kResultIndex);
|
|
ASSERT(def < NumReturnDefinitions());
|
|
return kResultIndex;
|
|
}
|
|
ASSERT(def_index_global < NumDefinitions());
|
|
intptr_t defs = 0;
|
|
intptr_t arg_index = 0;
|
|
for (; arg_index < num_args(); arg_index++) {
|
|
defs += NumDefinitions(arg_index);
|
|
if (defs > def_index_global) {
|
|
return arg_index;
|
|
}
|
|
}
|
|
UNREACHABLE();
|
|
}
|
|
|
|
intptr_t BaseMarshaller::FirstDefinitionIndex(intptr_t arg_index) const {
|
|
if (arg_index <= kResultIndex) {
|
|
return kResultIndex;
|
|
}
|
|
ASSERT(arg_index < num_args());
|
|
intptr_t num_defs = 0;
|
|
for (intptr_t i = 0; i < arg_index; i++) {
|
|
num_defs += NumDefinitions(i);
|
|
}
|
|
return num_defs;
|
|
}
|
|
|
|
intptr_t BaseMarshaller::DefinitionInArgument(intptr_t def_index_global,
|
|
intptr_t arg_index) const {
|
|
if (ArgumentIndexIsReturn(arg_index)) {
|
|
// Counting down for return definitions.
|
|
const intptr_t def = kResultIndex - def_index_global;
|
|
ASSERT(def < NumReturnDefinitions());
|
|
return def;
|
|
} else {
|
|
// Counting up for arguments in consecutive order.
|
|
const intptr_t def = def_index_global - FirstDefinitionIndex(arg_index);
|
|
ASSERT(def < NumDefinitions());
|
|
return def;
|
|
}
|
|
}
|
|
|
|
intptr_t BaseMarshaller::DefinitionIndex(intptr_t def_index_in_arg,
|
|
intptr_t arg_index) const {
|
|
ASSERT(def_index_in_arg < NumDefinitions(arg_index));
|
|
if (ArgumentIndexIsReturn(arg_index)) {
|
|
return kResultIndex - def_index_in_arg;
|
|
} else {
|
|
return FirstDefinitionIndex(arg_index) + def_index_in_arg;
|
|
}
|
|
}
|
|
|
|
static Representation SelectRepresentationInIL(Zone* zone,
|
|
const NativeLocation& location) {
|
|
if (location.container_type().IsInt() && location.payload_type().IsFloat()) {
|
|
// IL can only pass integers to integer Locations, so pass as integer if
|
|
// the Location requires it to be an integer.
|
|
return location.container_type().AsRepresentationOverApprox(zone);
|
|
}
|
|
// Representations do not support 8 or 16 bit ints, over approximate to 32
|
|
// bits.
|
|
return location.payload_type().AsRepresentationOverApprox(zone);
|
|
}
|
|
|
|
// Implemented partially in BaseMarshaller because most Representations are
|
|
// the same in Calls and Callbacks.
|
|
Representation BaseMarshaller::RepInFfiCall(intptr_t def_index_global) const {
|
|
intptr_t arg_index = ArgumentIndex(def_index_global);
|
|
const auto& location = Location(arg_index);
|
|
|
|
if (location.container_type().IsPrimitive()) {
|
|
return SelectRepresentationInIL(zone_, location);
|
|
}
|
|
ASSERT(location.container_type().IsCompound());
|
|
|
|
if (location.IsStack()) {
|
|
// Split the struct in architecture size chunks.
|
|
return compiler::target::kWordSize == 8 ? Representation::kUnboxedInt64
|
|
: Representation::kUnboxedInt32;
|
|
}
|
|
|
|
if (location.IsMultiple()) {
|
|
const intptr_t def_index_in_arg =
|
|
DefinitionInArgument(def_index_global, arg_index);
|
|
const auto& def_loc =
|
|
*(location.AsMultiple().locations()[def_index_in_arg]);
|
|
return SelectRepresentationInIL(zone_, def_loc);
|
|
}
|
|
|
|
ASSERT(location.IsPointerToMemory());
|
|
UNREACHABLE(); // Implemented in subclasses.
|
|
}
|
|
|
|
Representation CallMarshaller::RepInFfiCall(intptr_t def_index_global) const {
|
|
intptr_t arg_index = ArgumentIndex(def_index_global);
|
|
const auto& location = Location(arg_index);
|
|
if (location.IsPointerToMemory()) {
|
|
if (ArgumentIndexIsReturn(arg_index)) {
|
|
// The IL type is the unboxed pointer.
|
|
const auto& pointer_location = location.AsPointerToMemory();
|
|
const auto& rep = pointer_location.pointer_location().payload_type();
|
|
ASSERT(rep.Equals(
|
|
pointer_location.pointer_return_location().payload_type()));
|
|
return rep.AsRepresentation();
|
|
} else {
|
|
// We're passing Pointer/TypedData object, the GC might move TypedData so
|
|
// we can't load the address from it eagerly.
|
|
return kTagged;
|
|
}
|
|
}
|
|
return BaseMarshaller::RepInFfiCall(def_index_global);
|
|
}
|
|
|
|
Representation CallbackMarshaller::RepInFfiCall(
|
|
intptr_t def_index_global) const {
|
|
intptr_t arg_index = ArgumentIndex(def_index_global);
|
|
const auto& location = Location(arg_index);
|
|
if (location.IsPointerToMemory()) {
|
|
// The IL type is the unboxed pointer, and FFI callback return. In the
|
|
// latter we've already copied the data into the result location in IL.
|
|
const auto& pointer_location = location.AsPointerToMemory();
|
|
const auto& rep = pointer_location.pointer_location().payload_type();
|
|
ASSERT(
|
|
rep.Equals(pointer_location.pointer_return_location().payload_type()));
|
|
return rep.AsRepresentation();
|
|
}
|
|
if (ArgumentIndexIsReturn(arg_index) && location.IsMultiple()) {
|
|
// We're passing a TypedData.
|
|
return Representation::kTagged;
|
|
}
|
|
return BaseMarshaller::RepInFfiCall(def_index_global);
|
|
}
|
|
|
|
void BaseMarshaller::RepsInFfiCall(intptr_t arg_index,
|
|
GrowableArray<Representation>* out) const {
|
|
const intptr_t num_definitions = NumDefinitions(arg_index);
|
|
const intptr_t first_def = FirstDefinitionIndex(arg_index);
|
|
for (int i = 0; i < num_definitions; i++) {
|
|
out->Add(RepInFfiCall(first_def + i));
|
|
}
|
|
}
|
|
|
|
// Helper method for `LocInFfiCall` to turn a stack location into either any
|
|
// location or a pair of two any locations.
|
|
static Location ConvertToAnyLocation(const NativeStackLocation& loc,
|
|
Representation rep_in_ffi_call) {
|
|
// Floating point values are never split: they are either in a single "FPU"
|
|
// register or a contiguous 64-bit slot on the stack. Unboxed 64-bit integer
|
|
// values, in contrast, can be split between any two registers on a 32-bit
|
|
// system.
|
|
//
|
|
// There is an exception for iOS and Android 32-bit ARM, where
|
|
// floating-point values are treated as integers as far as the calling
|
|
// convention is concerned. However, the representation of these arguments
|
|
// are set to kUnboxedInt32 or kUnboxedInt64 already, so we don't have to
|
|
// account for that here.
|
|
const bool is_atomic =
|
|
rep_in_ffi_call == kUnboxedDouble || rep_in_ffi_call == kUnboxedFloat;
|
|
|
|
if (loc.payload_type().IsPrimitive() &&
|
|
loc.payload_type().SizeInBytes() == 2 * compiler::target::kWordSize &&
|
|
!is_atomic) {
|
|
return Location::Pair(Location::Any(), Location::Any());
|
|
}
|
|
return Location::Any();
|
|
}
|
|
|
|
static Location SelectFpuLocationInIL(Zone* zone,
|
|
const NativeLocation& location) {
|
|
ASSERT((location.IsFpuRegisters()));
|
|
#if defined(TARGET_ARCH_ARM)
|
|
// Only pin FPU register if it is the lowest bits.
|
|
const auto& fpu_loc = location.AsFpuRegisters();
|
|
if (fpu_loc.IsLowestBits()) {
|
|
return fpu_loc.WidenToQFpuRegister(zone).AsLocation();
|
|
}
|
|
return Location::Any();
|
|
#endif // defined(TARGET_ARCH_ARM)
|
|
|
|
return location.AsLocation();
|
|
}
|
|
|
|
Location CallMarshaller::LocInFfiCall(intptr_t def_index_global) const {
|
|
const intptr_t arg_index = ArgumentIndex(def_index_global);
|
|
const NativeLocation& loc = this->Location(arg_index);
|
|
|
|
if (ArgumentIndexIsReturn(arg_index)) {
|
|
const intptr_t def = kResultIndex - def_index_global;
|
|
if (loc.IsMultiple()) {
|
|
ASSERT(loc.AsMultiple().locations()[def]->IsExpressibleAsLocation());
|
|
return loc.AsMultiple().locations()[def]->AsLocation();
|
|
}
|
|
|
|
if (loc.IsPointerToMemory()) {
|
|
// No location at all, because we store into TypedData passed to the
|
|
// FfiCall instruction. But we have to supply a location.
|
|
return Location::RegisterLocation(CallingConventions::kReturnReg);
|
|
}
|
|
|
|
return loc.AsLocation();
|
|
}
|
|
|
|
if (loc.IsMultiple()) {
|
|
const intptr_t def_index_in_arg =
|
|
def_index_global - FirstDefinitionIndex(arg_index);
|
|
const auto& def_loc = *(loc.AsMultiple().locations()[def_index_in_arg]);
|
|
if (def_loc.IsStack()) {
|
|
// Don't pin stack locations, they need to be moved anyway.
|
|
return ConvertToAnyLocation(def_loc.AsStack(),
|
|
RepInFfiCall(def_index_global));
|
|
}
|
|
|
|
if (def_loc.IsFpuRegisters()) {
|
|
return SelectFpuLocationInIL(zone_, def_loc);
|
|
}
|
|
|
|
return def_loc.AsLocation();
|
|
}
|
|
|
|
if (loc.IsPointerToMemory()) {
|
|
const auto& pointer_location = loc.AsPointerToMemory().pointer_location();
|
|
if (pointer_location.IsStack()) {
|
|
// Don't pin stack locations, they need to be moved anyway.
|
|
return ConvertToAnyLocation(pointer_location.AsStack(),
|
|
RepInFfiCall(def_index_global));
|
|
}
|
|
return pointer_location.AsLocation();
|
|
}
|
|
|
|
if (loc.IsStack()) {
|
|
return ConvertToAnyLocation(loc.AsStack(), RepInFfiCall(def_index_global));
|
|
}
|
|
|
|
if (loc.IsFpuRegisters()) {
|
|
return SelectFpuLocationInIL(zone_, loc);
|
|
}
|
|
|
|
ASSERT(loc.IsRegisters());
|
|
return loc.AsLocation();
|
|
}
|
|
|
|
bool CallMarshaller::PassTypedData() const {
|
|
return IsCompound(compiler::ffi::kResultIndex);
|
|
}
|
|
|
|
intptr_t CallMarshaller::TypedDataSizeInBytes() const {
|
|
ASSERT(PassTypedData());
|
|
return Utils::RoundUp(
|
|
Location(compiler::ffi::kResultIndex).payload_type().SizeInBytes(),
|
|
compiler::target::kWordSize);
|
|
}
|
|
|
|
// Const to be able to look up the `RequiredStackSpaceInBytes` in
|
|
// `PassByPointerStackOffset`.
|
|
const intptr_t kAfterLastArgumentIndex = kIntptrMax;
|
|
|
|
intptr_t CallMarshaller::PassByPointerStackOffset(intptr_t arg_index) const {
|
|
ASSERT(arg_index == kResultIndex ||
|
|
(arg_index >= 0 && arg_index < num_args()) ||
|
|
arg_index == kAfterLastArgumentIndex);
|
|
|
|
intptr_t stack_offset = 0;
|
|
|
|
// First the native arguments are on the stack.
|
|
// This is governed by the native ABI, the rest we can chose freely.
|
|
stack_offset += native_calling_convention_.StackTopInBytes();
|
|
stack_offset = Utils::RoundUp(stack_offset, compiler::target::kWordSize);
|
|
if (arg_index == kResultIndex) {
|
|
return stack_offset;
|
|
}
|
|
|
|
// Then save space for the result.
|
|
const auto& result_location = Location(compiler::ffi::kResultIndex);
|
|
if (result_location.IsPointerToMemory()) {
|
|
stack_offset += result_location.payload_type().SizeInBytes();
|
|
stack_offset = Utils::RoundUp(stack_offset, compiler::target::kWordSize);
|
|
}
|
|
|
|
// And finally put the arguments on the stack that are passed by pointer.
|
|
for (int i = 0; i < num_args(); i++) {
|
|
if (arg_index == i) {
|
|
return stack_offset;
|
|
}
|
|
const auto& arg_location = Location(i);
|
|
if (arg_location.IsPointerToMemory()) {
|
|
stack_offset += arg_location.payload_type().SizeInBytes();
|
|
stack_offset = Utils::RoundUp(stack_offset, compiler::target::kWordSize);
|
|
}
|
|
}
|
|
|
|
// The total stack space we need.
|
|
ASSERT(arg_index == kAfterLastArgumentIndex);
|
|
return stack_offset;
|
|
}
|
|
|
|
intptr_t CallMarshaller::RequiredStackSpaceInBytes() const {
|
|
return PassByPointerStackOffset(kAfterLastArgumentIndex);
|
|
}
|
|
|
|
// This classes translates the ABI location of arguments into the locations they
|
|
// will inhabit after entry-frame setup in the invocation of a native callback.
|
|
//
|
|
// Native -> Dart callbacks must push all the arguments before executing any
|
|
// Dart code because the reading the Thread from TLS requires calling a native
|
|
// stub, and the argument registers are volatile on all ABIs we support.
|
|
//
|
|
// To avoid complicating initial definitions, all callback arguments are read
|
|
// off the stack from their pushed locations, so this class updates the argument
|
|
// positions to account for this.
|
|
//
|
|
// See 'NativeEntryInstr::EmitNativeCode' for details.
|
|
class CallbackArgumentTranslator : public ValueObject {
|
|
public:
|
|
static NativeLocations& TranslateArgumentLocations(
|
|
Zone* zone,
|
|
const NativeLocations& argument_locations,
|
|
const NativeLocation& return_loc) {
|
|
const bool treat_return_loc = return_loc.IsPointerToMemory();
|
|
|
|
auto& pushed_locs = *(new (zone) NativeLocations(
|
|
argument_locations.length() + (treat_return_loc ? 1 : 0)));
|
|
|
|
CallbackArgumentTranslator translator;
|
|
for (intptr_t i = 0, n = argument_locations.length(); i < n; i++) {
|
|
translator.AllocateArgument(*argument_locations[i]);
|
|
}
|
|
if (treat_return_loc) {
|
|
translator.AllocateArgument(return_loc);
|
|
}
|
|
for (intptr_t i = 0, n = argument_locations.length(); i < n; ++i) {
|
|
pushed_locs.Add(
|
|
&translator.TranslateArgument(zone, *argument_locations[i]));
|
|
}
|
|
if (treat_return_loc) {
|
|
pushed_locs.Add(&translator.TranslateArgument(zone, return_loc));
|
|
}
|
|
|
|
return pushed_locs;
|
|
}
|
|
|
|
private:
|
|
void AllocateArgument(const NativeLocation& arg) {
|
|
if (arg.IsStack()) return;
|
|
|
|
if (arg.IsRegisters()) {
|
|
argument_slots_required_ += arg.AsRegisters().num_regs();
|
|
} else if (arg.IsFpuRegisters()) {
|
|
argument_slots_required_ += 8 / target::kWordSize;
|
|
} else if (arg.IsPointerToMemory()) {
|
|
if (arg.AsPointerToMemory().pointer_location().IsRegisters()) {
|
|
argument_slots_required_ += 1;
|
|
}
|
|
} else {
|
|
ASSERT(arg.IsMultiple());
|
|
const auto& multiple = arg.AsMultiple();
|
|
for (intptr_t i = 0; i < multiple.locations().length(); i++) {
|
|
AllocateArgument(*multiple.locations().At(i));
|
|
}
|
|
}
|
|
}
|
|
|
|
const NativeLocation& TranslateArgument(Zone* zone,
|
|
const NativeLocation& arg) {
|
|
if (arg.IsStack()) {
|
|
// Add extra slots after the saved arguments for the return address and
|
|
// frame pointer of the dummy arguments frame, which will be between the
|
|
// saved argument registers and stack arguments. Also add slots for the
|
|
// shadow space if present (factored into
|
|
// kCallbackSlotsBeforeSavedArguments).
|
|
//
|
|
// Finally, if we are using NativeCallbackTrampolines, factor in the extra
|
|
// stack space corresponding to those trampolines' frames (above the entry
|
|
// frame).
|
|
intptr_t stack_delta = kCallbackSlotsBeforeSavedArguments;
|
|
if (NativeCallbackTrampolines::Enabled()) {
|
|
stack_delta += StubCodeCompiler::kNativeCallbackTrampolineStackDelta;
|
|
}
|
|
FrameRebase rebase(
|
|
zone,
|
|
/*old_base=*/SPREG, /*new_base=*/SPREG,
|
|
/*stack_delta=*/(argument_slots_required_ + stack_delta) *
|
|
compiler::target::kWordSize);
|
|
return rebase.Rebase(arg);
|
|
}
|
|
|
|
if (arg.IsRegisters()) {
|
|
const auto& result = *new (zone) NativeStackLocation(
|
|
arg.payload_type(), arg.container_type(), SPREG,
|
|
argument_slots_used_ * compiler::target::kWordSize);
|
|
argument_slots_used_ += arg.AsRegisters().num_regs();
|
|
return result;
|
|
}
|
|
|
|
if (arg.IsFpuRegisters()) {
|
|
const auto& result = *new (zone) NativeStackLocation(
|
|
arg.payload_type(), arg.container_type(), SPREG,
|
|
argument_slots_used_ * compiler::target::kWordSize);
|
|
argument_slots_used_ += 8 / target::kWordSize;
|
|
return result;
|
|
}
|
|
|
|
if (arg.IsPointerToMemory()) {
|
|
const auto& pointer_loc = arg.AsPointerToMemory().pointer_location();
|
|
const auto& pointer_ret_loc =
|
|
arg.AsPointerToMemory().pointer_return_location();
|
|
const auto& pointer_translated = TranslateArgument(zone, pointer_loc);
|
|
return *new (zone) PointerToMemoryLocation(
|
|
pointer_translated, pointer_ret_loc, arg.payload_type().AsCompound());
|
|
}
|
|
|
|
ASSERT(arg.IsMultiple());
|
|
const auto& multiple = arg.AsMultiple();
|
|
NativeLocations& multiple_locations =
|
|
*new (zone) NativeLocations(multiple.locations().length());
|
|
for (intptr_t i = 0; i < multiple.locations().length(); i++) {
|
|
multiple_locations.Add(
|
|
&TranslateArgument(zone, *multiple.locations().At(i)));
|
|
}
|
|
return *new (zone) MultipleNativeLocations(
|
|
multiple.payload_type().AsCompound(), multiple_locations);
|
|
}
|
|
|
|
intptr_t argument_slots_used_ = 0;
|
|
intptr_t argument_slots_required_ = 0;
|
|
};
|
|
|
|
CallbackMarshaller::CallbackMarshaller(Zone* zone,
|
|
const Function& dart_signature)
|
|
: BaseMarshaller(zone, dart_signature),
|
|
callback_locs_(CallbackArgumentTranslator::TranslateArgumentLocations(
|
|
zone_,
|
|
native_calling_convention_.argument_locations(),
|
|
native_calling_convention_.return_location())) {}
|
|
|
|
const NativeLocation& CallbackMarshaller::NativeLocationOfNativeParameter(
|
|
intptr_t def_index) const {
|
|
const intptr_t arg_index = ArgumentIndex(def_index);
|
|
if (arg_index == kResultIndex) {
|
|
const auto& result_loc = Location(arg_index);
|
|
if (result_loc.IsPointerToMemory()) {
|
|
// If it's a pointer we return it in the last.
|
|
return *callback_locs_.At(callback_locs_.length() - 1);
|
|
}
|
|
// The other return types are not translated.
|
|
return result_loc;
|
|
}
|
|
|
|
// Check that we only have stack arguments.
|
|
const auto& loc = *callback_locs_.At(arg_index);
|
|
ASSERT(loc.IsStack() || loc.IsPointerToMemory() || loc.IsMultiple());
|
|
if (loc.IsStack()) {
|
|
ASSERT(loc.AsStack().base_register() == SPREG);
|
|
if (loc.payload_type().IsPrimitive()) {
|
|
return loc;
|
|
}
|
|
const intptr_t index = DefinitionInArgument(def_index, arg_index);
|
|
const intptr_t count = NumDefinitions(arg_index);
|
|
return loc.Split(zone_, count, index);
|
|
} else if (loc.IsPointerToMemory()) {
|
|
const auto& pointer_loc = loc.AsPointerToMemory().pointer_location();
|
|
ASSERT(pointer_loc.IsStack() &&
|
|
pointer_loc.AsStack().base_register() == SPREG);
|
|
return loc;
|
|
}
|
|
const auto& multiple = loc.AsMultiple();
|
|
const intptr_t index = DefinitionInArgument(def_index, arg_index);
|
|
const auto& multi_loc = *multiple.locations().At(index);
|
|
ASSERT(multi_loc.IsStack() && multi_loc.AsStack().base_register() == SPREG);
|
|
return multi_loc;
|
|
}
|
|
|
|
} // namespace ffi
|
|
|
|
} // namespace compiler
|
|
|
|
} // namespace dart
|