mirror of
https://github.com/dart-lang/sdk
synced 2024-09-05 00:13:50 +00:00
[vm/ffi] DBC support on x64 Linux/MacOS hosts
Issue: https://github.com/dart-lang/sdk/issues/35773 Change-Id: Ib58fb8df6c5c18e604fbf2a156ab69025e5f57d7 Cq-Include-Trybots: luci.dart.try:vm-kernel-linux-debug-simdbc64-try, vm-kernel-linux-release-simdbc64-try, vm-kernel-mac-debug-simdbc64-try, vm-kernel-mac-release-simdbc64-try, vm-kernel-reload-mac-debug-simdbc64-try, vm-kernel-reload-mac-release-simdbc64-try, vm-kernel-linux-debug-ia32-try, vm-dartkb-linux-debug-simarm64-try Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/97221 Reviewed-by: Samir Jindel <sjindel@google.com> Commit-Queue: Daco Harkes <dacoharkes@google.com>
This commit is contained in:
parent
a0290f823c
commit
a8b93d9e78
|
@ -2,11 +2,13 @@
|
|||
// 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.h"
|
||||
#include "runtime/lib/ffi.h"
|
||||
|
||||
#include "include/dart_api.h"
|
||||
#include "vm/bootstrap_natives.h"
|
||||
#include "vm/class_finalizer.h"
|
||||
#include "vm/compiler/assembler/assembler.h"
|
||||
#include "vm/compiler/ffi.h"
|
||||
#include "vm/exceptions.h"
|
||||
#include "vm/log.h"
|
||||
#include "vm/native_arguments.h"
|
||||
|
@ -614,4 +616,113 @@ DEFINE_NATIVE_ENTRY(Ffi_fromFunction, 1, 1) {
|
|||
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<intptr_t>(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;
|
||||
}
|
||||
|
||||
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 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);
|
||||
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());
|
||||
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<intptr_t>(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 index = kOffsetNumStackSlots;
|
||||
const intptr_t num_stack_slots = data_[index];
|
||||
OS::PrintErr(" %02" Pd " 0x%" Pp " (number of stack slots)\n", index,
|
||||
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
|
||||
|
|
74
runtime/lib/ffi.h
Normal file
74
runtime/lib/ffi.h
Normal file
|
@ -0,0 +1,74 @@
|
|||
// 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.
|
||||
|
||||
#ifndef RUNTIME_LIB_FFI_H_
|
||||
#define RUNTIME_LIB_FFI_H_
|
||||
|
||||
#if defined(TARGET_ARCH_DBC)
|
||||
|
||||
#include <platform/globals.h>
|
||||
|
||||
#include "runtime/vm/class_id.h"
|
||||
#include "runtime/vm/compiler/backend/locations.h"
|
||||
#include "runtime/vm/compiler/ffi.h"
|
||||
#include "runtime/vm/object.h"
|
||||
#include "runtime/vm/raw_object.h"
|
||||
|
||||
namespace dart {
|
||||
|
||||
// This structure contains all data required for an ffi call.
|
||||
// It consists of the function address, all calling convention argument
|
||||
// register values, argument fpu register values, number of stack arguments,
|
||||
// and stack argument values. The generic DBC trampoline reads its arguments
|
||||
// from this structure.
|
||||
//
|
||||
// Moreover, the DBC trampoline also stores the integer and floating point
|
||||
// result registers in the first two slots when returning.
|
||||
class FfiMarshalledArguments : public ValueObject {
|
||||
public:
|
||||
explicit FfiMarshalledArguments(uint64_t* data) : data_(data) {}
|
||||
|
||||
// Copies ffi trampoline arguments (including target address) from stack into
|
||||
// a signature agnostic data structure (FfiMarshalledArguments) using the
|
||||
// signature and the stack address of the first argument. (Note that this
|
||||
// only works on DBC as the stack grows upwards in DBC.)
|
||||
static uint64_t* New(const compiler::ffi::FfiSignatureDescriptor& signature,
|
||||
const uint64_t* arg_values);
|
||||
|
||||
uint64_t IntResult() const { return data_[kOffsetIntResult]; }
|
||||
uint64_t DoubleResult() const { return data_[kOffsetDoubleResult]; }
|
||||
|
||||
#if defined(DEBUG)
|
||||
void Print() const;
|
||||
#endif
|
||||
|
||||
private:
|
||||
void SetFunctionAddress(uint64_t value) const;
|
||||
void SetRegister(::dart::host::Register reg, uint64_t value) const;
|
||||
void SetFpuRegister(::dart::host::FpuRegister reg, uint64_t value) const;
|
||||
void SetNumStackSlots(intptr_t num_args) const;
|
||||
intptr_t GetNumStackSlots() const;
|
||||
void SetStackSlotValue(intptr_t index, uint64_t value) const;
|
||||
|
||||
// TODO(36809): Replace this with uword. On 32 bit architecture,
|
||||
// this should be 32 bits, as the DBC stack itself is 32 bits.
|
||||
uint64_t* data_;
|
||||
|
||||
static const intptr_t kOffsetFunctionAddress = 0;
|
||||
static const intptr_t kOffsetRegisters = 1;
|
||||
static const intptr_t kOffsetFpuRegisters =
|
||||
kOffsetRegisters + ::dart::host::CallingConventions::kNumArgRegs;
|
||||
static const intptr_t kOffsetNumStackSlots =
|
||||
kOffsetFpuRegisters + ::dart::host::CallingConventions::kNumFpuArgRegs;
|
||||
static const intptr_t kOffsetStackSlotValues = kOffsetNumStackSlots + 1;
|
||||
|
||||
static const intptr_t kOffsetIntResult = 0;
|
||||
static const intptr_t kOffsetDoubleResult = 1;
|
||||
};
|
||||
|
||||
} // namespace dart
|
||||
|
||||
#endif // defined(TARGET_ARCH_DBC)
|
||||
|
||||
#endif // RUNTIME_LIB_FFI_H_
|
|
@ -5272,10 +5272,6 @@ void BitCastInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
|
|||
|
||||
#endif // defined(TARGET_ARCH_ARM)
|
||||
|
||||
#if !defined(TARGET_ARCH_DBC)
|
||||
|
||||
#define Z zone_
|
||||
|
||||
Representation FfiCallInstr::RequiredInputRepresentation(intptr_t idx) const {
|
||||
if (idx == TargetAddressIndex()) {
|
||||
return kUnboxedFfiIntPtr;
|
||||
|
@ -5284,6 +5280,10 @@ Representation FfiCallInstr::RequiredInputRepresentation(intptr_t idx) const {
|
|||
}
|
||||
}
|
||||
|
||||
#if !defined(TARGET_ARCH_DBC)
|
||||
|
||||
#define Z zone_
|
||||
|
||||
LocationSummary* FfiCallInstr::MakeLocationSummary(Zone* zone,
|
||||
bool is_optimizing) const {
|
||||
// The temporary register needs to be callee-saved and not an argument
|
||||
|
@ -5337,10 +5337,6 @@ LocationSummary* FfiCallInstr::MakeLocationSummary(Zone* zone,
|
|||
return summary;
|
||||
}
|
||||
|
||||
Representation FfiCallInstr::representation() const {
|
||||
return compiler::ffi::ResultRepresentation(signature_);
|
||||
}
|
||||
|
||||
Location FfiCallInstr::UnallocateStackSlots(Location in, bool is_atomic) {
|
||||
if (in.IsPairLocation()) {
|
||||
ASSERT(!is_atomic);
|
||||
|
@ -5361,21 +5357,34 @@ Location FfiCallInstr::UnallocateStackSlots(Location in, bool is_atomic) {
|
|||
|
||||
#else
|
||||
|
||||
Representation FfiCallInstr::RequiredInputRepresentation(intptr_t idx) const {
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
LocationSummary* FfiCallInstr::MakeLocationSummary(Zone* zone,
|
||||
bool is_optimizing) const {
|
||||
UNREACHABLE();
|
||||
}
|
||||
LocationSummary* summary =
|
||||
new (zone) LocationSummary(zone, /*num_inputs=*/InputCount(),
|
||||
/*num_temps=*/0, LocationSummary::kCall);
|
||||
|
||||
Representation FfiCallInstr::representation() const {
|
||||
UNREACHABLE();
|
||||
summary->set_in(
|
||||
TargetAddressIndex(),
|
||||
Location::RegisterLocation(compiler::ffi::kFunctionAddressRegister));
|
||||
for (intptr_t i = 0, n = NativeArgCount(); i < n; ++i) {
|
||||
summary->set_in(i, arg_locations_[i]);
|
||||
}
|
||||
summary->set_out(0, compiler::ffi::ResultLocation(
|
||||
compiler::ffi::ResultHostRepresentation(signature_)));
|
||||
|
||||
return summary;
|
||||
}
|
||||
|
||||
#endif // !defined(TARGET_ARCH_DBC)
|
||||
|
||||
Representation FfiCallInstr::representation() const {
|
||||
#if !defined(TARGET_ARCH_DBC)
|
||||
return compiler::ffi::ResultRepresentation(signature_);
|
||||
#else
|
||||
return compiler::ffi::ResultHostRepresentation(signature_);
|
||||
#endif // !defined(TARGET_ARCH_DBC)
|
||||
}
|
||||
|
||||
// SIMD
|
||||
|
||||
SimdOpInstr* SimdOpInstr::CreateFromCall(Zone* zone,
|
||||
|
|
|
@ -4162,13 +4162,15 @@ class FfiCallInstr : public Definition {
|
|||
intptr_t deopt_id,
|
||||
const Function& signature,
|
||||
const ZoneGrowableArray<Representation>& arg_reps,
|
||||
const ZoneGrowableArray<Location>& arg_locs)
|
||||
const ZoneGrowableArray<Location>& arg_locs,
|
||||
const ZoneGrowableArray<HostLocation>* arg_host_locs = nullptr)
|
||||
: Definition(deopt_id),
|
||||
zone_(zone),
|
||||
signature_(signature),
|
||||
inputs_(arg_reps.length() + 1),
|
||||
arg_representations_(arg_reps),
|
||||
arg_locations_(arg_locs) {
|
||||
arg_locations_(arg_locs),
|
||||
arg_host_locations_(arg_host_locs) {
|
||||
inputs_.FillWith(nullptr, 0, arg_reps.length() + 1);
|
||||
ASSERT(signature.IsZoneHandle());
|
||||
}
|
||||
|
@ -4208,6 +4210,7 @@ class FfiCallInstr : public Definition {
|
|||
GrowableArray<Value*> inputs_;
|
||||
const ZoneGrowableArray<Representation>& arg_representations_;
|
||||
const ZoneGrowableArray<Location>& arg_locations_;
|
||||
const ZoneGrowableArray<HostLocation>* arg_host_locations_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(FfiCallInstr);
|
||||
};
|
||||
|
|
|
@ -992,7 +992,16 @@ EMIT_NATIVE_CODE(StringInterpolate,
|
|||
}
|
||||
|
||||
void FfiCallInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
|
||||
UNREACHABLE();
|
||||
const Representation result_rep =
|
||||
compiler::ffi::ResultHostRepresentation(signature_);
|
||||
// TODO(36809): In 32 bit we'll need a result location as well.
|
||||
const TypedData& signature_descriptor =
|
||||
TypedData::Handle(compiler::ffi::FfiSignatureDescriptor::New(
|
||||
*arg_host_locations_, result_rep));
|
||||
|
||||
const intptr_t sigdesc_kidx = __ AddConstant(signature_descriptor);
|
||||
|
||||
__ FfiCall(sigdesc_kidx);
|
||||
}
|
||||
|
||||
EMIT_NATIVE_CODE(NativeCall,
|
||||
|
@ -1704,9 +1713,13 @@ void BoxInstr::EmitAllocateBox(FlowGraphCompiler* compiler) {
|
|||
}
|
||||
|
||||
EMIT_NATIVE_CODE(Box, 1, Location::RequiresRegister(), LocationSummary::kCall) {
|
||||
ASSERT(from_representation() == kUnboxedDouble);
|
||||
ASSERT(from_representation() == kUnboxedDouble ||
|
||||
from_representation() == kUnboxedFloat);
|
||||
const Register value = locs()->in(0).reg();
|
||||
const Register out = locs()->out(0).reg();
|
||||
if (from_representation() == kUnboxedFloat) {
|
||||
__ FloatToDouble(value, value);
|
||||
}
|
||||
EmitAllocateBox(compiler);
|
||||
__ WriteIntoDouble(out, value);
|
||||
}
|
||||
|
@ -1716,24 +1729,31 @@ EMIT_NATIVE_CODE(Unbox, 1, Location::RequiresRegister()) {
|
|||
EmitLoadInt64FromBoxOrSmi(compiler);
|
||||
return;
|
||||
}
|
||||
ASSERT(representation() == kUnboxedDouble);
|
||||
ASSERT(representation() == kUnboxedDouble ||
|
||||
representation() == kUnboxedFloat);
|
||||
const intptr_t value_cid = value()->Type()->ToCid();
|
||||
const intptr_t box_cid = BoxCid();
|
||||
const Register box = locs()->in(0).reg();
|
||||
const Register result = locs()->out(0).reg();
|
||||
if (value_cid == box_cid) {
|
||||
if (value_cid == box_cid ||
|
||||
(speculative_mode() == kNotSpeculative && value_cid != kSmiCid)) {
|
||||
__ UnboxDouble(result, box);
|
||||
} else if (CanConvertSmi() && (value_cid == kSmiCid)) {
|
||||
__ SmiToDouble(result, box);
|
||||
} else if ((value()->Type()->ToNullableCid() == box_cid) &&
|
||||
value()->Type()->is_nullable()) {
|
||||
__ IfEqNull(box);
|
||||
ASSERT(CanDeoptimize());
|
||||
compiler->EmitDeopt(GetDeoptId(), ICData::kDeoptCheckClass);
|
||||
__ UnboxDouble(result, box);
|
||||
} else {
|
||||
__ CheckedUnboxDouble(result, box);
|
||||
ASSERT(CanDeoptimize());
|
||||
compiler->EmitDeopt(GetDeoptId(), ICData::kDeoptCheckClass);
|
||||
}
|
||||
if (representation() == kUnboxedFloat) {
|
||||
__ DoubleToFloat(result, result);
|
||||
}
|
||||
}
|
||||
|
||||
EMIT_NATIVE_CODE(UnboxInteger32, 1, Location::RequiresRegister()) {
|
||||
|
|
|
@ -407,6 +407,9 @@ class TemplateLocation : public ValueObject {
|
|||
|
||||
TemplateLocation Copy() const;
|
||||
|
||||
static TemplateLocation read(uword value) { return TemplateLocation(value); }
|
||||
uword write() const { return value_; }
|
||||
|
||||
private:
|
||||
explicit TemplateLocation(uword value) : value_(value) {}
|
||||
|
||||
|
|
|
@ -146,6 +146,17 @@ compiler_sources = [
|
|||
"stub_code_compiler_x64.cc",
|
||||
]
|
||||
|
||||
# The most common way to include assembly files in C projects is to have
|
||||
# separate assembly files per architecture and operating system (for example
|
||||
# boringssl).
|
||||
#
|
||||
# Not that this diverges from our convention to build every file on every OS
|
||||
# but have ifdef guards which make the files empty on some configurations.
|
||||
if (is_linux || is_mac) {
|
||||
# MASM on Windows does not support c preproccesor style flags.
|
||||
compiler_sources += [ "ffi_dbc_trampoline_x64_linux_mac.S" ]
|
||||
}
|
||||
|
||||
compiler_sources_tests = [
|
||||
"assembler/assembler_arm64_test.cc",
|
||||
"assembler/assembler_arm_test.cc",
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#include <algorithm>
|
||||
|
||||
#include "platform/globals.h"
|
||||
#include "vm/compiler/backend/locations.h"
|
||||
#include "vm/compiler/runtime_api.h"
|
||||
|
||||
namespace dart {
|
||||
|
@ -88,8 +89,6 @@ SmallRepresentation TypeSmallRepresentation(const AbstractType& ffi_type) {
|
|||
}
|
||||
}
|
||||
|
||||
#if !defined(TARGET_ARCH_DBC)
|
||||
|
||||
bool NativeTypeIsVoid(const AbstractType& result_type) {
|
||||
return result_type.type_class_id() == kFfiVoidCid;
|
||||
}
|
||||
|
@ -117,7 +116,8 @@ bool NativeTypeIsPointer(const AbstractType& result_type) {
|
|||
|
||||
// Converts a Ffi [signature] to a list of Representations.
|
||||
// Note that this ignores first argument (receiver) which is dynamic.
|
||||
ZoneGrowableArray<Representation>* ArgumentRepresentations(
|
||||
template <class CallingConventions>
|
||||
ZoneGrowableArray<Representation>* ArgumentRepresentationsBase(
|
||||
const Function& signature) {
|
||||
intptr_t num_arguments = signature.num_fixed_parameters() - 1;
|
||||
auto result = new ZoneGrowableArray<Representation>(num_arguments);
|
||||
|
@ -125,6 +125,8 @@ ZoneGrowableArray<Representation>* ArgumentRepresentations(
|
|||
AbstractType& arg_type =
|
||||
AbstractType::Handle(signature.ParameterTypeAt(i + 1));
|
||||
Representation rep = TypeRepresentation(arg_type);
|
||||
// In non simulator mode host::CallingConventions == CallingConventions.
|
||||
// In simulator mode convert arguments to host representation.
|
||||
if (rep == kUnboxedFloat && CallingConventions::kAbiSoftFP) {
|
||||
rep = kUnboxedInt32;
|
||||
} else if (rep == kUnboxedDouble && CallingConventions::kAbiSoftFP) {
|
||||
|
@ -135,9 +137,51 @@ ZoneGrowableArray<Representation>* ArgumentRepresentations(
|
|||
return result;
|
||||
}
|
||||
|
||||
template <class CallingConventions>
|
||||
Representation ResultRepresentationBase(const Function& signature) {
|
||||
AbstractType& arg_type = AbstractType::Handle(signature.result_type());
|
||||
Representation rep = TypeRepresentation(arg_type);
|
||||
if (rep == kUnboxedFloat && CallingConventions::kAbiSoftFP) {
|
||||
rep = kUnboxedInt32;
|
||||
} else if (rep == kUnboxedDouble && CallingConventions::kAbiSoftFP) {
|
||||
rep = kUnboxedInt64;
|
||||
}
|
||||
return rep;
|
||||
}
|
||||
|
||||
#if !defined(TARGET_ARCH_DBC)
|
||||
|
||||
ZoneGrowableArray<Representation>* ArgumentRepresentations(
|
||||
const Function& signature) {
|
||||
return ArgumentRepresentationsBase<CallingConventions>(signature);
|
||||
}
|
||||
|
||||
Representation ResultRepresentation(const Function& signature) {
|
||||
return ResultRepresentationBase<CallingConventions>(signature);
|
||||
}
|
||||
|
||||
#endif // !defined(TARGET_ARCH_DBC)
|
||||
|
||||
#if defined(USING_SIMULATOR)
|
||||
|
||||
ZoneGrowableArray<Representation>* ArgumentHostRepresentations(
|
||||
const Function& signature) {
|
||||
return ArgumentRepresentationsBase<host::CallingConventions>(signature);
|
||||
}
|
||||
|
||||
Representation ResultHostRepresentation(const Function& signature) {
|
||||
return ResultRepresentationBase<host::CallingConventions>(signature);
|
||||
}
|
||||
|
||||
#endif // defined(USING_SIMULATOR)
|
||||
|
||||
// Represents the state of a stack frame going into a call, between allocations
|
||||
// of argument locations. Acts like a register allocator but for arguments in
|
||||
// the native ABI.
|
||||
template <class CallingConventions,
|
||||
class Location,
|
||||
class Register,
|
||||
class FpuRegister>
|
||||
class ArgumentFrameState : public ValueObject {
|
||||
public:
|
||||
Location AllocateArgument(Representation rep) {
|
||||
|
@ -173,7 +217,8 @@ class ArgumentFrameState : public ValueObject {
|
|||
|
||||
private:
|
||||
Location AllocateStackSlot() {
|
||||
return Location::StackSlot(stack_height_in_slots++, SPREG);
|
||||
return Location::StackSlot(stack_height_in_slots++,
|
||||
CallingConventions::kStackPointerRegister);
|
||||
}
|
||||
|
||||
// Allocates a pair of stack slots where the first stack slot is aligned to an
|
||||
|
@ -186,7 +231,8 @@ class ArgumentFrameState : public ValueObject {
|
|||
|
||||
Location result;
|
||||
if (rep == kUnboxedDouble) {
|
||||
result = Location::DoubleStackSlot(stack_height_in_slots, SPREG);
|
||||
result = Location::DoubleStackSlot(
|
||||
stack_height_in_slots, CallingConventions::kStackPointerRegister);
|
||||
stack_height_in_slots += 2;
|
||||
} else {
|
||||
const Location low = AllocateStackSlot();
|
||||
|
@ -243,13 +289,18 @@ class ArgumentFrameState : public ValueObject {
|
|||
|
||||
// Takes a list of argument representations, and converts it to a list of
|
||||
// argument locations based on calling convention.
|
||||
ZoneGrowableArray<Location>* ArgumentLocations(
|
||||
template <class CallingConventions,
|
||||
class Location,
|
||||
class Register,
|
||||
class FpuRegister>
|
||||
ZoneGrowableArray<Location>* ArgumentLocationsBase(
|
||||
const ZoneGrowableArray<Representation>& arg_reps) {
|
||||
intptr_t num_arguments = arg_reps.length();
|
||||
auto result = new ZoneGrowableArray<Location>(num_arguments);
|
||||
|
||||
// Loop through all arguments and assign a register or a stack location.
|
||||
ArgumentFrameState frame_state;
|
||||
ArgumentFrameState<CallingConventions, Location, Register, FpuRegister>
|
||||
frame_state;
|
||||
for (intptr_t i = 0; i < num_arguments; i++) {
|
||||
Representation rep = arg_reps[i];
|
||||
result->Add(frame_state.AllocateArgument(rep));
|
||||
|
@ -257,18 +308,35 @@ ZoneGrowableArray<Location>* ArgumentLocations(
|
|||
return result;
|
||||
}
|
||||
|
||||
Representation ResultRepresentation(const Function& signature) {
|
||||
AbstractType& arg_type = AbstractType::Handle(signature.result_type());
|
||||
Representation rep = TypeRepresentation(arg_type);
|
||||
if (rep == kUnboxedFloat && CallingConventions::kAbiSoftFP) {
|
||||
rep = kUnboxedInt32;
|
||||
} else if (rep == kUnboxedDouble && CallingConventions::kAbiSoftFP) {
|
||||
rep = kUnboxedInt64;
|
||||
ZoneGrowableArray<Location>* ArgumentLocations(
|
||||
const ZoneGrowableArray<Representation>& arg_reps) {
|
||||
#if !defined(TARGET_ARCH_DBC)
|
||||
return ArgumentLocationsBase<dart::CallingConventions, Location,
|
||||
dart::Register, dart::FpuRegister>(arg_reps);
|
||||
#else
|
||||
intptr_t next_free_register = compiler::ffi::kFirstArgumentRegister;
|
||||
intptr_t num_arguments = arg_reps.length();
|
||||
auto result = new ZoneGrowableArray<Location>(num_arguments);
|
||||
for (intptr_t i = 0; i < num_arguments; i++) {
|
||||
// TODO(dacoharkes): In 32 bits, use pair locations.
|
||||
result->Add(Location::RegisterLocation(next_free_register));
|
||||
next_free_register++;
|
||||
}
|
||||
return rep;
|
||||
return result;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(TARGET_ARCH_DBC)
|
||||
ZoneGrowableArray<HostLocation>* HostArgumentLocations(
|
||||
const ZoneGrowableArray<Representation>& arg_reps) {
|
||||
return ArgumentLocationsBase<dart::host::CallingConventions, HostLocation,
|
||||
dart::host::Register, dart::host::FpuRegister>(
|
||||
arg_reps);
|
||||
}
|
||||
#endif
|
||||
|
||||
Location ResultLocation(Representation result_rep) {
|
||||
#ifndef TARGET_ARCH_DBC
|
||||
switch (result_rep) {
|
||||
case kUnboxedFloat:
|
||||
case kUnboxedDouble:
|
||||
|
@ -293,10 +361,15 @@ Location ResultLocation(Representation result_rep) {
|
|||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
#else
|
||||
// TODO(dacoharkes): Support 64 bit result values on 32 bit DBC.
|
||||
return Location::RegisterLocation(0);
|
||||
#endif
|
||||
}
|
||||
|
||||
// Accounts for alignment, where some stack slots are used as padding.
|
||||
intptr_t NumStackSlots(const ZoneGrowableArray<Location>& locations) {
|
||||
template <class Location>
|
||||
intptr_t TemplateNumStackSlots(const ZoneGrowableArray<Location>& locations) {
|
||||
intptr_t num_arguments = locations.length();
|
||||
intptr_t max_height_in_slots = 0;
|
||||
for (intptr_t i = 0; i < num_arguments; i++) {
|
||||
|
@ -316,7 +389,88 @@ intptr_t NumStackSlots(const ZoneGrowableArray<Location>& locations) {
|
|||
return max_height_in_slots;
|
||||
}
|
||||
|
||||
#endif // !defined(TARGET_ARCH_DBC)
|
||||
intptr_t NumStackSlots(const ZoneGrowableArray<Location>& locations) {
|
||||
return TemplateNumStackSlots(locations);
|
||||
}
|
||||
|
||||
#if defined(TARGET_ARCH_DBC)
|
||||
|
||||
static RawTypedData* typed_data_new_uintptr(intptr_t length) {
|
||||
#if defined(ARCH_IS_32_BIT)
|
||||
return TypedData::New(kTypedDataUint32ArrayCid, length);
|
||||
#else
|
||||
return TypedData::New(kTypedDataUint64ArrayCid, length);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void typed_data_set_uintptr(const TypedData& typed_data,
|
||||
intptr_t index,
|
||||
uintptr_t value) {
|
||||
#if defined(ARCH_IS_32_BIT)
|
||||
typed_data.SetUint32(target::kWordSize * index, value);
|
||||
#else
|
||||
typed_data.SetUint64(target::kWordSize * index, value);
|
||||
#endif
|
||||
}
|
||||
|
||||
static uintptr_t typed_data_get_uintptr(const TypedData& typed_data,
|
||||
intptr_t index) {
|
||||
#if defined(ARCH_IS_32_BIT)
|
||||
return typed_data.GetUint32(target::kWordSize * index);
|
||||
#else
|
||||
return typed_data.GetUint64(target::kWordSize * index);
|
||||
#endif
|
||||
}
|
||||
|
||||
// Number of host stack slots used in 'locations'.
|
||||
static intptr_t HostNumStackSlots(
|
||||
const ZoneGrowableArray<HostLocation>& locations) {
|
||||
return TemplateNumStackSlots(locations);
|
||||
}
|
||||
|
||||
RawTypedData* FfiSignatureDescriptor::New(
|
||||
const ZoneGrowableArray<HostLocation>& arg_host_locations,
|
||||
const Representation result_representation) {
|
||||
const uintptr_t num_arguments = arg_host_locations.length();
|
||||
const uintptr_t num_stack_slots = HostNumStackSlots(arg_host_locations);
|
||||
|
||||
const TypedData& result = TypedData::Handle(
|
||||
typed_data_new_uintptr(kOffsetArgumentLocations + num_arguments));
|
||||
|
||||
typed_data_set_uintptr(result, kOffsetNumArguments, num_arguments);
|
||||
typed_data_set_uintptr(result, kOffsetNumStackSlots, num_stack_slots);
|
||||
typed_data_set_uintptr(result, kOffsetResultRepresentation,
|
||||
result_representation);
|
||||
|
||||
for (uintptr_t i = 0; i < num_arguments; i++) {
|
||||
typed_data_set_uintptr(result, kOffsetArgumentLocations + i,
|
||||
arg_host_locations.At(i).write());
|
||||
}
|
||||
|
||||
return result.raw();
|
||||
}
|
||||
|
||||
intptr_t FfiSignatureDescriptor::length() const {
|
||||
return typed_data_get_uintptr(typed_data_, kOffsetNumArguments);
|
||||
}
|
||||
|
||||
intptr_t FfiSignatureDescriptor::num_stack_slots() const {
|
||||
return typed_data_get_uintptr(typed_data_, kOffsetNumStackSlots);
|
||||
}
|
||||
|
||||
HostLocation FfiSignatureDescriptor::LocationAt(intptr_t index) const {
|
||||
return HostLocation::read(
|
||||
typed_data_get_uintptr(typed_data_, kOffsetArgumentLocations + index));
|
||||
}
|
||||
|
||||
Representation FfiSignatureDescriptor::ResultRepresentation() const {
|
||||
uintptr_t result_int =
|
||||
typed_data_get_uintptr(typed_data_, kOffsetResultRepresentation);
|
||||
ASSERT(result_int < kNumRepresentations);
|
||||
return static_cast<Representation>(result_int);
|
||||
}
|
||||
|
||||
#endif // defined(TARGET_ARCH_DBC)
|
||||
|
||||
#endif // !defined(DART_PRECOMPILED_RUNTIME)
|
||||
|
||||
|
|
|
@ -38,16 +38,31 @@ bool NativeTypeIsPointer(const AbstractType& result_type);
|
|||
// Whether a type is 'ffi.Void'.
|
||||
bool NativeTypeIsVoid(const AbstractType& result_type);
|
||||
|
||||
// Unboxed representation of the result of a C signature function.
|
||||
Representation ResultRepresentation(const Function& signature);
|
||||
|
||||
// Location for the result of a C signature function.
|
||||
Location ResultLocation(Representation result_rep);
|
||||
|
||||
#if !defined(TARGET_ARCH_DBC)
|
||||
|
||||
// Unboxed representations of the arguments to a C signature function.
|
||||
ZoneGrowableArray<Representation>* ArgumentRepresentations(
|
||||
const Function& signature);
|
||||
|
||||
// Unboxed representation of the result of a C signature function.
|
||||
Representation ResultRepresentation(const Function& signature);
|
||||
|
||||
#endif // !defined(TARGET_ARCH_DBC)
|
||||
|
||||
#if defined(USING_SIMULATOR)
|
||||
|
||||
// Unboxed host representations of the arguments to a C signature function.
|
||||
ZoneGrowableArray<Representation>* ArgumentHostRepresentations(
|
||||
const Function& signature);
|
||||
|
||||
// Unboxed host representation of the result of a C signature function.
|
||||
Representation ResultHostRepresentation(const Function& signature);
|
||||
|
||||
#endif // defined(USING_SIMULATOR)
|
||||
|
||||
// Location for the arguments of a C signature function.
|
||||
ZoneGrowableArray<Location>* ArgumentLocations(
|
||||
const ZoneGrowableArray<Representation>& arg_reps);
|
||||
|
@ -55,6 +70,44 @@ ZoneGrowableArray<Location>* ArgumentLocations(
|
|||
// Number of stack slots used in 'locations'.
|
||||
intptr_t NumStackSlots(const ZoneGrowableArray<Location>& locations);
|
||||
|
||||
#if defined(TARGET_ARCH_DBC)
|
||||
|
||||
// The first argument to a ffi trampoline is the function address, the arguments
|
||||
// to the call follow the function address.
|
||||
const intptr_t kFunctionAddressRegister = 0;
|
||||
const intptr_t kFirstArgumentRegister = 1;
|
||||
|
||||
// Location in host for the arguments of a C signature function.
|
||||
ZoneGrowableArray<HostLocation>* HostArgumentLocations(
|
||||
const ZoneGrowableArray<Representation>& arg_reps);
|
||||
|
||||
// A signature descriptor consists of the signature length, argument locations,
|
||||
// and result representation.
|
||||
class FfiSignatureDescriptor : public ValueObject {
|
||||
public:
|
||||
explicit FfiSignatureDescriptor(const TypedData& typed_data)
|
||||
: typed_data_(typed_data) {}
|
||||
|
||||
static RawTypedData* New(
|
||||
const ZoneGrowableArray<HostLocation>& arg_host_locations,
|
||||
const Representation result_representation);
|
||||
|
||||
intptr_t length() const;
|
||||
intptr_t num_stack_slots() const;
|
||||
HostLocation LocationAt(intptr_t index) const;
|
||||
Representation ResultRepresentation() const;
|
||||
|
||||
private:
|
||||
const TypedData& typed_data_;
|
||||
|
||||
static const intptr_t kOffsetNumArguments = 0;
|
||||
static const intptr_t kOffsetNumStackSlots = 1;
|
||||
static const intptr_t kOffsetResultRepresentation = 2;
|
||||
static const intptr_t kOffsetArgumentLocations = 3;
|
||||
};
|
||||
|
||||
#endif // defined(TARGET_ARCH_DBC)
|
||||
|
||||
} // namespace ffi
|
||||
|
||||
} // namespace compiler
|
||||
|
|
28
runtime/vm/compiler/ffi_dbc_trampoline.h
Normal file
28
runtime/vm/compiler/ffi_dbc_trampoline.h
Normal file
|
@ -0,0 +1,28 @@
|
|||
// 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.
|
||||
|
||||
#ifndef RUNTIME_VM_COMPILER_FFI_DBC_TRAMPOLINE_H_
|
||||
#define RUNTIME_VM_COMPILER_FFI_DBC_TRAMPOLINE_H_
|
||||
|
||||
#include "vm/globals.h"
|
||||
|
||||
namespace dart {
|
||||
|
||||
#if defined(HOST_ARCH_X64) && !defined(HOST_OS_WINDOWS)
|
||||
|
||||
// Generic Trampoline for DBC dart:ffi calls. Argument needs to be layed out as
|
||||
// a FfiMarshalledArguments.
|
||||
extern "C" void FfiTrampolineCall(uint64_t* ffi_marshalled_args);
|
||||
|
||||
#else
|
||||
|
||||
void FfiTrampolineCall(uint64_t* ffi_marshalled_args) {
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
#endif // defined(HOST_ARCH_X64) && !defined(HOST_OS_WINDOWS)
|
||||
|
||||
} // namespace dart
|
||||
|
||||
#endif // RUNTIME_VM_COMPILER_FFI_DBC_TRAMPOLINE_H_
|
63
runtime/vm/compiler/ffi_dbc_trampoline_x64_linux_mac.S
Normal file
63
runtime/vm/compiler/ffi_dbc_trampoline_x64_linux_mac.S
Normal file
|
@ -0,0 +1,63 @@
|
|||
#if defined(_M_X64) || defined(__x86_64__) /* HOST_ARCH_X64 */
|
||||
|
||||
.intel_syntax noprefix
|
||||
.text
|
||||
|
||||
#if defined(__linux__) || defined(__FreeBSD__) /* HOST_OS_LINUX */
|
||||
.globl FfiTrampolineCall
|
||||
.type FfiTrampolineCall, @function
|
||||
FfiTrampolineCall:
|
||||
#else /* HOST_OS_MACOS */
|
||||
.globl _FfiTrampolineCall
|
||||
_FfiTrampolineCall:
|
||||
#endif
|
||||
|
||||
push rbx
|
||||
mov rbx, rdi /* Save argument in scratch register. */
|
||||
|
||||
/* Copy stack arguments. */
|
||||
mov rax, [rbx+0x78] /* Load number of stack arguments. */
|
||||
cmp rax, 0x0 /* Check if number of stack arguments is 0. */
|
||||
jz 2f /* Skip loop if no stack arguments. */
|
||||
add rbx, 0x78 /* Offset RBX to point to stack arguments */
|
||||
1: /* Copy stack arguments loop. */
|
||||
push [rbx+0x8*rax] /* Push stack argument. */
|
||||
sub rax, 0x1 /* Decrement stack argument iterator. */
|
||||
cmp rax, 0x0 /* Compare iterator with 0 */
|
||||
jnz 1b /* Loop while iterator is not 0 */
|
||||
sub rbx, 0x78 /* Restore RBX to original value. */
|
||||
2: /* End stack arguments loop. */
|
||||
|
||||
/* Copy registers and fpu registers. */
|
||||
mov rdi, [rbx+0x8] /* kArg1Reg */
|
||||
mov rsi, [rbx+0x10] /* kArg2Reg */
|
||||
mov rdx, [rbx+0x18] /* kArg3Reg */
|
||||
mov rcx, [rbx+0x20] /* kArg4Reg */
|
||||
mov r8, [rbx+0x28] /* kArg5Reg */
|
||||
mov r9, [rbx+0x30] /* kArg6Reg */
|
||||
movsd xmm0, [rbx+0x38]
|
||||
movsd xmm1, [rbx+0x40]
|
||||
movsd xmm2, [rbx+0x48]
|
||||
movsd xmm3, [rbx+0x50]
|
||||
movsd xmm4, [rbx+0x58]
|
||||
movsd xmm5, [rbx+0x60]
|
||||
movsd xmm6, [rbx+0x68]
|
||||
movsd xmm7, [rbx+0x70]
|
||||
|
||||
/* Do call. */
|
||||
mov rax, [rbx] /* function address */
|
||||
call rax /* Call the function. */
|
||||
|
||||
/* Copy results back. */
|
||||
mov [rbx], rax /* Move integer result in kOffsetIntResult */
|
||||
movsd [rbx+8], xmm0 /* Move double result in kOffsetDoubleResult */
|
||||
|
||||
/* Clean up stack arguments. */
|
||||
mov rax, [rbx+0x78] /* Load number of stack arguments. */
|
||||
imul rax, 0x8 /* Multiply by stack argument size. */
|
||||
add rsp, rax /* Clean up the stack. */
|
||||
|
||||
pop rbx
|
||||
ret
|
||||
|
||||
#endif /* HOST_ARCH_X64 */
|
|
@ -3,11 +3,12 @@
|
|||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
#include "vm/compiler/frontend/kernel_to_il.h"
|
||||
#include "vm/compiler/aot/precompiler.h"
|
||||
#include "vm/compiler/backend/locations.h"
|
||||
|
||||
#include "platform/assert.h"
|
||||
#include "vm/compiler/aot/precompiler.h"
|
||||
#include "vm/compiler/backend/il.h"
|
||||
#include "vm/compiler/backend/il_printer.h"
|
||||
#include "vm/compiler/backend/locations.h"
|
||||
#include "vm/compiler/ffi.h"
|
||||
#include "vm/compiler/frontend/kernel_binary_flowgraph.h"
|
||||
#include "vm/compiler/frontend/kernel_translation_helper.h"
|
||||
|
@ -388,11 +389,12 @@ Fragment FlowGraphBuilder::ClosureCall(TokenPosition position,
|
|||
Fragment FlowGraphBuilder::FfiCall(
|
||||
const Function& signature,
|
||||
const ZoneGrowableArray<Representation>& arg_reps,
|
||||
const ZoneGrowableArray<Location>& arg_locs) {
|
||||
const ZoneGrowableArray<Location>& arg_locs,
|
||||
const ZoneGrowableArray<HostLocation>* arg_host_locs) {
|
||||
Fragment body;
|
||||
|
||||
FfiCallInstr* call =
|
||||
new (Z) FfiCallInstr(Z, GetNextDeoptId(), signature, arg_reps, arg_locs);
|
||||
FfiCallInstr* const call = new (Z) FfiCallInstr(
|
||||
Z, GetNextDeoptId(), signature, arg_reps, arg_locs, arg_host_locs);
|
||||
|
||||
for (intptr_t i = call->InputCount() - 1; i >= 0; --i) {
|
||||
call->SetInputAt(i, Pop());
|
||||
|
@ -2498,7 +2500,6 @@ Fragment FlowGraphBuilder::BitCast(Representation from, Representation to) {
|
|||
|
||||
FlowGraph* FlowGraphBuilder::BuildGraphOfFfiTrampoline(
|
||||
const Function& function) {
|
||||
#if !defined(TARGET_ARCH_DBC)
|
||||
graph_entry_ =
|
||||
new (Z) GraphEntryInstr(*parsed_function_, Compiler::kNoOSRDeoptId);
|
||||
|
||||
|
@ -2514,7 +2515,13 @@ FlowGraph* FlowGraphBuilder::BuildGraphOfFfiTrampoline(
|
|||
body += CheckStackOverflowInPrologue(function.token_pos());
|
||||
|
||||
const Function& signature = Function::ZoneHandle(Z, function.FfiCSignature());
|
||||
#if !defined(TARGET_ARCH_DBC)
|
||||
const auto& arg_reps = *compiler::ffi::ArgumentRepresentations(signature);
|
||||
const ZoneGrowableArray<HostLocation>* arg_host_locs = nullptr;
|
||||
#else
|
||||
const auto& arg_reps = *compiler::ffi::ArgumentHostRepresentations(signature);
|
||||
const auto* arg_host_locs = compiler::ffi::HostArgumentLocations(arg_reps);
|
||||
#endif
|
||||
const auto& arg_locs = *compiler::ffi::ArgumentLocations(arg_reps);
|
||||
|
||||
BuildArgumentTypeChecks(TypeChecksToBuild::kCheckAllTypeParameterBounds,
|
||||
|
@ -2559,7 +2566,7 @@ FlowGraph* FlowGraphBuilder::BuildGraphOfFfiTrampoline(
|
|||
Z, Class::Handle(I->object_store()->ffi_pointer_class()))
|
||||
->context_variables()[0]));
|
||||
body += UnboxTruncate(kUnboxedFfiIntPtr);
|
||||
body += FfiCall(signature, arg_reps, arg_locs);
|
||||
body += FfiCall(signature, arg_reps, arg_locs, arg_host_locs);
|
||||
|
||||
ffi_type = signature.result_type();
|
||||
if (compiler::ffi::NativeTypeIsPointer(ffi_type)) {
|
||||
|
@ -2569,7 +2576,12 @@ FlowGraph* FlowGraphBuilder::BuildGraphOfFfiTrampoline(
|
|||
body += Drop();
|
||||
body += NullConstant();
|
||||
} else {
|
||||
#if !defined(TARGET_ARCH_DBC)
|
||||
Representation from_rep = compiler::ffi::ResultRepresentation(signature);
|
||||
#else
|
||||
Representation from_rep =
|
||||
compiler::ffi::ResultHostRepresentation(signature);
|
||||
#endif // !defined(TARGET_ARCH_DBC)
|
||||
Representation to_rep = compiler::ffi::TypeRepresentation(ffi_type);
|
||||
if (from_rep != to_rep) {
|
||||
body += BitCast(from_rep, to_rep);
|
||||
|
@ -2583,9 +2595,6 @@ FlowGraph* FlowGraphBuilder::BuildGraphOfFfiTrampoline(
|
|||
|
||||
return new (Z) FlowGraph(*parsed_function_, graph_entry_, last_used_block_id_,
|
||||
prologue_info);
|
||||
#else
|
||||
UNREACHABLE();
|
||||
#endif
|
||||
}
|
||||
|
||||
void FlowGraphBuilder::SetCurrentTryCatchBlock(TryCatchBlock* try_catch_block) {
|
||||
|
|
|
@ -151,9 +151,12 @@ class FlowGraphBuilder : public BaseFlowGraphBuilder {
|
|||
intptr_t argument_count,
|
||||
const Array& argument_names,
|
||||
bool use_unchecked_entry = false);
|
||||
Fragment FfiCall(const Function& signature,
|
||||
const ZoneGrowableArray<Representation>& arg_reps,
|
||||
const ZoneGrowableArray<Location>& arg_locs);
|
||||
|
||||
Fragment FfiCall(
|
||||
const Function& signature,
|
||||
const ZoneGrowableArray<Representation>& arg_reps,
|
||||
const ZoneGrowableArray<Location>& arg_locs,
|
||||
const ZoneGrowableArray<HostLocation>* arg_host_locs = nullptr);
|
||||
|
||||
Fragment RethrowException(TokenPosition position, int catch_try_index);
|
||||
Fragment LoadLocal(LocalVariable* variable);
|
||||
|
|
|
@ -885,6 +885,8 @@ static RawObject* CompileFunctionHelper(CompilationPipeline* pipeline,
|
|||
if (optimized) {
|
||||
if (error.IsLanguageError() &&
|
||||
LanguageError::Cast(error).kind() == Report::kBailout) {
|
||||
// Functions which cannot deoptimize should never bail out.
|
||||
ASSERT(!function.ForceOptimize());
|
||||
// Optimizer bailed out. Disable optimizations and never try again.
|
||||
if (trace_compiler) {
|
||||
THR_Print("--> disabling optimizations for '%s'\n",
|
||||
|
@ -1160,8 +1162,8 @@ RawError* Compiler::CompileAllFunctions(const Class& cls) {
|
|||
for (int i = 0; i < functions.Length(); i++) {
|
||||
func ^= functions.At(i);
|
||||
ASSERT(!func.IsNull());
|
||||
if (!func.HasCode() &&
|
||||
!func.is_abstract() && !func.IsRedirectingFactory()) {
|
||||
if (!func.HasCode() && !func.is_abstract() &&
|
||||
!func.IsRedirectingFactory()) {
|
||||
result = CompileFunction(thread, func);
|
||||
if (result.IsError()) {
|
||||
return Error::Cast(result).raw();
|
||||
|
|
|
@ -371,6 +371,7 @@ class CallingConventions {
|
|||
static constexpr Register kFirstNonArgumentRegister = R8;
|
||||
static constexpr Register kSecondNonArgumentRegister = R9;
|
||||
static constexpr Register kFirstCalleeSavedCpuReg = NOTFP;
|
||||
static constexpr Register kStackPointerRegister = SPREG;
|
||||
};
|
||||
|
||||
#undef R
|
||||
|
|
|
@ -213,6 +213,7 @@ class CallingConventions {
|
|||
static constexpr Register kFirstCalleeSavedCpuReg = kAbiFirstPreservedCpuReg;
|
||||
static constexpr Register kFirstNonArgumentRegister = R8;
|
||||
static constexpr Register kSecondNonArgumentRegister = R9;
|
||||
static constexpr Register kStackPointerRegister = SPREG;
|
||||
};
|
||||
|
||||
#undef R
|
||||
|
|
|
@ -170,6 +170,11 @@ namespace dart {
|
|||
// Invoke native function at pool[ArgB] with argc_tag at pool[ArgC] using
|
||||
// wrapper at pool[ArgA].
|
||||
//
|
||||
// - FfiCall ArgD
|
||||
//
|
||||
// Invoke foreign function with unboxed arguments using the signature
|
||||
// descriptor PP[D].
|
||||
//
|
||||
// - PushPolymorphicInstanceCall ArgC, D
|
||||
//
|
||||
// Skips 2*D + 1 instructions and pushes a function object onto the stack
|
||||
|
@ -801,6 +806,7 @@ namespace dart {
|
|||
V(PushPolymorphicInstanceCall, A_D, num, num, ___) \
|
||||
V(PushPolymorphicInstanceCallByRange, A_D, num, num, ___) \
|
||||
V(NativeCall, A_B_C, num, num, num) \
|
||||
V(FfiCall, D, lit, ___, ___) \
|
||||
V(OneByteStringFromCharCode, A_X, reg, xeg, ___) \
|
||||
V(StringToCharCode, A_X, reg, xeg, ___) \
|
||||
V(AddTOS, 0, ___, ___, ___) \
|
||||
|
|
|
@ -151,6 +151,7 @@ class CallingConventions {
|
|||
static constexpr Register kFirstCalleeSavedCpuReg = EBX;
|
||||
static constexpr Register kFirstNonArgumentRegister = EAX;
|
||||
static constexpr Register kSecondNonArgumentRegister = ECX;
|
||||
static constexpr Register kStackPointerRegister = SPREG;
|
||||
|
||||
// Whether 64-bit arguments must be aligned to an even register or 8-byte
|
||||
// stack address. On IA32, 64-bit integers and floating-point values do *not*
|
||||
|
|
|
@ -264,6 +264,7 @@ class CallingConventions {
|
|||
static constexpr Register kFirstCalleeSavedCpuReg = RBX;
|
||||
static constexpr Register kFirstNonArgumentRegister = RAX;
|
||||
static constexpr Register kSecondNonArgumentRegister = RBX;
|
||||
static constexpr Register kStackPointerRegister = SPREG;
|
||||
|
||||
COMPILE_ASSERT(((R(kFirstCalleeSavedCpuReg)) & kCalleeSaveCpuRegisters) != 0);
|
||||
|
||||
|
|
|
@ -297,8 +297,15 @@ class Api : AllStatic {
|
|||
|
||||
static bool IsFfiEnabled() {
|
||||
// dart:ffi is not implemented for the following configurations
|
||||
#if defined(TARGET_ARCH_DBC)
|
||||
// https://github.com/dart-lang/sdk/issues/35773 DBC
|
||||
#if defined(TARGET_ARCH_DBC) && !defined(ARCH_IS_64_BIT)
|
||||
// TODO(36809): Support SimDBC32.
|
||||
return false;
|
||||
#elif defined(TARGET_ARCH_DBC) && !defined(HOST_ARCH_X64)
|
||||
// TODO(35773): Support ia32, arm64, and arm.
|
||||
return false;
|
||||
#elif defined(TARGET_ARCH_DBC) && defined(HOST_ARCH_X64) && \
|
||||
defined(HOST_OS_WINDOWS)
|
||||
// TODO(35773): Support x64 Windows.
|
||||
return false;
|
||||
#elif defined(TARGET_ARCH_ARM) && \
|
||||
!(defined(TARGET_OS_ANDROID) || defined(TARGET_OS_MACOS_IOS))
|
||||
|
|
|
@ -14,14 +14,20 @@
|
|||
|
||||
#include "vm/simulator.h"
|
||||
|
||||
#include "lib/ffi.h"
|
||||
#include "platform/assert.h"
|
||||
#include "vm/compiler/assembler/assembler.h"
|
||||
#include "vm/compiler/assembler/disassembler.h"
|
||||
#include "vm/compiler/backend/il.h"
|
||||
#include "vm/compiler/backend/locations.h"
|
||||
#include "vm/compiler/ffi.h"
|
||||
#include "vm/compiler/ffi_dbc_trampoline.h"
|
||||
#include "vm/compiler/jit/compiler.h"
|
||||
#include "vm/constants_dbc.h"
|
||||
#include "vm/cpu.h"
|
||||
#include "vm/dart_entry.h"
|
||||
#include "vm/debugger.h"
|
||||
#include "vm/heap/safepoint.h"
|
||||
#include "vm/lockers.h"
|
||||
#include "vm/native_arguments.h"
|
||||
#include "vm/native_entry.h"
|
||||
|
@ -616,6 +622,7 @@ typedef intptr_t (*SimulatorLeafRuntimeCall)(intptr_t r0,
|
|||
// Calls to leaf float Dart runtime functions are based on this interface.
|
||||
typedef double (*SimulatorLeafFloatRuntimeCall)(double d0, double d1);
|
||||
|
||||
// Set up an exit frame for the garbage collector.
|
||||
void Simulator::Exit(Thread* thread,
|
||||
RawObject** base,
|
||||
RawObject** frame,
|
||||
|
@ -1860,6 +1867,39 @@ SwitchDispatch:
|
|||
DISPATCH();
|
||||
}
|
||||
|
||||
{
|
||||
BYTECODE(FfiCall, __D);
|
||||
{
|
||||
const auto& sig_desc = compiler::ffi::FfiSignatureDescriptor(
|
||||
TypedData::Cast(Object::Handle(LOAD_CONSTANT(rD))));
|
||||
|
||||
// The arguments to this ffi call are on the stack at FP.
|
||||
// kFirstLocalSlotFromFp is 0 in DBC, but the const is -1.
|
||||
uint64_t* arg_values = reinterpret_cast<uint64_t*>(FP);
|
||||
|
||||
uint64_t* marshalled_args_data =
|
||||
FfiMarshalledArguments::New(sig_desc, arg_values);
|
||||
const auto& marshalled_args =
|
||||
FfiMarshalledArguments(marshalled_args_data);
|
||||
|
||||
Exit(thread, FP, SP, pc);
|
||||
{
|
||||
TransitionGeneratedToNative transition(thread);
|
||||
FfiTrampolineCall(marshalled_args_data);
|
||||
}
|
||||
|
||||
const Representation result_rep = sig_desc.ResultRepresentation();
|
||||
intptr_t result;
|
||||
if (result_rep == kUnboxedDouble || result_rep == kUnboxedFloat) {
|
||||
result = marshalled_args.DoubleResult();
|
||||
} else {
|
||||
result = marshalled_args.IntResult();
|
||||
}
|
||||
FP[0] = reinterpret_cast<RawObject*>(result);
|
||||
}
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
{
|
||||
BYTECODE(OneByteStringFromCharCode, A_X);
|
||||
const intptr_t char_code = Smi::Value(RAW_CAST(Smi, FP[rD]));
|
||||
|
|
|
@ -108,6 +108,7 @@ class Simulator {
|
|||
|
||||
static IntrinsicHandler intrinsics_[kIntrinsicCount];
|
||||
|
||||
// Set up an exit frame for the garbage collector.
|
||||
void Exit(Thread* thread,
|
||||
RawObject** base,
|
||||
RawObject** exit_frame,
|
||||
|
|
|
@ -450,12 +450,8 @@ class Thread : public ThreadState {
|
|||
Heap* heap() const { return heap_; }
|
||||
static intptr_t heap_offset() { return OFFSET_OF(Thread, heap_); }
|
||||
|
||||
void set_top(uword value) {
|
||||
top_ = value;
|
||||
}
|
||||
void set_end(uword value) {
|
||||
end_ = value;
|
||||
}
|
||||
void set_top(uword value) { top_ = value; }
|
||||
void set_end(uword value) { end_ = value; }
|
||||
|
||||
uword top() { return top_; }
|
||||
uword end() { return end_; }
|
||||
|
@ -786,6 +782,17 @@ class Thread : public ThreadState {
|
|||
|
||||
uint64_t GetRandomUInt64() { return thread_random_.NextUInt64(); }
|
||||
|
||||
uint64_t* GetFfiMarshalledArguments(intptr_t size) {
|
||||
if (ffi_marshalled_arguments_size_ < size) {
|
||||
if (ffi_marshalled_arguments_size_ > 0) {
|
||||
free(ffi_marshalled_arguments_);
|
||||
}
|
||||
ffi_marshalled_arguments_ =
|
||||
reinterpret_cast<uint64_t*>(malloc(size * sizeof(uint64_t)));
|
||||
}
|
||||
return ffi_marshalled_arguments_;
|
||||
}
|
||||
|
||||
#ifndef PRODUCT
|
||||
void PrintJSON(JSONStream* stream) const;
|
||||
#endif
|
||||
|
@ -887,6 +894,9 @@ class Thread : public ThreadState {
|
|||
|
||||
Random thread_random_;
|
||||
|
||||
intptr_t ffi_marshalled_arguments_size_ = 0;
|
||||
uint64_t* ffi_marshalled_arguments_;
|
||||
|
||||
// Reusable handles support.
|
||||
#define REUSABLE_HANDLE_FIELDS(object) object* object##_handle_;
|
||||
REUSABLE_HANDLE_LIST(REUSABLE_HANDLE_FIELDS)
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
data_not_asan_test: SkipByDesign # This test tries to allocate too much memory on purpose.
|
||||
|
||||
# dartbug.com/35768: Structs not supported on 32-bit.
|
||||
[ $arch == ia32 || $arch == arm ]
|
||||
[ $arch == ia32 || $arch == arm || $arch == simdbc ]
|
||||
function_structs_test: Skip
|
||||
function_callbacks_test: Skip
|
||||
structs_test: Skip
|
||||
|
@ -22,14 +22,20 @@ function_structs_test: Skip
|
|||
function_test: Skip
|
||||
negative_function_test: Skip
|
||||
|
||||
[ $arch == x64 || $arch == arm64 ]
|
||||
[ $arch == x64 || $arch == arm64 || $arch == simdbc64 ]
|
||||
enable_structs_test: SkipByDesign # Tests that structs don't work on 32-bit systems.
|
||||
|
||||
[ $runtime == dart_precompiled ]
|
||||
*: Skip # AOT is not yet supported: dartbug.com/35765
|
||||
|
||||
[ $arch == simdbc64 || $arch == simarm || $arch == simarm64 ]
|
||||
*: Skip # FFI not yet supported on DBC or other simulated architectures.
|
||||
[ $arch == simarm || $arch == simarm64 ]
|
||||
*: Skip # FFI not yet supported on the arm simulator.
|
||||
|
||||
[ $arch == simdbc ]
|
||||
*: Skip # FFI not yet supported on SimDBC32: dartbug.com/36809
|
||||
|
||||
[ $arch == simdbc64 && $system != linux && $system != macos ]
|
||||
*: Skip # FFI not yet supported outside x64 Linux: dartbug.com/36809
|
||||
|
||||
[ $system != android && $system != linux && $system != macos && $system != windows ]
|
||||
*: Skip # FFI not yet supported on other OSes.
|
||||
|
|
Loading…
Reference in a new issue