[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>
diff --git a/runtime/vm/compiler/ffi/marshaller.cc b/runtime/vm/compiler/ffi/marshaller.cc
index c095e9e..6620468 100644
--- a/runtime/vm/compiler/ffi/marshaller.cc
+++ b/runtime/vm/compiler/ffi/marshaller.cc
@@ -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 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 @@
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 @@
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 @@
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
diff --git a/runtime/vm/compiler/ffi/marshaller.h b/runtime/vm/compiler/ffi/marshaller.h
index d175ddc..be567b6 100644
--- a/runtime/vm/compiler/ffi/marshaller.h
+++ b/runtime/vm/compiler/ffi/marshaller.h
@@ -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 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 @@
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 @@
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 @@
: BaseMarshaller(zone, dart_signature) {}
dart::Location LocInFfiCall(intptr_t arg_index) const;
+
+ protected:
+ ~CallMarshaller() {}
};
class CallbackMarshaller : public BaseMarshaller {
@@ -125,6 +148,8 @@
}
protected:
+ ~CallbackMarshaller() {}
+
const NativeLocations& callback_locs_;
};
diff --git a/runtime/vm/compiler/ffi/native_calling_convention.cc b/runtime/vm/compiler/ffi/native_calling_convention.cc
index 84081aa..2ac1445 100644
--- a/runtime/vm/compiler/ffi/native_calling_convention.cc
+++ b/runtime/vm/compiler/ffi/native_calling_convention.cc
@@ -16,9 +16,6 @@
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 @@
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 @@
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 @@
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 @@
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 @@
}
// 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 @@
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
diff --git a/runtime/vm/compiler/ffi/native_calling_convention.h b/runtime/vm/compiler/ffi/native_calling_convention.h
index 5eae152..4948910 100644
--- a/runtime/vm/compiler/ffi/native_calling_convention.h
+++ b/runtime/vm/compiler/ffi/native_calling_convention.h
@@ -23,43 +23,35 @@
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
diff --git a/runtime/vm/compiler/ffi/native_location.h b/runtime/vm/compiler/ffi/native_location.h
index 78caa24..6c7e980 100644
--- a/runtime/vm/compiler/ffi/native_location.h
+++ b/runtime/vm/compiler/ffi/native_location.h
@@ -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 @@
// * 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 @@
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 @@
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;
diff --git a/runtime/vm/compiler/ffi/native_type.cc b/runtime/vm/compiler/ffi/native_type.cc
index 5bb9f4c..c5cd230 100644
--- a/runtime/vm/compiler/ffi/native_type.cc
+++ b/runtime/vm/compiler/ffi/native_type.cc
@@ -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 @@
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()) {
diff --git a/runtime/vm/compiler/ffi/native_type.h b/runtime/vm/compiler/ffi/native_type.h
index 66d5f31..693b7c0 100644
--- a/runtime/vm/compiler/ffi/native_type.h
+++ b/runtime/vm/compiler/ffi/native_type.h
@@ -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 @@
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 @@
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 @@
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