[vm/ffi] Refactor NativeCallingConvention to be standalone

This CL refactors NativeCallingConvention to be a standalone class that
can be unit tested separately. It no longer uses Function to calcalate
the native ABI, but a NativeFunctionType.

The BaseMarshaller no longer extends NativeCallingConvention but nests
it instead.

The actual unit tests will be added in a follow up CL.

Issue: https://github.com/dart-lang/sdk/issues/44117

Change-Id: Ib529cbaa7e52b51ebdec831eeb772faee153335c
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/170981
Commit-Queue: Daco Harkes <dacoharkes@google.com>
Reviewed-by: Martin Kustermann <kustermann@google.com>
This commit is contained in:
Daco Harkes 2020-11-09 18:18:04 +00:00 committed by commit-bot@chromium.org
parent 711b625bca
commit 82320be8d9
7 changed files with 215 additions and 115 deletions

View file

@ -4,9 +4,13 @@
#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"
@ -17,6 +21,56 @@ 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 Function& 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_(Function::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();
}
@ -80,14 +134,14 @@ class CallbackArgumentTranslator : public ValueObject {
static NativeLocations& TranslateArgumentLocations(
Zone* zone,
const NativeLocations& arg_locs) {
auto& pushed_locs = *(new NativeLocations(arg_locs.length()));
auto& pushed_locs = *(new (zone) NativeLocations(arg_locs.length()));
CallbackArgumentTranslator translator;
for (intptr_t i = 0, n = arg_locs.length(); i < n; i++) {
translator.AllocateArgument(*arg_locs[i]);
}
for (intptr_t i = 0, n = arg_locs.length(); i < n; ++i) {
pushed_locs.Add(&translator.TranslateArgument(*arg_locs[i], zone));
pushed_locs.Add(&translator.TranslateArgument(zone, *arg_locs[i]));
}
return pushed_locs;
@ -97,16 +151,17 @@ class CallbackArgumentTranslator : public ValueObject {
void AllocateArgument(const NativeLocation& arg) {
if (arg.IsStack()) return;
ASSERT(arg.IsRegisters() || arg.IsFpuRegisters());
if (arg.IsRegisters()) {
argument_slots_required_ += arg.AsRegisters().num_regs();
} else {
} else if (arg.IsFpuRegisters()) {
argument_slots_required_ += 8 / target::kWordSize;
} else {
UNREACHABLE();
}
}
const NativeLocation& TranslateArgument(const NativeLocation& arg,
Zone* zone) {
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
@ -152,10 +207,9 @@ class CallbackArgumentTranslator : public ValueObject {
CallbackMarshaller::CallbackMarshaller(Zone* zone,
const Function& dart_signature)
: BaseMarshaller(zone, dart_signature),
callback_locs_(
CallbackArgumentTranslator::TranslateArgumentLocations(zone_,
arg_locs_)) {}
callback_locs_(CallbackArgumentTranslator::TranslateArgumentLocations(
zone_,
native_calling_convention_.argument_locations())) {}
} // namespace ffi
} // namespace compiler

View file

@ -11,6 +11,7 @@
#include <platform/globals.h>
#include "platform/assert.h"
#include "vm/compiler/backend/locations.h"
#include "vm/compiler/ffi/callback.h"
#include "vm/compiler/ffi/native_calling_convention.h"
@ -24,16 +25,32 @@ namespace compiler {
namespace ffi {
// Values below 0 index result (result might be multiple if composite).
const intptr_t kResultIndex = -1;
// Provides the mapping from the native calling convention to the Dart calling
// convention.
//
// This class is set up in a query-able way so that it's underlying logic can
// be extended to support more native ABI features and calling conventions.
//
// TODO(36730): Add a way to query arguments that are broken into multiple
// parts.
class BaseMarshaller : public NativeCallingConvention {
class BaseMarshaller : public ZoneAllocated {
public:
intptr_t num_args() const {
return native_calling_convention_.argument_locations().length();
}
intptr_t StackTopInBytes() const {
return native_calling_convention_.StackTopInBytes();
}
// The location of the argument at `arg_index`.
const NativeLocation& Location(intptr_t arg_index) const {
if (arg_index == kResultIndex) {
return native_calling_convention_.return_location();
}
return *native_calling_convention_.argument_locations().At(arg_index);
}
// Unboxed representation on how the value is passed or received from regular
// Dart code.
Representation RepInDart(intptr_t arg_index) const {
@ -62,6 +79,11 @@ class BaseMarshaller : public NativeCallingConvention {
return Location(arg_index).payload_type();
}
// The C Type (expressed in a Dart Type) of the argument at `arg_index`.
//
// Excluding the #0 argument which is the function pointer.
AbstractTypePtr CType(intptr_t arg_index) const;
// Requires boxing or unboxing.
bool IsPointer(intptr_t arg_index) const {
return AbstractType::Handle(zone_, CType(arg_index)).type_class_id() ==
@ -83,18 +105,16 @@ class BaseMarshaller : public NativeCallingConvention {
StringPtr function_name() const { return dart_signature_.name(); }
protected:
BaseMarshaller(Zone* zone, const Function& dart_signature)
: NativeCallingConvention(
zone,
Function::ZoneHandle(zone, dart_signature.FfiCSignature())),
dart_signature_(dart_signature) {
ASSERT(dart_signature_.IsZoneHandle());
}
BaseMarshaller(Zone* zone, const Function& dart_signature);
private:
~BaseMarshaller() {}
Zone* zone_;
// Contains the function pointer as argument #0.
// The Dart signature is used for the function and argument names.
const Function& dart_signature_;
const Function& c_signature_;
const NativeCallingConvention& native_calling_convention_;
};
class CallMarshaller : public BaseMarshaller {
@ -103,6 +123,9 @@ class CallMarshaller : public BaseMarshaller {
: BaseMarshaller(zone, dart_signature) {}
dart::Location LocInFfiCall(intptr_t arg_index) const;
protected:
~CallMarshaller() {}
};
class CallbackMarshaller : public BaseMarshaller {
@ -125,6 +148,8 @@ class CallbackMarshaller : public BaseMarshaller {
}
protected:
~CallbackMarshaller() {}
const NativeLocations& callback_locs_;
};

View file

@ -16,9 +16,6 @@ namespace compiler {
namespace ffi {
// Argument #0 is the function pointer.
const intptr_t kNativeParamsStartAt = 1;
const intptr_t kNoFpuRegister = -1;
// In Soft FP, floats and doubles get passed in integer registers.
@ -44,29 +41,6 @@ static const NativeType& ConvertIfSoftFp(Zone* zone, const NativeType& rep) {
return rep;
}
// Representations of the arguments to a C signature function.
static ZoneGrowableArray<const NativeType*>& ArgumentRepresentations(
Zone* zone,
const Function& signature) {
const intptr_t num_arguments =
signature.num_fixed_parameters() - kNativeParamsStartAt;
auto& result = *new ZoneGrowableArray<const NativeType*>(zone, num_arguments);
for (intptr_t i = 0; i < num_arguments; i++) {
AbstractType& arg_type = AbstractType::Handle(
zone, signature.ParameterTypeAt(i + kNativeParamsStartAt));
const auto& rep = NativeType::FromAbstractType(zone, arg_type);
result.Add(&rep);
}
return result;
}
// Representation of the result of a C signature function.
static NativeType& ResultRepresentation(Zone* zone, const Function& signature) {
AbstractType& result_type =
AbstractType::Handle(zone, signature.result_type());
return NativeType::FromAbstractType(zone, result_type);
}
// Represents the state of a stack frame going into a call, between allocations
// of argument locations.
class ArgumentAllocator : public ValueObject {
@ -90,9 +64,9 @@ class ArgumentAllocator : public ValueObject {
if (CallingConventions::kArgumentIntRegXorFpuReg) {
ASSERT(cpu_regs_used == CallingConventions::kNumArgRegs);
}
// Transfer on stack.
}
} else {
ASSERT(payload_type_converted.IsInt());
} else if (payload_type_converted.IsInt()) {
// Some calling conventions require the callee to make the lowest 32 bits
// in registers non-garbage.
const auto& container_type =
@ -115,8 +89,12 @@ class ArgumentAllocator : public ValueObject {
if (cpu_regs_used + 1 <= CallingConventions::kNumArgRegs) {
return *new (zone_) NativeRegistersLocation(
payload_type, container_type, AllocateCpuRegister());
} else {
// Transfer on stack.
}
}
} else {
UNREACHABLE();
}
return AllocateStack(payload_type);
@ -227,7 +205,7 @@ static NativeLocations& ArgumentLocations(
Zone* zone,
const ZoneGrowableArray<const NativeType*>& arg_reps) {
intptr_t num_arguments = arg_reps.length();
auto& result = *new NativeLocations(zone, num_arguments);
auto& result = *new (zone) NativeLocations(zone, num_arguments);
// Loop through all arguments and assign a register or a stack location.
ArgumentAllocator frame_state(zone);
@ -239,8 +217,8 @@ static NativeLocations& ArgumentLocations(
}
// Location for the result of a C signature function.
static NativeLocation& ResultLocation(Zone* zone,
const NativeType& payload_type) {
static const NativeLocation& ResultLocation(Zone* zone,
const NativeType& payload_type) {
const auto& payload_type_converted = ConvertIfSoftFp(zone, payload_type);
const auto& container_type =
CallingConventions::kReturnRegisterExtension == kExtendedTo4
@ -263,46 +241,45 @@ static NativeLocation& ResultLocation(Zone* zone,
CallingConventions::kReturnReg);
}
NativeCallingConvention::NativeCallingConvention(Zone* zone,
const Function& c_signature)
: zone_(ASSERT_NOTNULL(zone)),
c_signature_(c_signature),
arg_locs_(
ArgumentLocations(zone_,
ArgumentRepresentations(zone_, c_signature_))),
result_loc_(
ResultLocation(zone_, ResultRepresentation(zone_, c_signature_))) {}
intptr_t NativeCallingConvention::num_args() const {
ASSERT(c_signature_.NumOptionalParameters() == 0);
ASSERT(c_signature_.NumOptionalPositionalParameters() == 0);
// Subtract the #0 argument, the function pointer.
return c_signature_.num_fixed_parameters() - kNativeParamsStartAt;
}
AbstractTypePtr NativeCallingConvention::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);
const NativeCallingConvention& NativeCallingConvention::FromSignature(
Zone* zone,
const NativeFunctionType& signature) {
const auto& argument_locations =
ArgumentLocations(zone, signature.argument_types());
const auto& return_location = ResultLocation(zone, signature.return_type());
return *new (zone)
NativeCallingConvention(argument_locations, return_location);
}
intptr_t NativeCallingConvention::StackTopInBytes() const {
const intptr_t num_arguments = arg_locs_.length();
const intptr_t num_arguments = argument_locations_.length();
intptr_t max_height_in_bytes = 0;
for (intptr_t i = 0; i < num_arguments; i++) {
if (Location(i).IsStack()) {
const intptr_t height = Location(i).AsStack().offset_in_bytes() +
Location(i).container_type().SizeInBytes();
max_height_in_bytes = Utils::Maximum(height, max_height_in_bytes);
}
max_height_in_bytes = Utils::Maximum(
max_height_in_bytes, argument_locations_[i]->StackTopInBytes());
}
return Utils::RoundUp(max_height_in_bytes, compiler::target::kWordSize);
}
const char* NativeCallingConvention::ToCString() const {
char buffer[1024];
BufferFormatter bf(buffer, 1024);
PrintTo(&bf);
return Thread::Current()->zone()->MakeCopyOfString(buffer);
}
void NativeCallingConvention::PrintTo(BaseTextBuffer* f) const {
f->AddString("(");
for (intptr_t i = 0; i < argument_locations_.length(); i++) {
if (i > 0) {
f->AddString(", ");
}
argument_locations_[i]->PrintTo(f);
}
f->AddString(") => ");
return_location_.PrintTo(f);
}
} // namespace ffi
} // namespace compiler

View file

@ -23,43 +23,35 @@ namespace ffi {
using NativeLocations = ZoneGrowableArray<const NativeLocation*>;
// Values below 0 index result (result might be multiple if composite).
const intptr_t kResultIndex = -1;
// Calculates native calling convention, is not aware of Dart calling
// convention constraints.
//
// This class is meant to be extended or embedded in a class that is aware
// of Dart calling convention constraints.
// This class is meant to beembedded in a class that is aware of Dart calling
// convention constraints.
class NativeCallingConvention : public ZoneAllocated {
public:
NativeCallingConvention(Zone* zone, const Function& c_signature);
static const NativeCallingConvention& FromSignature(
Zone* zone,
const NativeFunctionType& signature);
// Excluding the #0 argument which is the function pointer.
intptr_t num_args() const;
// The C Type (expressed in a Dart Type) of the argument at `arg_index`.
//
// Excluding the #0 argument which is the function pointer.
AbstractTypePtr CType(intptr_t arg_index) const;
// The location of the argument at `arg_index`.
const NativeLocation& Location(intptr_t arg_index) const {
if (arg_index == kResultIndex) {
return result_loc_;
}
return *arg_locs_.At(arg_index);
const NativeLocations& argument_locations() const {
return argument_locations_;
}
const NativeLocation& return_location() const { return return_location_; }
intptr_t StackTopInBytes() const;
protected:
Zone* const zone_;
// Contains the function pointer as argument #0.
const Function& c_signature_;
const NativeLocations& arg_locs_;
const NativeLocation& result_loc_;
void PrintTo(BaseTextBuffer* f) const;
const char* ToCString() const;
private:
NativeCallingConvention(const NativeLocations& argument_locations,
const NativeLocation& return_location)
: argument_locations_(argument_locations),
return_location_(return_location) {}
const NativeLocations& argument_locations_;
const NativeLocation& return_location_;
};
} // namespace ffi

View file

@ -9,6 +9,7 @@
#error "AOT runtime should not use compiler sources (including header files)"
#endif // defined(DART_PRECOMPILED_RUNTIME)
#include "platform/assert.h"
#include "vm/compiler/backend/locations.h"
#include "vm/compiler/ffi/native_type.h"
#include "vm/growable_array.h"
@ -34,14 +35,15 @@ class NativeStackLocation;
// * The container type, equal to or larger than the payload. If the
// container is larger than the payload, the upper bits are defined by sign
// or zero extension.
// Container type is also used to denote an integer container when floating
// point values are passed in integer registers.
//
// NativeLocations can express things that dart::Locations cannot express:
// * Multiple consecutive registers.
// * Multiple sizes of FPU registers (e.g. S, D, and Q on Arm32).
// * Arbitrary byte-size stack locations, at byte-size offsets.
// (The Location class uses word-size offsets.)
// * Pointers including a backing location on the stack.
// * No location.
// * Pointers to a memory location.
// * Split between multiple registers and stack.
//
// NativeLocations cannot express the following dart::Locations:
@ -106,6 +108,9 @@ class NativeLocation : public ZoneAllocated {
UNREACHABLE();
}
// Return the top of the stack in bytes.
virtual intptr_t StackTopInBytes() const { return 0; }
// Equality of location, ignores the payload and container native types.
virtual bool Equals(const NativeLocation& other) const { UNREACHABLE(); }
@ -303,6 +308,10 @@ class NativeStackLocation : public NativeLocation {
virtual NativeStackLocation& Split(Zone* zone, intptr_t index) const;
virtual intptr_t StackTopInBytes() const {
return offset_in_bytes() + container_type().SizeInBytes();
}
virtual void PrintTo(BaseTextBuffer* f) const;
virtual bool Equals(const NativeLocation& other) const;

View file

@ -6,8 +6,12 @@
#include "platform/assert.h"
#include "platform/globals.h"
#include "vm/class_id.h"
#include "vm/compiler/runtime_api.h"
#include "vm/growable_array.h"
#include "vm/log.h"
#include "vm/object.h"
#include "vm/symbols.h"
#if !defined(DART_PRECOMPILED_RUNTIME)
#include "vm/compiler/backend/locations.h"
@ -324,6 +328,25 @@ void NativePrimitiveType::PrintTo(BaseTextBuffer* f) const {
f->Printf("%s", PrimitiveTypeToCString(representation_));
}
const char* NativeFunctionType::ToCString() const {
char buffer[1024];
BufferFormatter bf(buffer, 1024);
PrintTo(&bf);
return Thread::Current()->zone()->MakeCopyOfString(buffer);
}
void NativeFunctionType::PrintTo(BaseTextBuffer* f) const {
f->AddString("(");
for (intptr_t i = 0; i < argument_types_.length(); i++) {
if (i > 0) {
f->AddString(", ");
}
argument_types_[i]->PrintTo(f);
}
f->AddString(") => ");
return_type_.PrintTo(f);
}
const NativeType& NativeType::WidenTo4Bytes(Zone* zone) const {
if (IsInt() && SizeInBytes() <= 2) {
if (IsSigned()) {

View file

@ -10,6 +10,7 @@
#include "platform/assert.h"
#include "vm/allocation.h"
#include "vm/compiler/runtime_api.h"
#include "vm/growable_array.h"
#if !defined(DART_PRECOMPILED_RUNTIME)
#include "vm/compiler/backend/locations.h"
@ -66,7 +67,7 @@ class NativeType : public ZoneAllocated {
virtual bool IsFloat() const { return false; }
virtual bool IsVoid() const { return false; }
virtual bool IsSigned() const = 0;
virtual bool IsSigned() const { return false; }
// The size in bytes of this representation.
//
@ -84,7 +85,7 @@ class NativeType : public ZoneAllocated {
virtual bool IsExpressibleAsRepresentation() const { return false; }
// Unboxed Representation if it exists.
virtual Representation AsRepresentation() const = 0;
virtual Representation AsRepresentation() const { UNREACHABLE(); }
// Unboxed Representation, over approximates if needed.
Representation AsRepresentationOverApprox(Zone* zone_) const {
@ -167,6 +168,25 @@ class NativePrimitiveType : public NativeType {
const PrimitiveType representation_;
};
using NativeTypes = ZoneGrowableArray<const NativeType*>;
class NativeFunctionType : public ZoneAllocated {
public:
NativeFunctionType(const NativeTypes& argument_types,
const NativeType& return_type)
: argument_types_(argument_types), return_type_(return_type) {}
const NativeTypes& argument_types() const { return argument_types_; }
const NativeType& return_type() const { return return_type_; }
void PrintTo(BaseTextBuffer* f) const;
const char* ToCString() const;
private:
const NativeTypes& argument_types_;
const NativeType& return_type_;
};
} // namespace ffi
} // namespace compiler