[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:
Daco Harkes 2019-05-09 11:46:03 +00:00 committed by commit-bot@chromium.org
parent a0290f823c
commit a8b93d9e78
24 changed files with 687 additions and 70 deletions

View file

@ -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
View 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_

View file

@ -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,

View file

@ -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);
};

View file

@ -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()) {

View file

@ -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) {}

View file

@ -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",

View file

@ -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)

View file

@ -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

View 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_

View 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 */

View file

@ -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) {

View file

@ -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);

View file

@ -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();

View file

@ -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

View file

@ -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

View file

@ -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, ___, ___, ___) \

View file

@ -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*

View file

@ -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);

View file

@ -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))

View file

@ -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]));

View file

@ -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,

View file

@ -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)

View file

@ -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.