From 8408232522765e29fb6c42e53b6d122456c5c35e Mon Sep 17 00:00:00 2001 From: Daco Harkes Date: Wed, 5 Feb 2020 08:46:07 +0000 Subject: [PATCH] [vm/ffi] Replace FFI compiler pipeline Introduces NativeRepresentation and NativeLocation for the compilation of FFI. NativeRepresentations are able to express all representations (or types) of the native ABIs we bind to with FFI, this is more representations that than that are used in Dart itself. NativeLocations are able to express all locations of the native ABIs we bind to with FFI, this is more types of locations than that are used for the Dart calling convention. See the documentation in the respective files. These NativeLocations and NativeRepresentations are computed by the NativeCallingConvention and consumed by the Marshaller and Assemblers. This reenginering is required for go/dart-ffi-by-value, hardfp (Arm S and D fpu registers), and iOS 64 bit (non-word-aligned stack arguments). In addition, by using the NativeRepresentations we also get slightly reduced code size: * The tracking of sizes is improved, so less sign/zero-extension operations are required. * UnboxedWidthExtenderInstr is fully removed, the size extension is done inside the native moves, coalescing moves and size extension when possible. * BitCastInstr is only used when really needed. This reduces code-size on arm32 softfp. This fixes the iOS arm64 calling convention, manually tested with https://github.com/flutter/flutter/pull/46078 and https://dart-review.googlesource.com/c/sdk/+/131074. Fixes: https://github.com/dart-lang/sdk/issues/39637 Issue: https://github.com/dart-lang/sdk/issues/36309 Issue: https://github.com/dart-lang/sdk/issues/36730 Change-Id: I8878bc0f314277bab4ca22f417c6295ecc017720 Cq-Include-Trybots: luci.dart.try:vm-ffi-android-debug-arm-try,vm-ffi-android-debug-arm64-try,app-kernel-linux-debug-x64-try,vm-kernel-linux-debug-ia32-try,vm-kernel-win-debug-x64-try,vm-kernel-win-debug-ia32-try,vm-kernel-precomp-linux-debug-x64-try,vm-dartkb-linux-release-x64-abi-try,vm-kernel-precomp-android-release-arm64-try,vm-kernel-asan-linux-release-x64-try,vm-kernel-linux-release-simarm-try,vm-kernel-linux-release-simarm64-try,vm-kernel-precomp-android-release-arm_x64-try,vm-kernel-precomp-obfuscate-linux-release-x64-try,dart-sdk-linux-try,analyzer-analysis-server-linux-try,analyzer-linux-release-try,front-end-linux-release-x64-try,vm-kernel-precomp-win-release-x64-try Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/129081 Commit-Queue: Daco Harkes Reviewed-by: Martin Kustermann Reviewed-by: Alexander Markov --- runtime/lib/ffi.cc | 33 +- .../vm/compiler/assembler/assembler_arm64.cc | 20 + .../vm/compiler/assembler/assembler_arm64.h | 12 +- .../vm/compiler/assembler/assembler_ia32.cc | 6 + .../vm/compiler/assembler/assembler_x64.cc | 3 + .../compiler/backend/constant_propagator.cc | 5 - .../compiler/backend/flow_graph_compiler.cc | 233 ++++++++++- .../vm/compiler/backend/flow_graph_compiler.h | 43 +++ .../backend/flow_graph_compiler_arm.cc | 157 ++++++++ .../backend/flow_graph_compiler_arm64.cc | 149 +++++++ .../backend/flow_graph_compiler_ia32.cc | 157 ++++++++ .../backend/flow_graph_compiler_x64.cc | 175 +++++++++ runtime/vm/compiler/backend/il.cc | 106 ++--- runtime/vm/compiler/backend/il.h | 141 ++----- runtime/vm/compiler/backend/il_arm.cc | 73 ++-- runtime/vm/compiler/backend/il_arm64.cc | 74 +--- runtime/vm/compiler/backend/il_ia32.cc | 82 +--- runtime/vm/compiler/backend/il_printer.cc | 39 +- runtime/vm/compiler/backend/il_x64.cc | 74 +--- runtime/vm/compiler/backend/locations.h | 10 - runtime/vm/compiler/compiler_sources.gni | 6 +- runtime/vm/compiler/ffi/README.md | 15 + runtime/vm/compiler/ffi/abi.cc | 5 + runtime/vm/compiler/ffi/frame_rebase.cc | 13 +- runtime/vm/compiler/ffi/frame_rebase.h | 19 +- runtime/vm/compiler/ffi/marshaller.cc | 168 +++++--- runtime/vm/compiler/ffi/marshaller.h | 109 +++++- .../compiler/ffi/native_calling_convention.cc | 365 ++++++++---------- .../compiler/ffi/native_calling_convention.h | 81 ++-- runtime/vm/compiler/ffi/native_location.cc | 246 ++++++++++++ runtime/vm/compiler/ffi/native_location.h | 280 ++++++++++++++ .../vm/compiler/ffi/native_representation.cc | 55 --- .../vm/compiler/ffi/native_representation.h | 30 -- runtime/vm/compiler/ffi/native_type.cc | 329 ++++++++++++++++ runtime/vm/compiler/ffi/native_type.h | 158 ++++++++ .../frontend/bytecode_flow_graph_builder.cc | 2 +- .../frontend/kernel_binary_flowgraph.cc | 3 +- runtime/vm/compiler/frontend/kernel_to_il.cc | 153 +++----- runtime/vm/compiler/frontend/kernel_to_il.h | 23 +- runtime/vm/constants.h | 27 ++ runtime/vm/constants_arm.cc | 3 +- runtime/vm/constants_arm.h | 26 +- runtime/vm/constants_arm64.cc | 3 +- runtime/vm/constants_arm64.h | 28 +- runtime/vm/constants_ia32.cc | 3 +- runtime/vm/constants_ia32.h | 34 +- runtime/vm/constants_x64.cc | 3 +- runtime/vm/constants_x64.h | 38 +- 48 files changed, 2840 insertions(+), 977 deletions(-) create mode 100644 runtime/vm/compiler/ffi/README.md create mode 100644 runtime/vm/compiler/ffi/native_location.cc create mode 100644 runtime/vm/compiler/ffi/native_location.h delete mode 100644 runtime/vm/compiler/ffi/native_representation.cc delete mode 100644 runtime/vm/compiler/ffi/native_representation.h create mode 100644 runtime/vm/compiler/ffi/native_type.cc create mode 100644 runtime/vm/compiler/ffi/native_type.h diff --git a/runtime/lib/ffi.cc b/runtime/lib/ffi.cc index df3fb7fb683e..0a40666f0a18 100644 --- a/runtime/lib/ffi.cc +++ b/runtime/lib/ffi.cc @@ -10,7 +10,7 @@ #include "vm/compiler/assembler/assembler.h" #include "vm/compiler/ffi/call.h" #include "vm/compiler/ffi/callback.h" -#include "vm/compiler/ffi/native_representation.h" +#include "vm/compiler/ffi/native_type.h" #include "vm/compiler/jit/compiler.h" #include "vm/exceptions.h" #include "vm/flags.h" @@ -72,9 +72,10 @@ static const Double& AsDouble(const Instance& instance) { // // You must check [IsConcreteNativeType] and [CheckSized] first to verify that // this type has a defined size. -static size_t SizeOf(const AbstractType& type) { +static size_t SizeOf(const AbstractType& type, Zone* zone) { if (RawObject::IsFfiTypeClassId(type.type_class_id())) { - return compiler::ffi::ElementSizeInBytes(type.type_class_id()); + return compiler::ffi::NativeType::FromAbstractType(type, zone) + .SizeInBytes(); } else { Class& struct_class = Class::Handle(type.type_class()); Object& result = Object::Handle( @@ -105,8 +106,10 @@ static RawObject* LoadValueNumeric(Zone* zone, const Integer& index) { // TODO(36370): Make representation consistent with kUnboxedFfiIntPtr. const size_t address = - target.NativeAddress() + static_cast(index.AsInt64Value()) * - compiler::ffi::ElementSizeInBytes(type_cid); + target.NativeAddress() + + static_cast(index.AsInt64Value()) * + compiler::ffi::NativeType::FromTypedDataClassId(type_cid, zone) + .SizeInBytes(); switch (type_cid) { case kFfiInt8Cid: return Integer::New(*reinterpret_cast(address)); @@ -156,8 +159,8 @@ DEFINE_NATIVE_ENTRY(Ffi_loadPointer, 1, 2) { // TODO(36370): Make representation consistent with kUnboxedFfiIntPtr. const size_t address = - pointer.NativeAddress() + - static_cast(index.AsInt64Value()) * SizeOf(pointer_type_arg); + pointer.NativeAddress() + static_cast(index.AsInt64Value()) * + SizeOf(pointer_type_arg, zone); return Pointer::New(type_arg, *reinterpret_cast(address)); } @@ -195,8 +198,8 @@ DEFINE_NATIVE_ENTRY(Ffi_loadStruct, 0, 2) { // TODO(36370): Make representation consistent with kUnboxedFfiIntPtr. const size_t address = - pointer.NativeAddress() + - static_cast(index.AsInt64Value()) * SizeOf(pointer_type_arg); + pointer.NativeAddress() + static_cast(index.AsInt64Value()) * + SizeOf(pointer_type_arg, zone); const Pointer& pointer_offset = Pointer::Handle(zone, Pointer::New(pointer_type_arg, address)); @@ -210,8 +213,10 @@ static void StoreValueNumeric(Zone* zone, const Instance& new_value) { // TODO(36370): Make representation consistent with kUnboxedFfiIntPtr. const size_t address = - pointer.NativeAddress() + static_cast(index.AsInt64Value()) * - compiler::ffi::ElementSizeInBytes(type_cid); + pointer.NativeAddress() + + static_cast(index.AsInt64Value()) * + compiler::ffi::NativeType::FromTypedDataClassId(type_cid, zone) + .SizeInBytes(); switch (type_cid) { case kFfiInt8Cid: *reinterpret_cast(address) = AsInteger(new_value).AsInt64Value(); @@ -291,8 +296,8 @@ DEFINE_NATIVE_ENTRY(Ffi_storePointer, 0, 3) { ASSERT(IsPointerType(pointer_type_arg)); // TODO(36370): Make representation consistent with kUnboxedFfiIntPtr. const size_t address = - pointer.NativeAddress() + - static_cast(index.AsInt64Value()) * SizeOf(pointer_type_arg); + pointer.NativeAddress() + static_cast(index.AsInt64Value()) * + SizeOf(pointer_type_arg, zone); *reinterpret_cast(address) = new_value.NativeAddress(); return Object::null(); } @@ -301,7 +306,7 @@ DEFINE_NATIVE_ENTRY(Ffi_sizeOf, 1, 0) { GET_NATIVE_TYPE_ARGUMENT(type_arg, arguments->NativeTypeArgAt(0)); CheckSized(type_arg); - return Integer::New(SizeOf(type_arg)); + return Integer::New(SizeOf(type_arg, zone)); } // Static invocations to this method are translated directly in streaming FGB diff --git a/runtime/vm/compiler/assembler/assembler_arm64.cc b/runtime/vm/compiler/assembler/assembler_arm64.cc index ea356c047d4a..600bf631ca67 100644 --- a/runtime/vm/compiler/assembler/assembler_arm64.cc +++ b/runtime/vm/compiler/assembler/assembler_arm64.cc @@ -821,6 +821,16 @@ void Assembler::LoadFromOffset(Register dest, } } +void Assembler::LoadSFromOffset(VRegister dest, Register base, int32_t offset) { + if (Address::CanHoldOffset(offset, Address::Offset, kSWord)) { + fldrs(dest, Address(base, offset, Address::Offset, kSWord)); + } else { + ASSERT(base != TMP2); + AddImmediate(TMP2, base, offset); + fldrs(dest, Address(TMP2)); + } +} + void Assembler::LoadDFromOffset(VRegister dest, Register base, int32_t offset) { if (Address::CanHoldOffset(offset, Address::Offset, kDWord)) { fldrd(dest, Address(base, offset, Address::Offset, kDWord)); @@ -855,6 +865,16 @@ void Assembler::StoreToOffset(Register src, } } +void Assembler::StoreSToOffset(VRegister src, Register base, int32_t offset) { + if (Address::CanHoldOffset(offset, Address::Offset, kSWord)) { + fstrs(src, Address(base, offset, Address::Offset, kSWord)); + } else { + ASSERT(base != TMP2); + AddImmediate(TMP2, base, offset); + fstrs(src, Address(TMP2)); + } +} + void Assembler::StoreDToOffset(VRegister src, Register base, int32_t offset) { if (Address::CanHoldOffset(offset, Address::Offset, kDWord)) { fstrd(src, Address(base, offset, Address::Offset, kDWord)); diff --git a/runtime/vm/compiler/assembler/assembler_arm64.h b/runtime/vm/compiler/assembler/assembler_arm64.h index 9058c8e913c0..19a26c687dba 100644 --- a/runtime/vm/compiler/assembler/assembler_arm64.h +++ b/runtime/vm/compiler/assembler/assembler_arm64.h @@ -1270,6 +1270,13 @@ class Assembler : public AssemblerBase { orr(rd, ZR, Operand(rn)); } } + void movw(Register rd, Register rn) { + if ((rd == CSP) || (rn == CSP)) { + addw(rd, rn, Operand(0)); + } else { + orrw(rd, ZR, Operand(rn)); + } + } void vmov(VRegister vd, VRegister vn) { vorr(vd, vn, vn); } void mvn(Register rd, Register rm) { orn(rd, ZR, Operand(rm)); } void mvnw(Register rd, Register rm) { ornw(rd, ZR, Operand(rm)); } @@ -1406,7 +1413,7 @@ class Assembler : public AssemblerBase { // Macros accepting a pp Register argument may attempt to load values from // the object pool when possible. Unless you are sure that the untagged object // pool pointer is in another register, or that it is not available at all, - // PP should be passed for pp. + // PP should be passed for pp. `dest` can be TMP2, `rn` cannot. void AddImmediate(Register dest, Register rn, int64_t imm); void AddImmediateSetFlags(Register dest, Register rn, @@ -1432,6 +1439,7 @@ class Assembler : public AssemblerBase { OperandSize sz = kDoubleWord) { LoadFromOffset(dest, base, offset - kHeapObjectTag, sz); } + void LoadSFromOffset(VRegister dest, Register base, int32_t offset); void LoadDFromOffset(VRegister dest, Register base, int32_t offset); void LoadDFieldFromOffset(VRegister dest, Register base, int32_t offset) { LoadDFromOffset(dest, base, offset - kHeapObjectTag); @@ -1451,6 +1459,8 @@ class Assembler : public AssemblerBase { OperandSize sz = kDoubleWord) { StoreToOffset(src, base, offset - kHeapObjectTag, sz); } + + void StoreSToOffset(VRegister src, Register base, int32_t offset); void StoreDToOffset(VRegister src, Register base, int32_t offset); void StoreDFieldToOffset(VRegister src, Register base, int32_t offset) { StoreDToOffset(src, base, offset - kHeapObjectTag); diff --git a/runtime/vm/compiler/assembler/assembler_ia32.cc b/runtime/vm/compiler/assembler/assembler_ia32.cc index 6c44032d9703..d9e2ae6a7382 100644 --- a/runtime/vm/compiler/assembler/assembler_ia32.cc +++ b/runtime/vm/compiler/assembler/assembler_ia32.cc @@ -184,6 +184,9 @@ void Assembler::movsxb(Register dst, const Address& src) { } void Assembler::movb(Register dst, const Address& src) { + // This would leave 24 bits above the 1 byte value undefined. + // If we ever want to purposefully have those undefined, remove this. + // TODO(dartbug.com/40210): Allow this. FATAL("Use movzxb or movsxb instead."); } @@ -230,6 +233,9 @@ void Assembler::movsxw(Register dst, const Address& src) { } void Assembler::movw(Register dst, const Address& src) { + // This would leave 16 bits above the 2 byte value undefined. + // If we ever want to purposefully have those undefined, remove this. + // TODO(dartbug.com/40210): Allow this. FATAL("Use movzxw or movsxw instead."); } diff --git a/runtime/vm/compiler/assembler/assembler_x64.cc b/runtime/vm/compiler/assembler/assembler_x64.cc index 805ebcc4737a..0b02b8e52988 100644 --- a/runtime/vm/compiler/assembler/assembler_x64.cc +++ b/runtime/vm/compiler/assembler/assembler_x64.cc @@ -359,6 +359,9 @@ void Assembler::movb(const Address& dst, const Immediate& imm) { } void Assembler::movw(Register dst, const Address& src) { + // This would leave 16 bits above the 2 byte value undefined. + // If we ever want to purposefully have those undefined, remove this. + // TODO(40210): Allow this. FATAL("Use movzxw or movsxw instead."); } diff --git a/runtime/vm/compiler/backend/constant_propagator.cc b/runtime/vm/compiler/backend/constant_propagator.cc index f0131d4f143e..fb02b0b26714 100644 --- a/runtime/vm/compiler/backend/constant_propagator.cc +++ b/runtime/vm/compiler/backend/constant_propagator.cc @@ -1319,11 +1319,6 @@ void ConstantPropagator::VisitIntConverter(IntConverterInstr* instr) { SetValue(instr, non_constant_); } -void ConstantPropagator::VisitUnboxedWidthExtender( - UnboxedWidthExtenderInstr* instr) { - SetValue(instr, non_constant_); -} - void ConstantPropagator::VisitBitCast(BitCastInstr* instr) { SetValue(instr, non_constant_); } diff --git a/runtime/vm/compiler/backend/flow_graph_compiler.cc b/runtime/vm/compiler/backend/flow_graph_compiler.cc index eec380e544f6..23b9dd4570e1 100644 --- a/runtime/vm/compiler/backend/flow_graph_compiler.cc +++ b/runtime/vm/compiler/backend/flow_graph_compiler.cc @@ -350,7 +350,7 @@ static intptr_t LocationToStackIndex(const Location& src) { } static CatchEntryMove CatchEntryMoveFor(compiler::Assembler* assembler, - Representation src_rep, + Representation src_type, const Location& src, intptr_t dst_index) { if (src.IsConstant()) { @@ -376,7 +376,7 @@ static CatchEntryMove CatchEntryMoveFor(compiler::Assembler* assembler, } CatchEntryMove::SourceKind src_kind; - switch (src_rep) { + switch (src_type) { case kTagged: src_kind = CatchEntryMove::SourceKind::kTaggedSlot; break; @@ -445,11 +445,11 @@ void FlowGraphCompiler::RecordCatchEntryMoves(Environment* env, // Can only occur if AllocationSinking is enabled - and it is disabled // in functions with try. ASSERT(!src.IsInvalid()); - const Representation src_rep = + const Representation src_type = env->ValueAt(i)->definition()->representation(); intptr_t dest_index = i - num_direct_parameters; const auto move = - CatchEntryMoveFor(assembler(), src_rep, src, dest_index); + CatchEntryMoveFor(assembler(), src_type, src, dest_index); if (!move.IsRedundant()) { catch_entry_moves_maps_builder_->Append(move); } @@ -2484,6 +2484,231 @@ void ThrowErrorSlowPathCode::EmitNativeCode(FlowGraphCompiler* compiler) { } } +void FlowGraphCompiler::EmitNativeMove( + const compiler::ffi::NativeLocation& destination, + const compiler::ffi::NativeLocation& source, + TemporaryRegisterAllocator* temp) { + const auto& src_payload_type = source.payload_type(); + const auto& dst_payload_type = destination.payload_type(); + const auto& src_container_type = source.container_type(); + const auto& dst_container_type = destination.container_type(); + const intptr_t src_payload_size = src_payload_type.SizeInBytes(); + const intptr_t dst_payload_size = dst_payload_type.SizeInBytes(); + const intptr_t src_container_size = src_container_type.SizeInBytes(); + const intptr_t dst_container_size = dst_container_type.SizeInBytes(); + + // This function does not know how to do larger mem copy moves yet. + ASSERT(src_payload_type.IsFundamental()); + ASSERT(dst_payload_type.IsFundamental()); + + // This function does not deal with sign conversions yet. + ASSERT(src_payload_type.IsSigned() == dst_payload_type.IsSigned()); + + // This function does not deal with bit casts yet. + ASSERT(src_container_type.IsFloat() == dst_container_type.IsFloat()); + ASSERT(src_container_type.IsInt() == dst_container_type.IsInt()); + + // If the location, payload, and container are equal, we're done. + if (source.Equals(destination) && src_payload_type.Equals(dst_payload_type) && + src_container_type.Equals(dst_container_type)) { + return; + } + + // Solve descrepancies between container size and payload size. + if (src_payload_type.IsInt() && dst_payload_type.IsInt() && + (src_payload_size != src_container_size || + dst_payload_size != dst_container_size)) { + if (src_payload_size <= dst_payload_size && + src_container_size >= dst_container_size) { + // The upper bits of the source are already properly sign or zero + // extended, so just copy the required amount of bits. + return EmitNativeMove( + destination.WithOtherRep(dst_container_type, dst_container_type, + zone_), + source.WithOtherRep(dst_container_type, dst_container_type, zone_), + temp); + } + if (src_payload_size >= dst_payload_size && + dst_container_size > dst_payload_size) { + // The upper bits of the source are not properly sign or zero extended + // to be copied to the target, so regard the source as smaller. + return EmitNativeMove( + destination.WithOtherRep(dst_container_type, dst_container_type, + zone_), + source.WithOtherRep(dst_payload_type, dst_payload_type, zone_), temp); + } + UNREACHABLE(); + } + ASSERT(src_payload_size == src_container_size); + ASSERT(dst_payload_size == dst_container_size); + + // Split moves that are larger than kWordSize, these require separate + // instructions on all architectures. + if (compiler::target::kWordSize == 4 && src_container_size == 8 && + dst_container_size == 8 && !source.IsFpuRegisters() && + !destination.IsFpuRegisters()) { + // TODO(40209): If this is stack to stack, we could use FpuTMP. + // Test the impact on code size and speed. + EmitNativeMove(destination.Split(0, zone_), source.Split(0, zone_), temp); + EmitNativeMove(destination.Split(1, zone_), source.Split(1, zone_), temp); + return; + } + + // Split moves from stack to stack, none of the architectures provides + // memory to memory move instructions. + if (source.IsStack() && destination.IsStack()) { + Register scratch = TMP; + if (TMP == kNoRegister) { + scratch = temp->AllocateTemporary(); + } + const auto& intermediate = + *new (zone_) compiler::ffi::NativeRegistersLocation( + dst_payload_type, dst_container_type, scratch); + EmitNativeMove(intermediate, source, temp); + EmitNativeMove(destination, intermediate, temp); + if (TMP == kNoRegister) { + temp->ReleaseTemporary(); + } + return; + } + + const bool sign_or_zero_extend = dst_container_size > src_container_size; + + // No architecture supports sign extending with memory as destination. + if (sign_or_zero_extend && destination.IsStack()) { + ASSERT(source.IsRegisters()); + const auto& intermediate = + source.WithOtherRep(dst_payload_type, dst_container_type, zone_); + EmitNativeMove(intermediate, source, temp); + EmitNativeMove(destination, intermediate, temp); + return; + } + +#if defined(TARGET_ARCH_ARM) || defined(TARGET_ARCH_ARM64) + // Arm does not support sign extending from a memory location, x86 does. + if (sign_or_zero_extend && source.IsStack()) { + ASSERT(destination.IsRegisters()); + const auto& intermediate = + destination.WithOtherRep(src_payload_type, src_container_type, zone_); + EmitNativeMove(intermediate, source, temp); + EmitNativeMove(destination, intermediate, temp); + return; + } +#endif + + // If we're not sign extending, and we're moving 8 or 16 bits into a + // register, upgrade the move to take upper bits of garbage from the + // source location. This is the same as leaving the previous garbage in + // there. + // + // TODO(40210): If our assemblers would support moving 1 and 2 bytes into + // registers, this code can be removed. + if (!sign_or_zero_extend && destination.IsRegisters() && + destination.container_type().SizeInBytes() <= 2) { + ASSERT(source.payload_type().IsInt()); + return EmitNativeMove(destination.WidenTo4Bytes(zone_), + source.WidenTo4Bytes(zone_), temp); + } + + // Do the simple architecture specific moves. + EmitNativeMoveArchitecture(destination, source); +} + +// TODO(dartbug.com/36730): Remove this if PairLocations can be converted +// into NativeLocations. +void FlowGraphCompiler::EmitMoveToNative( + const compiler::ffi::NativeLocation& dst, + Location src_loc, + Representation src_type, + TemporaryRegisterAllocator* temp) { + if (src_loc.IsPairLocation()) { + for (intptr_t i : {0, 1}) { + const auto& src_split = compiler::ffi::NativeLocation::FromPairLocation( + src_loc, src_type, i, zone_); + EmitNativeMove(dst.Split(i, zone_), src_split, temp); + } + } else { + const auto& src = + compiler::ffi::NativeLocation::FromLocation(src_loc, src_type, zone_); + EmitNativeMove(dst, src, temp); + } +} + +// TODO(dartbug.com/36730): Remove this if PairLocations can be converted +// into NativeLocations. +void FlowGraphCompiler::EmitMoveFromNative( + Location dst_loc, + Representation dst_type, + const compiler::ffi::NativeLocation& src, + TemporaryRegisterAllocator* temp) { + if (dst_loc.IsPairLocation()) { + for (intptr_t i : {0, 1}) { + const auto& dest_split = compiler::ffi::NativeLocation::FromPairLocation( + dst_loc, dst_type, i, zone_); + EmitNativeMove(dest_split, src.Split(i, zone_), temp); + } + } else { + const auto& dest = + compiler::ffi::NativeLocation::FromLocation(dst_loc, dst_type, zone_); + EmitNativeMove(dest, src, temp); + } +} + +void FlowGraphCompiler::EmitMoveConst(const compiler::ffi::NativeLocation& dst, + Location src, + Representation src_type, + TemporaryRegisterAllocator* temp) { + ASSERT(src.IsConstant()); + const auto& dst_type = dst.payload_type(); + if (dst.IsExpressibleAsLocation() && + dst_type.IsExpressibleAsRepresentation() && + dst_type.AsRepresentationOverApprox(zone_) == src_type) { + // We can directly emit the const in the right place and representation. + const Location dst_loc = dst.AsLocation(); + EmitMove(dst_loc, src, temp); + } else { + // We need an intermediate location. + Location intermediate; + if (dst_type.IsInt()) { + if (TMP == kNoRegister) { + Register scratch = temp->AllocateTemporary(); + Location::RegisterLocation(scratch); + } else { + intermediate = Location::RegisterLocation(TMP); + } + } else { + ASSERT(dst_type.IsFloat()); + intermediate = Location::FpuRegisterLocation(FpuTMP); + } + + if (src.IsPairLocation()) { + for (intptr_t i : {0, 1}) { + const Representation src_type_split = + compiler::ffi::NativeType::FromUnboxedRepresentation(src_type, + zone_) + .Split(i, zone_) + .AsRepresentation(); + const auto& intermediate_native = + compiler::ffi::NativeLocation::FromLocation(intermediate, + src_type_split, zone_); + EmitMove(intermediate, src.AsPairLocation()->At(i), temp); + EmitNativeMove(dst.Split(i, zone_), intermediate_native, temp); + } + } else { + const auto& intermediate_native = + compiler::ffi::NativeLocation::FromLocation(intermediate, src_type, + zone_); + EmitMove(intermediate, src, temp); + EmitNativeMove(dst, intermediate_native, temp); + } + + if (dst_type.IsInt() && TMP == kNoRegister) { + temp->ReleaseTemporary(); + } + } + return; +} + #undef __ #endif // !defined(DART_PRECOMPILED_RUNTIME) diff --git a/runtime/vm/compiler/backend/flow_graph_compiler.h b/runtime/vm/compiler/backend/flow_graph_compiler.h index 5ce85ab49a4b..5c94d9a8e770 100644 --- a/runtime/vm/compiler/backend/flow_graph_compiler.h +++ b/runtime/vm/compiler/backend/flow_graph_compiler.h @@ -12,6 +12,7 @@ #include "vm/compiler/assembler/assembler.h" #include "vm/compiler/backend/code_statistics.h" #include "vm/compiler/backend/il.h" +#include "vm/compiler/backend/locations.h" #include "vm/runtime_entry.h" namespace dart { @@ -507,8 +508,46 @@ class FlowGraphCompiler : public ValueObject { bool TryIntrinsify(); // Emits code for a generic move from a location 'src' to a location 'dst'. + // + // Note that Location does not include a size (that can only be deduced from + // a Representation), so these moves might overapproximate the size needed + // to move. The maximal overapproximation is moving 8 bytes instead of 4 on + // 64 bit architectures. This overapproximation is not a problem, because + // the Dart calling convention only uses word-sized stack slots. + // + // TODO(dartbug.com/40400): Express this in terms of EmitMove(NativeLocation + // NativeLocation) to remove code duplication. void EmitMove(Location dst, Location src, TemporaryRegisterAllocator* temp); + // Emits code for a move from a location `src` to a location `dst`. + // + // Takes into account the payload and container representations of `dst` and + // `src` to do the smallest move possible, and sign (or zero) extend or + // truncate if needed. + // + // Makes use of TMP, FpuTMP, and `temp`. + void EmitNativeMove(const compiler::ffi::NativeLocation& dst, + const compiler::ffi::NativeLocation& src, + TemporaryRegisterAllocator* temp); + + // Helper method to move from a Location to a NativeLocation. + void EmitMoveToNative(const compiler::ffi::NativeLocation& dst, + Location src_loc, + Representation src_type, + TemporaryRegisterAllocator* temp); + + // Helper method to move from a NativeLocation to a Location. + void EmitMoveFromNative(Location dst_loc, + Representation dst_type, + const compiler::ffi::NativeLocation& src, + TemporaryRegisterAllocator* temp); + + // Emits a Dart const to a native location. + void EmitMoveConst(const compiler::ffi::NativeLocation& dst, + Location src, + Representation src_type, + TemporaryRegisterAllocator* temp); + void GenerateAssertAssignable(TokenPosition token_pos, intptr_t deopt_id, const AbstractType& dst_type, @@ -913,6 +952,10 @@ class FlowGraphCompiler : public ValueObject { friend class CheckedSmiSlowPath; // Same. friend class CheckedSmiComparisonSlowPath; // Same. + // Architecture specific implementation of simple native moves. + void EmitNativeMoveArchitecture(const compiler::ffi::NativeLocation& dst, + const compiler::ffi::NativeLocation& src); + void EmitFrameEntry(); bool TryIntrinsifyHelper(); diff --git a/runtime/vm/compiler/backend/flow_graph_compiler_arm.cc b/runtime/vm/compiler/backend/flow_graph_compiler_arm.cc index d491013d4640..b2ad07f9718e 100644 --- a/runtime/vm/compiler/backend/flow_graph_compiler_arm.cc +++ b/runtime/vm/compiler/backend/flow_graph_compiler_arm.cc @@ -1513,6 +1513,163 @@ void FlowGraphCompiler::EmitMove(Location destination, } } +static OperandSize BytesToOperandSize(intptr_t bytes) { + switch (bytes) { + case 4: + return OperandSize::kWord; + case 2: + return OperandSize::kHalfword; + case 1: + return OperandSize::kByte; + default: + UNIMPLEMENTED(); + } +} + +void FlowGraphCompiler::EmitNativeMoveArchitecture( + const compiler::ffi::NativeLocation& destination, + const compiler::ffi::NativeLocation& source) { + const auto& src_payload_type = source.payload_type(); + const auto& dst_payload_type = destination.payload_type(); + const auto& src_container_type = source.container_type(); + const auto& dst_container_type = destination.container_type(); + ASSERT(src_container_type.IsFloat() == dst_container_type.IsFloat()); + ASSERT(src_container_type.IsInt() == dst_container_type.IsInt()); + ASSERT(src_payload_type.IsSigned() == dst_payload_type.IsSigned()); + ASSERT(src_payload_type.IsFundamental()); + ASSERT(dst_payload_type.IsFundamental()); + const intptr_t src_size = src_payload_type.SizeInBytes(); + const intptr_t dst_size = dst_payload_type.SizeInBytes(); + const bool sign_or_zero_extend = dst_size > src_size; + + if (source.IsRegisters()) { + const auto& src = source.AsRegisters(); + ASSERT(src.num_regs() == 1); + ASSERT(src_size <= 4); + const auto src_reg = src.reg_at(0); + + if (destination.IsRegisters()) { + const auto& dst = destination.AsRegisters(); + ASSERT(dst.num_regs() == 1); + const auto dst_reg = dst.reg_at(0); + if (!sign_or_zero_extend) { + ASSERT(dst_size == 4); + __ mov(dst_reg, compiler::Operand(src_reg)); + } else { + ASSERT(sign_or_zero_extend); + // Arm has no sign- or zero-extension instructions, so use shifts. + const intptr_t shift_length = + (compiler::target::kWordSize - src_size) * kBitsPerByte; + __ Lsl(dst_reg, src_reg, compiler::Operand(shift_length)); + if (src_payload_type.IsSigned()) { + __ Asr(dst_reg, dst_reg, compiler::Operand(shift_length)); + } else { + __ Lsr(dst_reg, dst_reg, compiler::Operand(shift_length)); + } + } + + } else if (destination.IsFpuRegisters()) { + // Fpu Registers should only contain doubles and registers only ints. + // The bit casts are done with a BitCastInstr. + // TODO(dartbug.com/40371): Remove BitCastInstr and implement here. + UNIMPLEMENTED(); + + } else { + ASSERT(destination.IsStack()); + const auto& dst = destination.AsStack(); + ASSERT(!sign_or_zero_extend); + ASSERT(dst_size <= 4); + const OperandSize op_size = BytesToOperandSize(dst_size); + __ StoreToOffset(op_size, src.reg_at(0), dst.base_register(), + dst.offset_in_bytes()); + } + + } else if (source.IsFpuRegisters()) { + const auto& src = source.AsFpuRegisters(); + // We have not implemented conversions here, use IL convert instructions. + ASSERT(src_payload_type.Equals(dst_payload_type)); + + if (destination.IsRegisters()) { + // Fpu Registers should only contain doubles and registers only ints. + // The bit casts are done with a BitCastInstr. + // TODO(dartbug.com/40371): Remove BitCastInstr and implement here. + UNIMPLEMENTED(); + + } else if (destination.IsFpuRegisters()) { + const auto& dst = destination.AsFpuRegisters(); + switch (dst_size) { + case 16: + __ vmovq(dst.fpu_reg(), src.fpu_reg()); + return; + case 8: + // TODO(36309): Use the proper register instead of asuming even. + __ vmovd(EvenDRegisterOf(dst.fpu_reg()), + EvenDRegisterOf(src.fpu_reg())); + return; + default: + UNREACHABLE(); + } + + } else { + ASSERT(destination.IsStack()); + ASSERT(src_payload_type.IsFloat()); + const auto& dst = destination.AsStack(); + switch (dst_size) { + case 8: + // TODO(36309): Use the proper register instead of asuming even. + __ StoreDToOffset(EvenDRegisterOf(src.fpu_reg()), dst.base_register(), + dst.offset_in_bytes()); + return; + case 4: + // TODO(36309): Use the proper register instead of asuming even. + __ StoreSToOffset(EvenSRegisterOf(EvenDRegisterOf(src.fpu_reg())), + dst.base_register(), dst.offset_in_bytes()); + return; + default: + // TODO(dartbug.com/37470): Case 16 for simd packed data. + UNREACHABLE(); + } + } + + } else { + ASSERT(source.IsStack()); + const auto& src = source.AsStack(); + if (destination.IsRegisters()) { + const auto& dst = destination.AsRegisters(); + ASSERT(dst.num_regs() == 1); + const auto dst_reg = dst.reg_at(0); + ASSERT(!sign_or_zero_extend); + ASSERT(dst_size <= 4); + const OperandSize op_size = BytesToOperandSize(dst_size); + __ LoadFromOffset(op_size, dst_reg, src.base_register(), + src.offset_in_bytes()); + + } else if (destination.IsFpuRegisters()) { + ASSERT(src_payload_type.Equals(dst_payload_type)); + ASSERT(src_payload_type.IsFloat()); + const auto& dst = destination.AsFpuRegisters(); + switch (src_size) { + case 8: + // TODO(36309): Use the proper register instead of asuming even. + __ LoadDFromOffset(EvenDRegisterOf(dst.fpu_reg()), + src.base_register(), src.offset_in_bytes()); + return; + case 4: + // TODO(36309): Use the proper register instead of asuming even. + __ LoadSFromOffset(EvenSRegisterOf(EvenDRegisterOf(dst.fpu_reg())), + src.base_register(), src.offset_in_bytes()); + return; + default: + UNIMPLEMENTED(); + } + + } else { + ASSERT(destination.IsStack()); + UNREACHABLE(); + } + } +} + #undef __ #define __ compiler_->assembler()-> diff --git a/runtime/vm/compiler/backend/flow_graph_compiler_arm64.cc b/runtime/vm/compiler/backend/flow_graph_compiler_arm64.cc index 9a25c7b14bb8..92788631c658 100644 --- a/runtime/vm/compiler/backend/flow_graph_compiler_arm64.cc +++ b/runtime/vm/compiler/backend/flow_graph_compiler_arm64.cc @@ -1442,6 +1442,155 @@ void FlowGraphCompiler::EmitMove(Location destination, } } +static OperandSize BytesToOperandSize(intptr_t bytes) { + switch (bytes) { + case 8: + return OperandSize::kDoubleWord; + case 4: + return OperandSize::kWord; + case 2: + return OperandSize::kHalfword; + case 1: + return OperandSize::kByte; + default: + UNIMPLEMENTED(); + } +} + +void FlowGraphCompiler::EmitNativeMoveArchitecture( + const compiler::ffi::NativeLocation& destination, + const compiler::ffi::NativeLocation& source) { + const auto& src_type = source.payload_type(); + const auto& dst_type = destination.payload_type(); + ASSERT(src_type.IsFloat() == dst_type.IsFloat()); + ASSERT(src_type.IsInt() == dst_type.IsInt()); + ASSERT(src_type.IsSigned() == dst_type.IsSigned()); + ASSERT(src_type.IsFundamental()); + ASSERT(dst_type.IsFundamental()); + const intptr_t src_size = src_type.SizeInBytes(); + const intptr_t dst_size = dst_type.SizeInBytes(); + const bool sign_or_zero_extend = dst_size > src_size; + + if (source.IsRegisters()) { + const auto& src = source.AsRegisters(); + ASSERT(src.num_regs() == 1); + const auto src_reg = src.reg_at(0); + + if (destination.IsRegisters()) { + const auto& dst = destination.AsRegisters(); + ASSERT(dst.num_regs() == 1); + const auto dst_reg = dst.reg_at(0); + if (!sign_or_zero_extend) { + switch (dst_size) { + case 8: + __ mov(dst_reg, src_reg); + return; + case 4: + __ movw(dst_reg, src_reg); + return; + default: + UNIMPLEMENTED(); + } + } else { + switch (src_type.AsFundamental().representation()) { + case compiler::ffi::kInt8: // Sign extend operand. + __ sxtb(dst_reg, src_reg); + return; + case compiler::ffi::kInt16: + __ sxth(dst_reg, src_reg); + return; + case compiler::ffi::kUint8: // Zero extend operand. + __ uxtb(dst_reg, src_reg); + return; + case compiler::ffi::kUint16: + __ uxth(dst_reg, src_reg); + return; + default: + // 32 to 64 bit is covered in IL by Representation conversions. + UNIMPLEMENTED(); + } + } + + } else if (destination.IsFpuRegisters()) { + // Fpu Registers should only contain doubles and registers only ints. + UNIMPLEMENTED(); + + } else { + ASSERT(destination.IsStack()); + const auto& dst = destination.AsStack(); + ASSERT(!sign_or_zero_extend); + const OperandSize op_size = BytesToOperandSize(dst_size); + __ StoreToOffset(src.reg_at(0), dst.base_register(), + dst.offset_in_bytes(), op_size); + } + + } else if (source.IsFpuRegisters()) { + const auto& src = source.AsFpuRegisters(); + // We have not implemented conversions here, use IL convert instructions. + ASSERT(src_type.Equals(dst_type)); + + if (destination.IsRegisters()) { + // Fpu Registers should only contain doubles and registers only ints. + UNIMPLEMENTED(); + + } else if (destination.IsFpuRegisters()) { + const auto& dst = destination.AsFpuRegisters(); + __ vmov(dst.fpu_reg(), src.fpu_reg()); + + } else { + ASSERT(destination.IsStack()); + ASSERT(src_type.IsFloat()); + const auto& dst = destination.AsStack(); + switch (dst_size) { + case 8: + __ StoreDToOffset(src.fpu_reg(), dst.base_register(), + dst.offset_in_bytes()); + return; + case 4: + __ StoreSToOffset(src.fpu_reg(), dst.base_register(), + dst.offset_in_bytes()); + return; + default: + UNREACHABLE(); + } + } + + } else { + ASSERT(source.IsStack()); + const auto& src = source.AsStack(); + if (destination.IsRegisters()) { + const auto& dst = destination.AsRegisters(); + ASSERT(dst.num_regs() == 1); + const auto dst_reg = dst.reg_at(0); + ASSERT(!sign_or_zero_extend); + const OperandSize op_size = BytesToOperandSize(dst_size); + __ LoadFromOffset(dst_reg, src.base_register(), src.offset_in_bytes(), + op_size); + + } else if (destination.IsFpuRegisters()) { + ASSERT(src_type.Equals(dst_type)); + ASSERT(src_type.IsFloat()); + const auto& dst = destination.AsFpuRegisters(); + switch (src_size) { + case 8: + __ LoadDFromOffset(dst.fpu_reg(), src.base_register(), + src.offset_in_bytes()); + return; + case 4: + __ LoadSFromOffset(dst.fpu_reg(), src.base_register(), + src.offset_in_bytes()); + return; + default: + UNIMPLEMENTED(); + } + + } else { + ASSERT(destination.IsStack()); + UNREACHABLE(); + } + } +} + #undef __ #define __ compiler_->assembler()-> diff --git a/runtime/vm/compiler/backend/flow_graph_compiler_ia32.cc b/runtime/vm/compiler/backend/flow_graph_compiler_ia32.cc index d5d204d038eb..da3e9c5329eb 100644 --- a/runtime/vm/compiler/backend/flow_graph_compiler_ia32.cc +++ b/runtime/vm/compiler/backend/flow_graph_compiler_ia32.cc @@ -1298,6 +1298,163 @@ void FlowGraphCompiler::EmitMove(Location destination, } } +void FlowGraphCompiler::EmitNativeMoveArchitecture( + const compiler::ffi::NativeLocation& destination, + const compiler::ffi::NativeLocation& source) { + const auto& src_type = source.payload_type(); + const auto& dst_type = destination.payload_type(); + ASSERT(src_type.IsFloat() == dst_type.IsFloat()); + ASSERT(src_type.IsInt() == dst_type.IsInt()); + ASSERT(src_type.IsSigned() == dst_type.IsSigned()); + ASSERT(src_type.IsFundamental()); + ASSERT(dst_type.IsFundamental()); + const intptr_t src_size = src_type.SizeInBytes(); + const intptr_t dst_size = dst_type.SizeInBytes(); + const bool sign_or_zero_extend = dst_size > src_size; + + if (source.IsRegisters()) { + const auto& src = source.AsRegisters(); + ASSERT(src.num_regs() == 1); + ASSERT(src_size <= 4); + const auto src_reg = src.reg_at(0); + + if (destination.IsRegisters()) { + const auto& dst = destination.AsRegisters(); + ASSERT(dst.num_regs() == 1); + const auto dst_reg = dst.reg_at(0); + if (!sign_or_zero_extend) { + ASSERT(dst_size == 4); + __ movl(dst_reg, src_reg); + } else { + switch (src_type.AsFundamental().representation()) { + case compiler::ffi::kInt8: // Sign extend operand. + __ movsxb(dst_reg, ByteRegisterOf(src_reg)); + return; + case compiler::ffi::kInt16: + __ movsxw(dst_reg, src_reg); + return; + case compiler::ffi::kUint8: // Zero extend operand. + __ movzxb(dst_reg, ByteRegisterOf(src_reg)); + return; + case compiler::ffi::kUint16: + __ movzxw(dst_reg, src_reg); + return; + default: + // 32 to 64 bit is covered in IL by Representation conversions. + UNIMPLEMENTED(); + } + } + + } else if (destination.IsFpuRegisters()) { + // Fpu Registers should only contain doubles and registers only ints. + UNIMPLEMENTED(); + + } else { + ASSERT(destination.IsStack()); + ASSERT(!sign_or_zero_extend); + const auto& dst = destination.AsStack(); + const auto dst_addr = NativeLocationToStackSlotAddress(dst); + switch (dst_size) { + case 4: + __ movl(dst_addr, src_reg); + return; + case 2: + __ movw(dst_addr, src_reg); + return; + case 1: + __ movb(dst_addr, ByteRegisterOf(src_reg)); + return; + default: + UNREACHABLE(); + } + } + + } else if (source.IsFpuRegisters()) { + const auto& src = source.AsFpuRegisters(); + // We have not implemented conversions here, use IL convert instructions. + ASSERT(src_type.Equals(dst_type)); + + if (destination.IsRegisters()) { + // Fpu Registers should only contain doubles and registers only ints. + UNIMPLEMENTED(); + + } else if (destination.IsFpuRegisters()) { + const auto& dst = destination.AsFpuRegisters(); + // Optimization manual recommends using MOVAPS for register + // to register moves. + __ movaps(dst.fpu_reg(), src.fpu_reg()); + + } else { + ASSERT(destination.IsStack()); + ASSERT(src_type.IsFloat()); + const auto& dst = destination.AsStack(); + const auto dst_addr = NativeLocationToStackSlotAddress(dst); + switch (dst_size) { + case 8: + __ movsd(dst_addr, src.fpu_reg()); + return; + case 4: + __ movss(dst_addr, src.fpu_reg()); + return; + default: + UNREACHABLE(); + } + } + + } else { + ASSERT(source.IsStack()); + const auto& src = source.AsStack(); + const auto src_addr = NativeLocationToStackSlotAddress(src); + if (destination.IsRegisters()) { + const auto& dst = destination.AsRegisters(); + ASSERT(dst.num_regs() == 1); + ASSERT(dst_size <= 4); + const auto dst_reg = dst.reg_at(0); + if (!sign_or_zero_extend) { + ASSERT(dst_size == 4); + __ movl(dst_reg, src_addr); + } else { + switch (src_type.AsFundamental().representation()) { + case compiler::ffi::kInt8: // Sign extend operand. + __ movsxb(dst_reg, src_addr); + return; + case compiler::ffi::kInt16: + __ movsxw(dst_reg, src_addr); + return; + case compiler::ffi::kUint8: // Zero extend operand. + __ movzxb(dst_reg, src_addr); + return; + case compiler::ffi::kUint16: + __ movzxw(dst_reg, src_addr); + return; + default: + // 32 to 64 bit is covered in IL by Representation conversions. + UNIMPLEMENTED(); + } + } + + } else if (destination.IsFpuRegisters()) { + ASSERT(src_type.Equals(dst_type)); + ASSERT(src_type.IsFloat()); + const auto& dst = destination.AsFpuRegisters(); + switch (dst_size) { + case 8: + __ movsd(dst.fpu_reg(), src_addr); + return; + case 4: + __ movss(dst.fpu_reg(), src_addr); + return; + default: + UNREACHABLE(); + } + + } else { + ASSERT(destination.IsStack()); + UNREACHABLE(); + } + } +} + #undef __ #define __ compiler_->assembler()-> diff --git a/runtime/vm/compiler/backend/flow_graph_compiler_x64.cc b/runtime/vm/compiler/backend/flow_graph_compiler_x64.cc index af7a58261462..2c2696490567 100644 --- a/runtime/vm/compiler/backend/flow_graph_compiler_x64.cc +++ b/runtime/vm/compiler/backend/flow_graph_compiler_x64.cc @@ -9,6 +9,7 @@ #include "vm/compiler/backend/il_printer.h" #include "vm/compiler/backend/locations.h" +#include "vm/compiler/ffi/native_location.h" #include "vm/compiler/jit/compiler.h" #include "vm/dart_entry.h" #include "vm/deopt_instructions.h" @@ -1433,6 +1434,180 @@ void FlowGraphCompiler::EmitMove(Location destination, } } +void FlowGraphCompiler::EmitNativeMoveArchitecture( + const compiler::ffi::NativeLocation& destination, + const compiler::ffi::NativeLocation& source) { + const auto& src_type = source.payload_type(); + const auto& dst_type = destination.payload_type(); + ASSERT(src_type.IsFloat() == dst_type.IsFloat()); + ASSERT(src_type.IsInt() == dst_type.IsInt()); + ASSERT(src_type.IsSigned() == dst_type.IsSigned()); + ASSERT(src_type.IsFundamental()); + ASSERT(dst_type.IsFundamental()); + const intptr_t src_size = src_type.SizeInBytes(); + const intptr_t dst_size = dst_type.SizeInBytes(); + const bool sign_or_zero_extend = dst_size > src_size; + + if (source.IsRegisters()) { + const auto& src = source.AsRegisters(); + ASSERT(src.num_regs() == 1); + const auto src_reg = src.reg_at(0); + + if (destination.IsRegisters()) { + const auto& dst = destination.AsRegisters(); + ASSERT(dst.num_regs() == 1); + const auto dst_reg = dst.reg_at(0); + if (!sign_or_zero_extend) { + switch (dst_size) { + case 8: + __ movq(dst_reg, src_reg); + return; + case 4: + __ movl(dst_reg, src_reg); + return; + default: + UNIMPLEMENTED(); + } + } else { + switch (src_type.AsFundamental().representation()) { + case compiler::ffi::kInt8: // Sign extend operand. + __ movsxb(dst_reg, src_reg); + return; + case compiler::ffi::kInt16: + __ movsxw(dst_reg, src_reg); + return; + case compiler::ffi::kUint8: // Zero extend operand. + __ movzxb(dst_reg, src_reg); + return; + case compiler::ffi::kUint16: + __ movzxw(dst_reg, src_reg); + return; + default: + // 32 to 64 bit is covered in IL by Representation conversions. + UNIMPLEMENTED(); + } + } + + } else if (destination.IsFpuRegisters()) { + // Fpu Registers should only contain doubles and registers only ints. + UNIMPLEMENTED(); + + } else { + ASSERT(destination.IsStack()); + const auto& dst = destination.AsStack(); + const auto dst_addr = NativeLocationToStackSlotAddress(dst); + ASSERT(!sign_or_zero_extend); + switch (dst_size) { + case 8: + __ movq(dst_addr, src_reg); + return; + case 4: + __ movl(dst_addr, src_reg); + return; + case 2: + __ movw(dst_addr, src_reg); + return; + case 1: + __ movb(dst_addr, src_reg); + return; + default: + UNREACHABLE(); + } + } + + } else if (source.IsFpuRegisters()) { + const auto& src = source.AsFpuRegisters(); + // We have not implemented conversions here, use IL convert instructions. + ASSERT(src_type.Equals(dst_type)); + + if (destination.IsRegisters()) { + // Fpu Registers should only contain doubles and registers only ints. + UNIMPLEMENTED(); + + } else if (destination.IsFpuRegisters()) { + const auto& dst = destination.AsFpuRegisters(); + // Optimization manual recommends using MOVAPS for register + // to register moves. + __ movaps(dst.fpu_reg(), src.fpu_reg()); + + } else { + ASSERT(destination.IsStack()); + ASSERT(src_type.IsFloat()); + const auto& dst = destination.AsStack(); + const auto dst_addr = NativeLocationToStackSlotAddress(dst); + switch (dst_size) { + case 8: + __ movsd(dst_addr, src.fpu_reg()); + return; + case 4: + __ movss(dst_addr, src.fpu_reg()); + return; + default: + UNREACHABLE(); + } + } + + } else { + ASSERT(source.IsStack()); + const auto& src = source.AsStack(); + const auto src_addr = NativeLocationToStackSlotAddress(src); + if (destination.IsRegisters()) { + const auto& dst = destination.AsRegisters(); + ASSERT(dst.num_regs() == 1); + const auto dst_reg = dst.reg_at(0); + if (!sign_or_zero_extend) { + switch (dst_size) { + case 8: + __ movq(dst_reg, src_addr); + return; + case 4: + __ movl(dst_reg, src_addr); + return; + default: + UNIMPLEMENTED(); + } + } else { + switch (src_type.AsFundamental().representation()) { + case compiler::ffi::kInt8: // Sign extend operand. + __ movsxb(dst_reg, src_addr); + return; + case compiler::ffi::kInt16: + __ movsxw(dst_reg, src_addr); + return; + case compiler::ffi::kUint8: // Zero extend operand. + __ movzxb(dst_reg, src_addr); + return; + case compiler::ffi::kUint16: + __ movzxw(dst_reg, src_addr); + return; + default: + // 32 to 64 bit is covered in IL by Representation conversions. + UNIMPLEMENTED(); + } + } + + } else if (destination.IsFpuRegisters()) { + ASSERT(src_type.Equals(dst_type)); + ASSERT(src_type.IsFloat()); + const auto& dst = destination.AsFpuRegisters(); + switch (dst_size) { + case 8: + __ movsd(dst.fpu_reg(), src_addr); + return; + case 4: + __ movss(dst.fpu_reg(), src_addr); + return; + default: + UNREACHABLE(); + } + + } else { + ASSERT(destination.IsStack()); + UNREACHABLE(); + } + } +} + #undef __ #define __ compiler_->assembler()-> diff --git a/runtime/vm/compiler/backend/il.cc b/runtime/vm/compiler/backend/il.cc index 229afc42c64c..9d2846efa54d 100644 --- a/runtime/vm/compiler/backend/il.cc +++ b/runtime/vm/compiler/backend/il.cc @@ -4098,26 +4098,30 @@ void NativeParameterInstr::EmitNativeCode(FlowGraphCompiler* compiler) { constexpr intptr_t kEntryFramePadding = 4; compiler::ffi::FrameRebase rebase( /*old_base=*/SPREG, /*new_base=*/FPREG, - -kExitLinkSlotFromEntryFp + kEntryFramePadding); - const Location dst = locs()->out(0); - const Location src = rebase.Rebase(loc_); + (-kExitLinkSlotFromEntryFp + kEntryFramePadding) * + compiler::target::kWordSize, + compiler->zone()); + const auto& src = + rebase.Rebase(marshaller_.NativeLocationOfNativeParameter(index_)); NoTemporaryAllocator no_temp; - compiler->EmitMove(dst, src, &no_temp); + const Location out_loc = locs()->out(0); + const Representation out_rep = representation(); + compiler->EmitMoveFromNative(out_loc, out_rep, src, &no_temp); } LocationSummary* NativeParameterInstr::MakeLocationSummary(Zone* zone, bool opt) const { ASSERT(opt); - Location input = Location::Any(); + Location output = Location::Any(); if (representation() == kUnboxedInt64 && compiler::target::kWordSize < 8) { - input = Location::Pair(Location::RequiresRegister(), - Location::RequiresFpuRegister()); + output = Location::Pair(Location::RequiresRegister(), + Location::RequiresFpuRegister()); } else { - input = RegisterKindForResult() == Location::kRegister - ? Location::RequiresRegister() - : Location::RequiresFpuRegister(); + output = RegisterKindForResult() == Location::kRegister + ? Location::RequiresRegister() + : Location::RequiresFpuRegister(); } - return LocationSummary::Make(zone, /*num_inputs=*/0, input, + return LocationSummary::Make(zone, /*num_inputs=*/0, output, LocationSummary::kNoCall); } @@ -5473,7 +5477,7 @@ Representation FfiCallInstr::RequiredInputRepresentation(intptr_t idx) const { if (idx == TargetAddressIndex()) { return kUnboxedFfiIntPtr; } else { - return arg_representations_[idx]; + return marshaller_.RepInFfiCall(idx); } } @@ -5507,45 +5511,54 @@ LocationSummary* FfiCallInstr::MakeLocationSummary(Zone* zone, summary->set_temp(2, Location::RegisterLocation( CallingConventions::kSecondCalleeSavedCpuReg)); #endif - summary->set_out(0, compiler::ffi::ResultLocation( - compiler::ffi::ResultRepresentation(signature_))); + summary->set_out(0, marshaller_.LocInFfiCall(compiler::ffi::kResultIndex)); - for (intptr_t i = 0, n = NativeArgCount(); i < n; ++i) { - // Floating point values are never split: they are either in a single "FPU" - // register or a contiguous 64-bit slot on the stack. Unboxed 64-bit integer - // values, in contrast, can be split between any two registers on a 32-bit - // system. - // - // There is an exception for iOS and Android 32-bit ARM, where - // floating-point values are treated as integers as far as the calling - // convention is concerned. However, the representation of these arguments - // are set to kUnboxedInt32 or kUnboxedInt64 already, so we don't have to - // account for that here. - const bool is_atomic = arg_representations_[i] == kUnboxedFloat || - arg_representations_[i] == kUnboxedDouble; - - // Since we have to move this input down to the stack, there's no point in - // pinning it to any specific register. - summary->set_in(i, UnallocateStackSlots(arg_locations_[i], is_atomic)); + for (intptr_t i = 0, n = marshaller_.num_args(); i < n; ++i) { + summary->set_in(i, marshaller_.LocInFfiCall(i)); } return summary; } -Location FfiCallInstr::UnallocateStackSlots(Location in, bool is_atomic) { - if (in.IsPairLocation()) { - ASSERT(!is_atomic); - return Location::Pair(UnallocateStackSlots(in.AsPairLocation()->At(0)), - UnallocateStackSlots(in.AsPairLocation()->At(1))); - } else if (in.IsMachineRegister()) { - return in; - } else if (in.IsDoubleStackSlot()) { - return is_atomic ? Location::Any() - : Location::Pair(Location::Any(), Location::Any()); - } else { - ASSERT(in.IsStackSlot()); - return Location::Any(); +void FfiCallInstr::EmitParamMoves(FlowGraphCompiler* compiler) { + const Register saved_fp = locs()->temp(0).reg(); + const Register temp = locs()->temp(1).reg(); + + compiler::ffi::FrameRebase rebase(/*old_base=*/FPREG, /*new_base=*/saved_fp, + /*stack_delta=*/0, zone_); + for (intptr_t i = 0, n = NativeArgCount(); i < n; ++i) { + const Location origin = rebase.Rebase(locs()->in(i)); + const Representation origin_rep = RequiredInputRepresentation(i); + const auto& target = marshaller_.Location(i); + ConstantTemporaryAllocator temp_alloc(temp); + if (origin.IsConstant()) { + compiler->EmitMoveConst(target, origin, origin_rep, &temp_alloc); + } else { + compiler->EmitMoveToNative(target, origin, origin_rep, &temp_alloc); + } + } +} + +void FfiCallInstr::EmitReturnMoves(FlowGraphCompiler* compiler) { + const auto& src = marshaller_.Location(compiler::ffi::kResultIndex); + if (src.payload_type().IsVoid()) { + return; } + const Location dst_loc = locs()->out(0); + const Representation dst_type = representation(); + NoTemporaryAllocator no_temp; + compiler->EmitMoveFromNative(dst_loc, dst_type, src, &no_temp); +} + +void NativeReturnInstr::EmitReturnMoves(FlowGraphCompiler* compiler) { + const auto& dst = marshaller_.Location(compiler::ffi::kResultIndex); + if (dst.payload_type().IsVoid()) { + return; + } + const Location src_loc = locs()->in(0); + const Representation src_type = RequiredInputRepresentation(0); + NoTemporaryAllocator no_temp; + compiler->EmitMoveToNative(dst, src_loc, src_type, &no_temp); } LocationSummary* NativeReturnInstr::MakeLocationSummary(Zone* zone, @@ -5554,14 +5567,15 @@ LocationSummary* NativeReturnInstr::MakeLocationSummary(Zone* zone, const intptr_t kNumTemps = 0; LocationSummary* locs = new (zone) LocationSummary(zone, kNumInputs, kNumTemps, LocationSummary::kNoCall); - locs->set_in(0, result_location_); + locs->set_in( + 0, marshaller_.LocationOfNativeParameter(compiler::ffi::kResultIndex)); return locs; } #undef Z Representation FfiCallInstr::representation() const { - return compiler::ffi::ResultRepresentation(signature_); + return marshaller_.RepInFfiCall(compiler::ffi::kResultIndex); } // SIMD diff --git a/runtime/vm/compiler/backend/il.h b/runtime/vm/compiler/backend/il.h index c14556e08f0f..202a0ce9cf61 100644 --- a/runtime/vm/compiler/backend/il.h +++ b/runtime/vm/compiler/backend/il.h @@ -13,6 +13,10 @@ #include "vm/compiler/backend/locations.h" #include "vm/compiler/backend/slot.h" #include "vm/compiler/compiler_state.h" +#include "vm/compiler/ffi/marshaller.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/compiler/method_recognizer.h" #include "vm/flags.h" #include "vm/growable_array.h" @@ -492,7 +496,6 @@ struct InstrAttrs { M(UnboxInt32, kNoGC) \ M(IntConverter, _) \ M(BitCast, _) \ - M(UnboxedWidthExtender, _) \ M(Deoptimize, kNoGC) \ M(SimdOp, kNoGC) @@ -1844,7 +1847,7 @@ class FunctionEntryInstr : public BlockEntryWithInitialDefs { // NativeParameter instead (which doesn't count as an initial definition). class NativeEntryInstr : public FunctionEntryInstr { public: - NativeEntryInstr(const ZoneGrowableArray* argument_locations, + NativeEntryInstr(const compiler::ffi::CallbackMarshaller& marshaller, GraphEntryInstr* graph_entry, intptr_t block_id, intptr_t try_index, @@ -1852,17 +1855,18 @@ class NativeEntryInstr : public FunctionEntryInstr { intptr_t callback_id) : FunctionEntryInstr(graph_entry, block_id, try_index, deopt_id), callback_id_(callback_id), - argument_locations_(argument_locations) {} + marshaller_(marshaller) {} DECLARE_INSTRUCTION(NativeEntry) PRINT_TO_SUPPORT private: - void SaveArgument(FlowGraphCompiler* compiler, Location loc) const; + void SaveArgument(FlowGraphCompiler* compiler, + const compiler::ffi::NativeLocation& loc) const; const intptr_t callback_id_; - const ZoneGrowableArray* const argument_locations_; + const compiler::ffi::CallbackMarshaller& marshaller_; }; // Represents an OSR entrypoint to a function. @@ -2489,21 +2493,18 @@ class ParameterInstr : public Definition { // TOOD(33549): Unify with ParameterInstr. class NativeParameterInstr : public Definition { public: - NativeParameterInstr(Location loc, Representation representation) - : loc_(loc), representation_(representation) { - if (loc.IsPairLocation()) { - for (intptr_t i : {0, 1}) { - ASSERT(loc_.Component(i).HasStackIndex() && - loc_.Component(i).base_reg() == SPREG); - } - } else { - ASSERT(loc_.HasStackIndex() && loc_.base_reg() == SPREG); - } + NativeParameterInstr(const compiler::ffi::CallbackMarshaller& marshaller, + intptr_t index) + : marshaller_(marshaller), index_(index) { + const auto& loc = marshaller.NativeLocationOfNativeParameter(index_); + ASSERT(loc.IsStack() && loc.AsStack().base_register() == SPREG); } DECLARE_INSTRUCTION(NativeParameter) - virtual Representation representation() const { return representation_; } + virtual Representation representation() const { + return marshaller_.RepInFfiCall(index_); + } intptr_t InputCount() const { return 0; } Value* InputAt(intptr_t i) const { @@ -2525,8 +2526,8 @@ class NativeParameterInstr : public Definition { private: virtual void RawSetInputAt(intptr_t i, Value* value) { UNREACHABLE(); } - const Location loc_; - const Representation representation_; + const compiler::ffi::CallbackMarshaller& marshaller_; + const intptr_t index_; DISALLOW_COPY_AND_ASSIGN(NativeParameterInstr); }; @@ -2765,12 +2766,9 @@ class NativeReturnInstr : public ReturnInstr { public: NativeReturnInstr(TokenPosition token_pos, Value* value, - Representation rep, - Location result_location, + const compiler::ffi::CallbackMarshaller& marshaller, intptr_t deopt_id) - : ReturnInstr(token_pos, value, deopt_id), - result_representation_(rep), - result_location_(result_location) {} + : ReturnInstr(token_pos, value, deopt_id), marshaller_(marshaller) {} DECLARE_INSTRUCTION(NativeReturn) @@ -2778,7 +2776,7 @@ class NativeReturnInstr : public ReturnInstr { virtual Representation RequiredInputRepresentation(intptr_t idx) const { ASSERT(idx == 0); - return result_representation_; + return marshaller_.RepInFfiCall(compiler::ffi::kResultIndex); } virtual bool CanBecomeDeoptimizationTarget() const { @@ -2788,8 +2786,9 @@ class NativeReturnInstr : public ReturnInstr { } private: - const Representation result_representation_; - const Location result_location_; + const compiler::ffi::CallbackMarshaller& marshaller_; + + void EmitReturnMoves(FlowGraphCompiler* compiler); DISALLOW_COPY_AND_ASSIGN(NativeReturnInstr); }; @@ -4742,17 +4741,12 @@ class FfiCallInstr : public Definition { public: FfiCallInstr(Zone* zone, intptr_t deopt_id, - const Function& signature, - const ZoneGrowableArray& arg_reps, - const ZoneGrowableArray& arg_locs) + const compiler::ffi::CallMarshaller& marshaller) : Definition(deopt_id), zone_(zone), - signature_(signature), - inputs_(arg_reps.length() + 1), - arg_representations_(arg_reps), - arg_locations_(arg_locs) { - inputs_.FillWith(nullptr, 0, arg_reps.length() + 1); - ASSERT(signature.IsZoneHandle()); + marshaller_(marshaller), + inputs_(marshaller.num_args() + 1) { + inputs_.FillWith(nullptr, 0, marshaller.num_args() + 1); } DECLARE_INSTRUCTION(FfiCall) @@ -4789,16 +4783,13 @@ class FfiCallInstr : public Definition { private: virtual void RawSetInputAt(intptr_t i, Value* value) { inputs_[i] = value; } - // Mark stack slots in 'loc' as unallocated. Split a double-word stack slot - // into a pair location if 'is_atomic' is false. - static Location UnallocateStackSlots(Location loc, bool is_atomic = false); + void EmitParamMoves(FlowGraphCompiler* compiler); + void EmitReturnMoves(FlowGraphCompiler* compiler); Zone* const zone_; - const Function& signature_; + const compiler::ffi::CallMarshaller& marshaller_; GrowableArray inputs_; - const ZoneGrowableArray& arg_representations_; - const ZoneGrowableArray& arg_locations_; DISALLOW_COPY_AND_ASSIGN(FfiCallInstr); }; @@ -8395,74 +8386,6 @@ class BitCastInstr : public TemplateDefinition<1, NoThrow, Pure> { DISALLOW_COPY_AND_ASSIGN(BitCastInstr); }; -// Sign- or zero-extends an integer in unboxed 32-bit representation. -// -// The choice between sign- and zero- extension is made based on the whether the -// chosen representation is signed or unsigned. -// -// It is only supported to extend 1- or 2-byte operands; however, since we don't -// have a representation less than 32-bits, both the input and output -// representations are 32-bit (and equal). -class UnboxedWidthExtenderInstr : public TemplateDefinition<1, NoThrow, Pure> { - public: - UnboxedWidthExtenderInstr(Value* value, - Representation rep, - SmallRepresentation from_rep) - : TemplateDefinition(DeoptId::kNone), - representation_(rep), - from_representation_(from_rep) { - ASSERT((rep == kUnboxedInt32 && (from_rep == kSmallUnboxedInt8 || - from_rep == kSmallUnboxedInt16)) || - (rep == kUnboxedUint32 && (from_rep == kSmallUnboxedUint8 || - from_rep == kSmallUnboxedUint16))); - SetInputAt(0, value); - } - - Value* value() const { return inputs_[0]; } - - Representation representation() const { return representation_; } - - SmallRepresentation from_representation() const { - return from_representation_; - } - - bool ComputeCanDeoptimize() const { return false; } - - virtual Representation RequiredInputRepresentation(intptr_t idx) const { - ASSERT(idx == 0); - return representation_; - } - - virtual bool AttributesEqual(Instruction* other) const { - ASSERT(other->IsUnboxedWidthExtender()); - const UnboxedWidthExtenderInstr* ext = other->AsUnboxedWidthExtender(); - return ext->representation() == representation() && - ext->from_representation_ == from_representation_; - } - - virtual CompileType ComputeType() const { return CompileType::Int(); } - - DECLARE_INSTRUCTION(UnboxedWidthExtender); - - PRINT_OPERANDS_TO_SUPPORT - - private: - intptr_t from_width_bytes() const { - if (from_representation_ == kSmallUnboxedInt8 || - from_representation_ == kSmallUnboxedUint8) { - return 1; - } - ASSERT(from_representation_ == kSmallUnboxedInt16 || - from_representation_ == kSmallUnboxedUint16); - return 2; - } - - const Representation representation_; - const SmallRepresentation from_representation_; - - DISALLOW_COPY_AND_ASSIGN(UnboxedWidthExtenderInstr); -}; - // SimdOpInstr // // All SIMD intrinsics and recognized methods are represented via instances diff --git a/runtime/vm/compiler/backend/il_arm.cc b/runtime/vm/compiler/backend/il_arm.cc index f6aa7360611e..80dcb51ea329 100644 --- a/runtime/vm/compiler/backend/il_arm.cc +++ b/runtime/vm/compiler/backend/il_arm.cc @@ -13,7 +13,6 @@ #include "vm/compiler/backend/locations_helpers.h" #include "vm/compiler/backend/range_analysis.h" #include "vm/compiler/compiler_state.h" -#include "vm/compiler/ffi/frame_rebase.h" #include "vm/compiler/ffi/native_calling_convention.h" #include "vm/compiler/jit/compiler.h" #include "vm/cpu.h" @@ -1133,17 +1132,9 @@ void FfiCallInstr::EmitNativeCode(FlowGraphCompiler* compiler) { __ EnterDartFrame(0, /*load_pool_pointer=*/false); // Reserve space for arguments and align frame before entering C++ world. - __ ReserveAlignedFrameSpace(compiler::ffi::NumStackSlots(arg_locations_) * - compiler::target::kWordSize); + __ ReserveAlignedFrameSpace(marshaller_.StackTopInBytes()); - compiler::ffi::FrameRebase rebase(/*old_base=*/FPREG, /*new_base=*/saved_fp, - /*stack_delta=*/0); - for (intptr_t i = 0, n = NativeArgCount(); i < n; ++i) { - const Location origin = rebase.Rebase(locs()->in(i)); - const Location target = arg_locations_[i]; - ConstantTemporaryAllocator temp_alloc(temp); - compiler->EmitMove(target, origin, &temp_alloc); - } + EmitParamMoves(compiler); // We need to copy the return address up into the dummy stack frame so the // stack walker will know which safepoint to use. @@ -1188,6 +1179,8 @@ void FfiCallInstr::EmitNativeCode(FlowGraphCompiler* compiler) { THR, compiler::target::Thread::global_object_pool_offset())); } + EmitReturnMoves(compiler); + // Leave dummy exit frame. __ LeaveDartFrame(); __ set_constant_pool_allowed(true); @@ -1197,6 +1190,8 @@ void FfiCallInstr::EmitNativeCode(FlowGraphCompiler* compiler) { } void NativeReturnInstr::EmitNativeCode(FlowGraphCompiler* compiler) { + EmitReturnMoves(compiler); + __ LeaveDartFrame(); // The dummy return address is in LR, no need to pop it as on Intel. @@ -1240,13 +1235,12 @@ void NativeReturnInstr::EmitNativeCode(FlowGraphCompiler* compiler) { __ set_constant_pool_allowed(true); } -void NativeEntryInstr::SaveArgument(FlowGraphCompiler* compiler, - Location loc) const { +static void save_argument(FlowGraphCompiler* compiler, Location loc) { if (loc.IsPairLocation()) { // Save higher-order component first, so bytes are in little-endian layout // overall. for (intptr_t i : {1, 0}) { - SaveArgument(compiler, loc.Component(i)); + save_argument(compiler, loc.Component(i)); } return; } @@ -1264,6 +1258,14 @@ void NativeEntryInstr::SaveArgument(FlowGraphCompiler* compiler, } } +void NativeEntryInstr::SaveArgument( + FlowGraphCompiler* compiler, + const compiler::ffi::NativeLocation& nloc) const { + const Location loc = nloc.WidenTo4Bytes(compiler->zone()).AsLocation(); + + save_argument(compiler, loc); +} + void NativeEntryInstr::EmitNativeCode(FlowGraphCompiler* compiler) { // Constant pool cannot be used until we enter the actual Dart frame. __ set_constant_pool_allowed(false); @@ -1275,8 +1277,8 @@ void NativeEntryInstr::EmitNativeCode(FlowGraphCompiler* compiler) { __ EnterFrame((1 << FP) | (1 << LR), 0); // Save the argument registers, in reverse order. - for (intptr_t i = argument_locations_->length(); i-- > 0;) { - SaveArgument(compiler, argument_locations_->At(i)); + for (intptr_t i = marshaller_.num_args(); i-- > 0;) { + SaveArgument(compiler, marshaller_.Location(i)); } // Enter the entry frame. @@ -1605,8 +1607,7 @@ LocationSummary* LoadIndexedInstr::MakeLocationSummary(Zone* zone, (representation() == kUnboxedFloat64x2)) { if (class_id() == kTypedDataFloat32ArrayCid) { // Need register < Q7 for float operations. - // TODO(fschneider): Add a register policy to specify a subset of - // registers. + // TODO(30953): Support register range constraints in the regalloc. locs->set_out(0, Location::FpuRegisterLocation(Q6)); } else { locs->set_out(0, Location::RequiresFpuRegister()); @@ -4648,6 +4649,10 @@ LocationSummary* UnboxInstr::MakeLocationSummary(Zone* zone, bool opt) const { Location::RequiresRegister())); } else if (representation() == kUnboxedInt32) { summary->set_out(0, Location::RequiresRegister()); + } else if (representation() == kUnboxedFloat) { + // Low (< Q7) Q registers are needed for the vcvtds and vmovs instructions. + // TODO(30953): Support register range constraints in the regalloc. + summary->set_out(0, Location::FpuRegisterLocation(Q6)); } else { summary->set_out(0, Location::RequiresFpuRegister()); } @@ -4674,6 +4679,7 @@ void UnboxInstr::EmitLoadFromBox(FlowGraphCompiler* compiler) { } case kUnboxedFloat: { + // Should only be <= Q7, because >= Q8 cannot be addressed as S register. const DRegister result = EvenDRegisterOf(locs()->out(0).fpu_reg()); __ LoadDFromOffset(result, box, ValueOffset() - kHeapObjectTag); __ vcvtsd(EvenSRegisterOf(result), result); @@ -7238,37 +7244,6 @@ void IntConverterInstr::EmitNativeCode(FlowGraphCompiler* compiler) { } } -LocationSummary* UnboxedWidthExtenderInstr::MakeLocationSummary( - Zone* zone, - bool is_optimizing) const { - const intptr_t kNumTemps = 0; - LocationSummary* summary = new (zone) - LocationSummary(zone, /*num_inputs=*/InputCount(), - /*num_temps=*/kNumTemps, LocationSummary::kNoCall); - summary->set_in(0, Location::RequiresRegister()); - summary->set_out(0, Location::SameAsFirstInput()); - return summary; -} - -void UnboxedWidthExtenderInstr::EmitNativeCode(FlowGraphCompiler* compiler) { - Register reg = locs()->in(0).reg(); - // There are no builtin sign- or zero-extension instructions, so we'll have to - // use shifts instead. - const intptr_t shift_length = - (compiler::target::kWordSize - from_width_bytes()) * kBitsPerByte; - __ Lsl(reg, reg, compiler::Operand(shift_length)); - switch (representation_) { - case kUnboxedInt32: // Sign extend operand. - __ Asr(reg, reg, compiler::Operand(shift_length)); - break; - case kUnboxedUint32: // Zero extend operand. - __ Lsr(reg, reg, compiler::Operand(shift_length)); - break; - default: - UNREACHABLE(); - } -} - LocationSummary* BitCastInstr::MakeLocationSummary(Zone* zone, bool opt) const { LocationSummary* summary = new (zone) LocationSummary(zone, /*num_inputs=*/InputCount(), diff --git a/runtime/vm/compiler/backend/il_arm64.cc b/runtime/vm/compiler/backend/il_arm64.cc index 8076992f4771..4ed71aa17b9f 100644 --- a/runtime/vm/compiler/backend/il_arm64.cc +++ b/runtime/vm/compiler/backend/il_arm64.cc @@ -12,7 +12,6 @@ #include "vm/compiler/backend/locations.h" #include "vm/compiler/backend/locations_helpers.h" #include "vm/compiler/backend/range_analysis.h" -#include "vm/compiler/ffi/frame_rebase.h" #include "vm/compiler/ffi/native_calling_convention.h" #include "vm/compiler/jit/compiler.h" #include "vm/dart_entry.h" @@ -998,17 +997,9 @@ void FfiCallInstr::EmitNativeCode(FlowGraphCompiler* compiler) { __ EnterDartFrame(0, PP); // Make space for arguments and align the frame. - __ ReserveAlignedFrameSpace(compiler::ffi::NumStackSlots(arg_locations_) * - kWordSize); + __ ReserveAlignedFrameSpace(marshaller_.StackTopInBytes()); - compiler::ffi::FrameRebase rebase(/*old_base=*/FPREG, /*new_base=*/saved_fp, - /*stack_delta=*/0); - for (intptr_t i = 0, n = NativeArgCount(); i < n; ++i) { - const Location origin = rebase.Rebase(locs()->in(i)); - const Location target = arg_locations_[i]; - ConstantTemporaryAllocator temp_alloc(temp); - compiler->EmitMove(target, origin, &temp_alloc); - } + EmitParamMoves(compiler); // We need to copy a dummy return address up into the dummy stack frame so the // stack walker will know which safepoint to use. @@ -1057,6 +1048,8 @@ void FfiCallInstr::EmitNativeCode(FlowGraphCompiler* compiler) { // Refresh pinned registers values (inc. write barrier mask and null object). __ RestorePinnedRegisters(); + EmitReturnMoves(compiler); + // Although PP is a callee-saved register, it may have been moved by the GC. __ LeaveDartFrame(compiler::kRestoreCallerPP); @@ -1070,6 +1063,8 @@ void FfiCallInstr::EmitNativeCode(FlowGraphCompiler* compiler) { } void NativeReturnInstr::EmitNativeCode(FlowGraphCompiler* compiler) { + EmitReturnMoves(compiler); + __ LeaveDartFrame(); // The dummy return address is in LR, no need to pop it as on Intel. @@ -1115,16 +1110,17 @@ void NativeReturnInstr::EmitNativeCode(FlowGraphCompiler* compiler) { __ set_constant_pool_allowed(true); } -void NativeEntryInstr::SaveArgument(FlowGraphCompiler* compiler, - Location loc) const { - ASSERT(!loc.IsPairLocation()); - - if (loc.HasStackIndex()) return; - - if (loc.IsRegister()) { - __ Push(loc.reg()); - } else if (loc.IsFpuRegister()) { - __ PushDouble(loc.fpu_reg()); +void NativeEntryInstr::SaveArgument( + FlowGraphCompiler* compiler, + const compiler::ffi::NativeLocation& nloc) const { + if (nloc.IsStack()) return; + + if (nloc.IsRegisters()) { + const auto& regs_loc = nloc.AsRegisters(); + ASSERT(regs_loc.num_regs() == 1); + __ Push(regs_loc.reg_at(0)); + } else if (nloc.IsFpuRegisters()) { + __ PushDouble(nloc.AsFpuRegisters().fpu_reg()); } else { UNREACHABLE(); } @@ -1147,8 +1143,8 @@ void NativeEntryInstr::EmitNativeCode(FlowGraphCompiler* compiler) { __ EnterFrame(0); // Save the argument registers, in reverse order. - for (intptr_t i = argument_locations_->length(); i-- > 0;) { - SaveArgument(compiler, argument_locations_->At(i)); + for (intptr_t i = marshaller_.num_args(); i-- > 0;) { + SaveArgument(compiler, marshaller_.Location(i)); } // Enter the entry frame. @@ -6343,38 +6339,6 @@ void IntConverterInstr::EmitNativeCode(FlowGraphCompiler* compiler) { } } -LocationSummary* UnboxedWidthExtenderInstr::MakeLocationSummary( - Zone* zone, - bool is_optimizing) const { - const intptr_t kNumTemps = 0; - LocationSummary* summary = new (zone) - LocationSummary(zone, /*num_inputs=*/InputCount(), - /*num_temps=*/kNumTemps, LocationSummary::kNoCall); - summary->set_in(0, Location::RequiresRegister()); - summary->set_out(0, Location::SameAsFirstInput()); - return summary; -} - -void UnboxedWidthExtenderInstr::EmitNativeCode(FlowGraphCompiler* compiler) { - Register reg = locs()->in(0).reg(); - switch (from_representation()) { - case kSmallUnboxedInt8: // Sign extend operand. - __ sxtb(reg, reg); - break; - case kSmallUnboxedInt16: - __ sxth(reg, reg); - break; - case kSmallUnboxedUint8: // Zero extend operand. - __ uxtb(reg, reg); - break; - case kSmallUnboxedUint16: - __ uxth(reg, reg); - break; - default: - UNREACHABLE(); - } -} - LocationSummary* ThrowInstr::MakeLocationSummary(Zone* zone, bool opt) const { const intptr_t kNumInputs = 1; const intptr_t kNumTemps = 0; diff --git a/runtime/vm/compiler/backend/il_ia32.cc b/runtime/vm/compiler/backend/il_ia32.cc index 8f28b4a65421..b272d58f834f 100644 --- a/runtime/vm/compiler/backend/il_ia32.cc +++ b/runtime/vm/compiler/backend/il_ia32.cc @@ -13,7 +13,6 @@ #include "vm/compiler/backend/locations.h" #include "vm/compiler/backend/locations_helpers.h" #include "vm/compiler/backend/range_analysis.h" -#include "vm/compiler/ffi/frame_rebase.h" #include "vm/compiler/ffi/native_calling_convention.h" #include "vm/compiler/frontend/flow_graph_builder.h" #include "vm/compiler/jit/compiler.h" @@ -142,9 +141,12 @@ void ReturnInstr::EmitNativeCode(FlowGraphCompiler* compiler) { } void NativeReturnInstr::EmitNativeCode(FlowGraphCompiler* compiler) { + EmitReturnMoves(compiler); + bool return_in_st0 = false; - if (result_representation_ == kUnboxedFloat || - result_representation_ == kUnboxedDouble) { + if (marshaller_.Location(compiler::ffi::kResultIndex) + .payload_type() + .IsFloat()) { ASSERT(locs()->in(0).IsFpuRegister() && locs()->in(0).fpu_reg() == XMM0); return_in_st0 = true; } @@ -187,7 +189,9 @@ void NativeReturnInstr::EmitNativeCode(FlowGraphCompiler* compiler) { // Move XMM0 into ST0 if needed. if (return_in_st0) { - if (result_representation_ == kUnboxedDouble) { + if (marshaller_.Location(compiler::ffi::kResultIndex) + .payload_type() + .SizeInBytes() == 8) { __ movsd(compiler::Address(SPREG, -8), XMM0); __ fldl(compiler::Address(SPREG, -8)); } else { @@ -938,21 +942,14 @@ void FfiCallInstr::EmitNativeCode(FlowGraphCompiler* compiler) { // We need to create a dummy "exit frame". It will have a null code object. __ LoadObject(CODE_REG, Object::null_object()); - __ EnterDartFrame(compiler::ffi::NumStackSlots(arg_locations_) * kWordSize); + __ EnterDartFrame(marshaller_.StackTopInBytes()); // Align frame before entering C++ world. if (OS::ActivationFrameAlignment() > 1) { __ andl(SPREG, compiler::Immediate(~(OS::ActivationFrameAlignment() - 1))); } - compiler::ffi::FrameRebase rebase(/*old_base=*/FPREG, /*new_base=*/saved_fp, - /*stack_delta=*/0); - for (intptr_t i = 0, n = NativeArgCount(); i < n; ++i) { - const Location origin = rebase.Rebase(locs()->in(i)); - const Location target = arg_locations_[i]; - ConstantTemporaryAllocator temp_alloc(temp); - compiler->EmitMove(target, origin, &temp_alloc); - } + EmitParamMoves(compiler); // We need to copy a dummy return address up into the dummy stack frame so the // stack walker will know which safepoint to use. Unlike X64, there's no @@ -997,6 +994,8 @@ void FfiCallInstr::EmitNativeCode(FlowGraphCompiler* compiler) { __ movss(XMM0, compiler::Address(SPREG, -kFloatSize)); } + EmitReturnMoves(compiler); + // Leave dummy exit frame. __ LeaveFrame(); @@ -1004,27 +1003,11 @@ void FfiCallInstr::EmitNativeCode(FlowGraphCompiler* compiler) { __ popl(temp); } -void NativeEntryInstr::SaveArgument(FlowGraphCompiler* compiler, - Location loc) const { - if (loc.IsPairLocation()) { - // Save the components in reverse order so that they will be in - // little-endian order on the stack. - for (intptr_t i : {1, 0}) { - SaveArgument(compiler, loc.Component(i)); - } - return; - } - - if (loc.HasStackIndex()) return; - - if (loc.IsRegister()) { - __ pushl(loc.reg()); - } else if (loc.IsFpuRegister()) { - __ subl(SPREG, compiler::Immediate(8)); - __ movsd(compiler::Address(SPREG, 0), loc.fpu_reg()); - } else { - UNREACHABLE(); - } +void NativeEntryInstr::SaveArgument( + FlowGraphCompiler* compiler, + const compiler::ffi::NativeLocation& nloc) const { + // IA32 has no arguments passed in registers. + UNREACHABLE(); } void NativeEntryInstr::EmitNativeCode(FlowGraphCompiler* compiler) { @@ -6250,37 +6233,6 @@ void IntConverterInstr::EmitNativeCode(FlowGraphCompiler* compiler) { } } -LocationSummary* UnboxedWidthExtenderInstr::MakeLocationSummary( - Zone* zone, - bool is_optimizing) const { - const intptr_t kNumTemps = 0; - LocationSummary* summary = new (zone) - LocationSummary(zone, /*num_inputs=*/InputCount(), - /*num_temps=*/kNumTemps, LocationSummary::kNoCall); - summary->set_in(0, Location::RegisterLocation(EAX)); - summary->set_out(0, Location::RegisterLocation(EAX)); - return summary; -} - -void UnboxedWidthExtenderInstr::EmitNativeCode(FlowGraphCompiler* compiler) { - switch (from_representation()) { - case kSmallUnboxedInt8: // Sign extend operand. - __ movsxb(EAX, AL); - break; - case kSmallUnboxedInt16: - __ movsxw(EAX, EAX); - break; - case kSmallUnboxedUint8: // Zero extend operand. - __ movzxb(EAX, AL); - break; - case kSmallUnboxedUint16: - __ movzxw(EAX, EAX); - break; - default: - UNREACHABLE(); - } -} - LocationSummary* ThrowInstr::MakeLocationSummary(Zone* zone, bool opt) const { const intptr_t kNumInputs = 1; const intptr_t kNumTemps = 0; diff --git a/runtime/vm/compiler/backend/il_printer.cc b/runtime/vm/compiler/backend/il_printer.cc index 53909e646769..f7905561188d 100644 --- a/runtime/vm/compiler/backend/il_printer.cc +++ b/runtime/vm/compiler/backend/il_printer.cc @@ -6,6 +6,7 @@ #include "vm/compiler/backend/il.h" #include "vm/compiler/backend/range_analysis.h" +#include "vm/compiler/ffi/native_calling_convention.h" #include "vm/os.h" #include "vm/parser.h" @@ -500,16 +501,6 @@ void ClosureCallInstr::PrintOperandsTo(BufferFormatter* f) const { } } -void FfiCallInstr::PrintOperandsTo(BufferFormatter* f) const { - f->Print(" pointer="); - InputAt(TargetAddressIndex())->PrintTo(f); - for (intptr_t i = 0, n = InputCount(); i < n - 1; ++i) { - f->Print(", "); - InputAt(i)->PrintTo(f); - f->Print(" (@%s)", arg_locations_[i].ToCString()); - } -} - void InstanceCallInstr::PrintOperandsTo(BufferFormatter* f) const { f->Print(" %s<%" Pd ">", function_name().ToCString(), type_args_len()); for (intptr_t i = 0; i < ArgumentCount(); ++i) { @@ -1007,12 +998,6 @@ void IntConverterInstr::PrintOperandsTo(BufferFormatter* f) const { Definition::PrintOperandsTo(f); } -void UnboxedWidthExtenderInstr::PrintOperandsTo(BufferFormatter* f) const { - f->Print("%" Pd " -> 4 (%s), ", from_width_bytes(), - RepresentationToCString(representation())); - Definition::PrintOperandsTo(f); -} - void BitCastInstr::PrintOperandsTo(BufferFormatter* f) const { Definition::PrintOperandsTo(f); f->Print(" (%s -> %s)", RepresentationToCString(from()), @@ -1086,13 +1071,31 @@ void ReturnInstr::PrintOperandsTo(BufferFormatter* f) const { } } +void FfiCallInstr::PrintOperandsTo(BufferFormatter* f) const { + f->Print(" pointer="); + InputAt(TargetAddressIndex())->PrintTo(f); + for (intptr_t i = 0, n = InputCount(); i < n - 1; ++i) { + f->Print(", "); + InputAt(i)->PrintTo(f); + f->Print(" (@"); + marshaller_.Location(i).PrintTo(f); + f->Print(")"); + } +} + void NativeReturnInstr::PrintOperandsTo(BufferFormatter* f) const { value()->PrintTo(f); + f->Print(" (@"); + marshaller_.Location(compiler::ffi::kResultIndex).PrintTo(f); + f->Print(")"); } void NativeParameterInstr::PrintOperandsTo(BufferFormatter* f) const { - f->Print("%s as %s", loc_.ToCString(), - RepresentationToCString(representation_)); + // Where the calling convention puts it. + marshaller_.Location(index_).PrintTo(f); + f->Print(" at "); + // Where the arguments are when pushed on the stack. + marshaller_.NativeLocationOfNativeParameter(index_).PrintTo(f); } void CatchBlockEntryInstr::PrintTo(BufferFormatter* f) const { diff --git a/runtime/vm/compiler/backend/il_x64.cc b/runtime/vm/compiler/backend/il_x64.cc index c05bbe67e31b..19370ff6eaf0 100644 --- a/runtime/vm/compiler/backend/il_x64.cc +++ b/runtime/vm/compiler/backend/il_x64.cc @@ -13,7 +13,6 @@ #include "vm/compiler/backend/locations.h" #include "vm/compiler/backend/locations_helpers.h" #include "vm/compiler/backend/range_analysis.h" -#include "vm/compiler/ffi/frame_rebase.h" #include "vm/compiler/ffi/native_calling_convention.h" #include "vm/compiler/jit/compiler.h" #include "vm/dart_entry.h" @@ -146,6 +145,8 @@ void ReturnInstr::EmitNativeCode(FlowGraphCompiler* compiler) { } void NativeReturnInstr::EmitNativeCode(FlowGraphCompiler* compiler) { + EmitReturnMoves(compiler); + __ LeaveDartFrame(); // Pop dummy return address. @@ -935,7 +936,6 @@ void NativeCallInstr::EmitNativeCode(FlowGraphCompiler* compiler) { void FfiCallInstr::EmitNativeCode(FlowGraphCompiler* compiler) { const Register saved_fp = locs()->temp(0).reg(); - const Register temp = locs()->temp(1).reg(); const Register target_address = locs()->in(TargetAddressIndex()).reg(); // Save frame pointer because we're going to update it when we enter the exit @@ -949,22 +949,14 @@ void FfiCallInstr::EmitNativeCode(FlowGraphCompiler* compiler) { // but have a null code object. __ LoadObject(CODE_REG, Object::null_object()); __ set_constant_pool_allowed(false); - __ EnterDartFrame(compiler::ffi::NumStackSlots(arg_locations_) * kWordSize, - PP); + __ EnterDartFrame(marshaller_.StackTopInBytes(), PP); // Align frame before entering C++ world. if (OS::ActivationFrameAlignment() > 1) { __ andq(SPREG, compiler::Immediate(~(OS::ActivationFrameAlignment() - 1))); } - compiler::ffi::FrameRebase rebase(/*old_base=*/FPREG, /*new_base=*/saved_fp, - /*stack_delta=*/0); - for (intptr_t i = 0, n = NativeArgCount(); i < n; ++i) { - const Location origin = rebase.Rebase(locs()->in(i)); - const Location target = arg_locations_[i]; - ConstantTemporaryAllocator temp_alloc(temp); - compiler->EmitMove(target, rebase.Rebase(origin), &temp_alloc); - } + EmitParamMoves(compiler); // We need to copy a dummy return address up into the dummy stack frame so the // stack walker will know which safepoint to use. RIP points to the *next* @@ -1000,6 +992,8 @@ void FfiCallInstr::EmitNativeCode(FlowGraphCompiler* compiler) { __ call(TMP); } + EmitReturnMoves(compiler); + // Although PP is a callee-saved register, it may have been moved by the GC. __ LeaveDartFrame(compiler::kRestoreCallerPP); @@ -1015,16 +1009,17 @@ void FfiCallInstr::EmitNativeCode(FlowGraphCompiler* compiler) { __ popq(TMP); } -void NativeEntryInstr::SaveArgument(FlowGraphCompiler* compiler, - Location loc) const { - ASSERT(!loc.IsPairLocation()); - - if (loc.HasStackIndex()) return; - - if (loc.IsRegister()) { - __ pushq(loc.reg()); - } else if (loc.IsFpuRegister()) { - __ movq(TMP, loc.fpu_reg()); +void NativeEntryInstr::SaveArgument( + FlowGraphCompiler* compiler, + const compiler::ffi::NativeLocation& nloc) const { + if (nloc.IsStack()) return; + + if (nloc.IsRegisters()) { + const auto& regs_loc = nloc.AsRegisters(); + ASSERT(regs_loc.num_regs() == 1); + __ pushq(regs_loc.reg_at(0)); + } else if (nloc.IsFpuRegisters()) { + __ movq(TMP, nloc.AsFpuRegisters().fpu_reg()); __ pushq(TMP); } else { UNREACHABLE(); @@ -1045,8 +1040,8 @@ void NativeEntryInstr::EmitNativeCode(FlowGraphCompiler* compiler) { #endif // Save the argument registers, in reverse order. - for (intptr_t i = argument_locations_->length(); i-- > 0;) { - SaveArgument(compiler, argument_locations_->At(i)); + for (intptr_t i = marshaller_.num_args(); i-- > 0;) { + SaveArgument(compiler, marshaller_.Location(i)); } // Enter the entry frame. Push a dummy return address for consistency with @@ -6579,37 +6574,6 @@ void IntConverterInstr::EmitNativeCode(FlowGraphCompiler* compiler) { } } -LocationSummary* UnboxedWidthExtenderInstr::MakeLocationSummary( - Zone* zone, - bool is_optimizing) const { - const intptr_t kNumTemps = 0; - LocationSummary* summary = new (zone) - LocationSummary(zone, /*num_inputs=*/InputCount(), - /*num_temps=*/kNumTemps, LocationSummary::kNoCall); - summary->set_in(0, Location::RegisterLocation(RAX)); - summary->set_out(0, Location::RegisterLocation(RAX)); - return summary; -} - -void UnboxedWidthExtenderInstr::EmitNativeCode(FlowGraphCompiler* compiler) { - switch (from_representation()) { - case kSmallUnboxedInt8: // Sign extend operand. - __ movsxb(RAX, RAX); - break; - case kSmallUnboxedInt16: - __ movsxw(RAX, RAX); - break; - case kSmallUnboxedUint8: // Zero extend operand. - __ movzxb(RAX, RAX); - break; - case kSmallUnboxedUint16: - __ movzxw(RAX, RAX); - break; - default: - UNREACHABLE(); - } -} - LocationSummary* ThrowInstr::MakeLocationSummary(Zone* zone, bool opt) const { const intptr_t kNumInputs = 1; const intptr_t kNumTemps = 0; diff --git a/runtime/vm/compiler/backend/locations.h b/runtime/vm/compiler/backend/locations.h index 82bf9116b96b..da9cc2b510a7 100644 --- a/runtime/vm/compiler/backend/locations.h +++ b/runtime/vm/compiler/backend/locations.h @@ -36,16 +36,6 @@ enum Representation { kNumRepresentations }; -// The representation of 8 and 16 bit integers in 32 bit. SmallRepresentation -// tracks the real representation of these small integers. -enum SmallRepresentation { - kNoSmallRepresentation, - kSmallUnboxedInt8, - kSmallUnboxedUint8, - kSmallUnboxedInt16, - kSmallUnboxedUint16, -}; - // 'UnboxedFfiIntPtr' should be able to hold a pointer of the target word-size. // On a 32-bit platform, it's an unsigned 32-bit int because it should be // zero-extended to 64-bits, not sign-extended (pointers are inherently diff --git a/runtime/vm/compiler/compiler_sources.gni b/runtime/vm/compiler/compiler_sources.gni index 93a1fc40efb8..b14ca033fc03 100644 --- a/runtime/vm/compiler/compiler_sources.gni +++ b/runtime/vm/compiler/compiler_sources.gni @@ -109,8 +109,10 @@ compiler_sources = [ "ffi/callback.h", "ffi/native_calling_convention.cc", "ffi/native_calling_convention.h", - "ffi/native_representation.cc", - "ffi/native_representation.h", + "ffi/native_location.cc", + "ffi/native_location.h", + "ffi/native_type.cc", + "ffi/native_type.h", "ffi/recognized_method.cc", "ffi/recognized_method.h", "frontend/base_flow_graph_builder.cc", diff --git a/runtime/vm/compiler/ffi/README.md b/runtime/vm/compiler/ffi/README.md new file mode 100644 index 000000000000..b46637f6f6d5 --- /dev/null +++ b/runtime/vm/compiler/ffi/README.md @@ -0,0 +1,15 @@ +The files in this folder have the following dependencies. +Arrow means depends on, invert arrows for includes. + +``` + Call & CallbackMarshaller + ↓ + NativeCallingConvention + ↓ ↓ (Interop with backend) + ↓ NativeLocation → Location +(Interop with frontend) ↓ ↓ +AbstractType ← NativeType → Representation +ClassId ← +``` + +The other files stand on themselves. diff --git a/runtime/vm/compiler/ffi/abi.cc b/runtime/vm/compiler/ffi/abi.cc index f3ea956c003c..a96a33f203ae 100644 --- a/runtime/vm/compiler/ffi/abi.cc +++ b/runtime/vm/compiler/ffi/abi.cc @@ -52,9 +52,14 @@ Abi TargetAbi() { (defined(TARGET_OS_LINUX) || defined(TARGET_OS_MACOS) || \ defined(TARGET_OS_ANDROID))) || \ (defined(TARGET_ARCH_ARM) && defined(TARGET_OS_IOS)) + static_assert( + CallingConventions::kFieldAlignment == kAlignedToValueSizeBut8AlignedTo4, + "FFI transformation alignment"); return Abi::kWordSize32Align32; #elif defined(TARGET_ARCH_IA32) && defined(TARGET_OS_WINDOWS) || \ defined(TARGET_ARCH_ARM) + static_assert(CallingConventions::kFieldAlignment == kAlignedToValueSize, + "FFI transformation alignment"); return Abi::kWordSize32Align64; #else #error "Unknown platform. Please add alignment requirements for ABI." diff --git a/runtime/vm/compiler/ffi/frame_rebase.cc b/runtime/vm/compiler/ffi/frame_rebase.cc index deef5eed0d1b..cc12a561f1ad 100644 --- a/runtime/vm/compiler/ffi/frame_rebase.cc +++ b/runtime/vm/compiler/ffi/frame_rebase.cc @@ -12,6 +12,16 @@ namespace compiler { namespace ffi { +const NativeLocation& FrameRebase::Rebase(const NativeLocation& loc) const { + if (!loc.IsStack() || loc.AsStack().base_register() != old_base_) { + return loc; + } + + return *new (zone_) NativeStackLocation( + loc.payload_type(), loc.container_type(), new_base_, + loc.AsStack().offset_in_bytes() + stack_delta_in_bytes_); +} + Location FrameRebase::Rebase(const Location loc) const { if (loc.IsPairLocation()) { return Location::Pair(Rebase(loc.Component(0)), Rebase(loc.Component(1))); @@ -20,7 +30,8 @@ Location FrameRebase::Rebase(const Location loc) const { return loc; } - const intptr_t new_stack_index = loc.stack_index() + stack_delta_; + const intptr_t new_stack_index = + loc.stack_index() + stack_delta_in_bytes_ / compiler::target::kWordSize; if (loc.IsStackSlot()) { return Location::StackSlot(new_stack_index, new_base_); } diff --git a/runtime/vm/compiler/ffi/frame_rebase.h b/runtime/vm/compiler/ffi/frame_rebase.h index be10aa7edd51..33d8683f4e8e 100644 --- a/runtime/vm/compiler/ffi/frame_rebase.h +++ b/runtime/vm/compiler/ffi/frame_rebase.h @@ -6,6 +6,8 @@ #define RUNTIME_VM_COMPILER_FFI_FRAME_REBASE_H_ #include "vm/compiler/backend/locations.h" +#include "vm/compiler/ffi/native_location.h" +#include "vm/compiler/ffi/native_type.h" #include "vm/compiler/runtime_api.h" #include "vm/thread.h" @@ -20,20 +22,29 @@ namespace ffi { // frame manipulations. // // If the stack offset register matches 'old_base', it is changed to 'new_base' -// and 'stack_delta' (# of slots) is applied. +// and 'stack_delta_in_bytes' (# of bytes) is applied. +// +// This class can be used to rebase both Locations and NativeLocations. class FrameRebase : public ValueObject { public: FrameRebase(const Register old_base, const Register new_base, - intptr_t stack_delta) - : old_base_(old_base), new_base_(new_base), stack_delta_(stack_delta) {} + intptr_t stack_delta_in_bytes, + Zone* zone) + : old_base_(old_base), + new_base_(new_base), + stack_delta_in_bytes_(stack_delta_in_bytes), + zone_(zone) {} + + const NativeLocation& Rebase(const NativeLocation& loc) const; Location Rebase(const Location loc) const; private: const Register old_base_; const Register new_base_; - const intptr_t stack_delta_; + const intptr_t stack_delta_in_bytes_; + Zone* zone_; }; } // namespace ffi diff --git a/runtime/vm/compiler/ffi/marshaller.cc b/runtime/vm/compiler/ffi/marshaller.cc index 758d1ad04985..2d75e1493f1a 100644 --- a/runtime/vm/compiler/ffi/marshaller.cc +++ b/runtime/vm/compiler/ffi/marshaller.cc @@ -5,6 +5,8 @@ #include "vm/compiler/ffi/marshaller.h" #include "vm/compiler/ffi/frame_rebase.h" +#include "vm/compiler/ffi/native_location.h" +#include "vm/compiler/ffi/native_type.h" #include "vm/raw_object.h" #include "vm/stack_frame.h" #include "vm/symbols.h" @@ -17,74 +19,128 @@ namespace ffi { #if !defined(DART_PRECOMPILED_RUNTIME) -ZoneGrowableArray* -CallbackArgumentTranslator::TranslateArgumentLocations( - const ZoneGrowableArray& arg_locs) { - auto& pushed_locs = *(new ZoneGrowableArray(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])); +Location CallMarshaller::LocInFfiCall(intptr_t arg_index) const { + if (arg_index == kResultIndex) { + return Location(arg_index).AsLocation(); } - return &pushed_locs; -} - -void CallbackArgumentTranslator::AllocateArgument(Location arg) { - if (arg.IsPairLocation()) { - AllocateArgument(arg.Component(0)); - AllocateArgument(arg.Component(1)); - return; - } - if (arg.HasStackIndex()) return; - ASSERT(arg.IsRegister() || arg.IsFpuRegister()); - if (arg.IsRegister()) { - argument_slots_required_++; - } else { - argument_slots_required_ += 8 / target::kWordSize; + // Floating point values are never split: they are either in a single "FPU" + // register or a contiguous 64-bit slot on the stack. Unboxed 64-bit integer + // values, in contrast, can be split between any two registers on a 32-bit + // system. + // + // There is an exception for iOS and Android 32-bit ARM, where + // floating-point values are treated as integers as far as the calling + // convention is concerned. However, the representation of these arguments + // are set to kUnboxedInt32 or kUnboxedInt64 already, so we don't have to + // account for that here. + const bool is_atomic = RepInFfiCall(arg_index) == kUnboxedDouble || + RepInFfiCall(arg_index) == kUnboxedFloat; + + const NativeLocation& loc = this->Location(arg_index); + + if (loc.IsStack()) { + if (loc.payload_type().SizeInBytes() == 2 * compiler::target::kWordSize && + !is_atomic) { + return Location::Pair(Location::Any(), Location::Any()); + } + return Location::Any(); } + return loc.AsLocation(); } -Location CallbackArgumentTranslator::TranslateArgument(Location arg) { - if (arg.IsPairLocation()) { - const Location low = TranslateArgument(arg.Component(0)); - const Location high = TranslateArgument(arg.Component(1)); - return Location::Pair(low, high); +// This classes translates the ABI location of arguments into the locations they +// will inhabit after entry-frame setup in the invocation of a native callback. +// +// Native -> Dart callbacks must push all the arguments before executing any +// Dart code because the reading the Thread from TLS requires calling a native +// stub, and the argument registers are volatile on all ABIs we support. +// +// To avoid complicating initial definitions, all callback arguments are read +// off the stack from their pushed locations, so this class updates the argument +// positions to account for this. +// +// See 'NativeEntryInstr::EmitNativeCode' for details. +class CallbackArgumentTranslator : public ValueObject { + public: + static NativeLocations& TranslateArgumentLocations( + const NativeLocations& arg_locs, + Zone* zone) { + auto& pushed_locs = *(new 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)); + } + + return pushed_locs; } - if (arg.HasStackIndex()) { - // Add extra slots after the saved arguments for the return address and - // frame pointer of the dummy arguments frame, which will be between the - // saved argument registers and stack arguments. Also add slots for the - // shadow space if present (factored into - // kCallbackSlotsBeforeSavedArguments). - // - // Finally, if we are using NativeCallbackTrampolines, factor in the extra - // stack space corresponding to those trampolines' frames (above the entry - // frame). - intptr_t stack_delta = kCallbackSlotsBeforeSavedArguments; - if (NativeCallbackTrampolines::Enabled()) { - stack_delta += StubCodeCompiler::kNativeCallbackTrampolineStackDelta; + private: + 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 { + argument_slots_required_ += 8 / target::kWordSize; } - FrameRebase rebase( - /*old_base=*/SPREG, /*new_base=*/SPREG, - /*stack_delta=*/argument_slots_required_ + stack_delta); - return rebase.Rebase(arg); } - if (arg.IsRegister()) { - return Location::StackSlot(argument_slots_used_++, SPREG); + const NativeLocation& TranslateArgument(const NativeLocation& arg, + Zone* zone) { + if (arg.IsStack()) { + // Add extra slots after the saved arguments for the return address and + // frame pointer of the dummy arguments frame, which will be between the + // saved argument registers and stack arguments. Also add slots for the + // shadow space if present (factored into + // kCallbackSlotsBeforeSavedArguments). + // + // Finally, if we are using NativeCallbackTrampolines, factor in the extra + // stack space corresponding to those trampolines' frames (above the entry + // frame). + intptr_t stack_delta = kCallbackSlotsBeforeSavedArguments; + if (NativeCallbackTrampolines::Enabled()) { + stack_delta += StubCodeCompiler::kNativeCallbackTrampolineStackDelta; + } + FrameRebase rebase( + /*old_base=*/SPREG, /*new_base=*/SPREG, + /*stack_delta=*/(argument_slots_required_ + stack_delta) * + compiler::target::kWordSize, + zone); + return rebase.Rebase(arg); + } + + if (arg.IsRegisters()) { + const auto& result = *new (zone) NativeStackLocation( + arg.payload_type(), arg.container_type(), SPREG, + argument_slots_used_ * compiler::target::kWordSize); + argument_slots_used_ += arg.AsRegisters().num_regs(); + return result; + } + + ASSERT(arg.IsFpuRegisters()); + const auto& result = *new (zone) NativeStackLocation( + arg.payload_type(), arg.container_type(), SPREG, + argument_slots_used_ * compiler::target::kWordSize); + argument_slots_used_ += 8 / target::kWordSize; + return result; } - ASSERT(arg.IsFpuRegister()); - const Location result = - Location::DoubleStackSlot(argument_slots_used_, SPREG); - argument_slots_used_ += 8 / target::kWordSize; - return result; -} + intptr_t argument_slots_used_ = 0; + intptr_t argument_slots_required_ = 0; +}; + +CallbackMarshaller::CallbackMarshaller(Zone* zone, + const Function& dart_signature) + : BaseMarshaller(zone, dart_signature), + callback_locs_( + CallbackArgumentTranslator::TranslateArgumentLocations(arg_locs_, + zone_)) {} #endif // !defined(DART_PRECOMPILED_RUNTIME) diff --git a/runtime/vm/compiler/ffi/marshaller.h b/runtime/vm/compiler/ffi/marshaller.h index de85c2ce5bf4..ea7d2a8b6b2f 100644 --- a/runtime/vm/compiler/ffi/marshaller.h +++ b/runtime/vm/compiler/ffi/marshaller.h @@ -10,6 +10,8 @@ #include "vm/compiler/backend/locations.h" #include "vm/compiler/ffi/callback.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/object.h" namespace dart { @@ -18,29 +20,102 @@ namespace compiler { namespace ffi { -// This classes translates the ABI location of arguments into the locations they -// will inhabit after entry-frame setup in the invocation of a native callback. +// Provides the mapping from the native calling convention to the Dart calling +// convention. // -// Native -> Dart callbacks must push all the arguments before executing any -// Dart code because the reading the Thread from TLS requires calling a native -// stub, and the argument registers are volatile on all ABIs we support. +// 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. // -// To avoid complicating initial definitions, all callback arguments are read -// off the stack from their pushed locations, so this class updates the argument -// positions to account for this. -// -// See 'NativeEntryInstr::EmitNativeCode' for details. -class CallbackArgumentTranslator : public ValueObject { +// TODO(36730): Add a way to query arguments that are broken into multiple +// parts. +class BaseMarshaller : public NativeCallingConvention { public: - static ZoneGrowableArray* TranslateArgumentLocations( - const ZoneGrowableArray& arg_locs); + // Unboxed representation on how the value is passed or received from regular + // Dart code. + Representation RepInDart(intptr_t arg_index) const { + return Location(arg_index).payload_type().AsRepresentationOverApprox(zone_); + } + + // Representation on how the value is passed to or recieved from the FfiCall + // instruction or StaticCall, NativeParameter, and NativeReturn instructions. + Representation RepInFfiCall(intptr_t arg_index) const { + if (Location(arg_index).container_type().IsInt() && + Location(arg_index).payload_type().IsFloat()) { + return Location(arg_index).container_type().AsRepresentationOverApprox( + zone_); + } + return Location(arg_index).payload_type().AsRepresentationOverApprox(zone_); + } + + // Bitcasting floats to ints, only required in SoftFP. + bool RequiresBitCast(intptr_t index) const { + return Location(index).payload_type().IsFloat() && + Location(index).container_type().IsInt(); + } + + // 8 or 16 bit int value to sign extend from. + const NativeType& SignExtendFrom(intptr_t arg_index) const { + return Location(arg_index).payload_type(); + } + + // Requires boxing or unboxing. + bool IsPointer(intptr_t arg_index) const { + return AbstractType::Handle(zone_, Type(arg_index)).type_class_id() == + kFfiPointerCid; + } + + // Treated as a null constant in Dart. + bool IsVoid(intptr_t arg_index) const { + return AbstractType::Handle(zone_, Type(arg_index)).type_class_id() == + kFfiVoidCid; + } + + RawString* 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()); + } private: - void AllocateArgument(Location arg); - Location TranslateArgument(Location arg); + // Contains the function pointer as argument #0. + // The Dart signature is used for the function and argument names. + const Function& dart_signature_; +}; + +class CallMarshaller : public BaseMarshaller { + public: + CallMarshaller(Zone* zone, const Function& dart_signature) + : BaseMarshaller(zone, dart_signature) {} + + dart::Location LocInFfiCall(intptr_t arg_index) const; +}; + +class CallbackMarshaller : public BaseMarshaller { + public: + CallbackMarshaller(Zone* zone, const Function& dart_signature); + + // All parameters are saved on stack to do safe-point transition. + const NativeLocation& NativeLocationOfNativeParameter( + intptr_t arg_index) const { + if (arg_index == kResultIndex) { + // No moving around of result. + return Location(arg_index); + } + return *callback_locs_.At(arg_index); + } + + // All parameters are saved on stack to do safe-point transition. + dart::Location LocationOfNativeParameter(intptr_t arg_index) const { + return NativeLocationOfNativeParameter(arg_index).AsLocation(); + } - intptr_t argument_slots_used_ = 0; - intptr_t argument_slots_required_ = 0; + protected: + const NativeLocations& callback_locs_; }; } // namespace ffi diff --git a/runtime/vm/compiler/ffi/native_calling_convention.cc b/runtime/vm/compiler/ffi/native_calling_convention.cc index 17aed54f68cb..0623e9bef537 100644 --- a/runtime/vm/compiler/ffi/native_calling_convention.cc +++ b/runtime/vm/compiler/ffi/native_calling_convention.cc @@ -4,9 +4,10 @@ #include "vm/compiler/ffi/native_calling_convention.h" -#include "vm/compiler/ffi/frame_rebase.h" +#include "vm/compiler/ffi/marshaller.h" +#include "vm/compiler/ffi/native_location.h" +#include "vm/compiler/ffi/native_type.h" #include "vm/log.h" -#include "vm/stack_frame.h" #include "vm/symbols.h" namespace dart { @@ -17,167 +18,97 @@ namespace ffi { #if !defined(DART_PRECOMPILED_RUNTIME) -Representation TypeRepresentation(classid_t class_id) { - switch (class_id) { - case kFfiFloatCid: - return kUnboxedFloat; - case kFfiDoubleCid: - return kUnboxedDouble; - case kFfiInt8Cid: - case kFfiInt16Cid: - case kFfiInt32Cid: - return kUnboxedInt32; - case kFfiUint8Cid: - case kFfiUint16Cid: - case kFfiUint32Cid: - return kUnboxedUint32; - case kFfiInt64Cid: - case kFfiUint64Cid: - return kUnboxedInt64; - case kFfiIntPtrCid: - return kUnboxedIntPtr; - case kFfiPointerCid: - case kFfiVoidCid: - return kUnboxedFfiIntPtr; - default: - UNREACHABLE(); - } -} - -SmallRepresentation TypeSmallRepresentation(const AbstractType& ffi_type) { - switch (ffi_type.type_class_id()) { - case kFfiInt8Cid: - return kSmallUnboxedInt8; - case kFfiInt16Cid: - return kSmallUnboxedInt16; - case kFfiUint8Cid: - return kSmallUnboxedUint8; - case kFfiUint16Cid: - return kSmallUnboxedUint16; - default: - return kNoSmallRepresentation; - } -} +// Argument #0 is the function pointer. +const intptr_t kNativeParamsStartAt = 1; -bool NativeTypeIsVoid(const AbstractType& result_type) { - return result_type.type_class_id() == kFfiVoidCid; +bool RequiresSoftFpConversion(const NativeType& in) { + return CallingConventions::kAbiSoftFP && in.IsFloat(); } -bool NativeTypeIsPointer(const AbstractType& result_type) { - return result_type.type_class_id() == kFfiPointerCid; +const NativeType& ConvertToSoftFp(const NativeType& rep, Zone* zone) { + if (RequiresSoftFpConversion(rep)) { + ASSERT(rep.IsFloat()); + if (rep.SizeInBytes() == 4) { + return *new (zone) NativeFundamentalType(kInt32); + } + if (rep.SizeInBytes() == 8) { + return *new (zone) NativeFundamentalType(kInt64); + } + } + return rep; } -// Converts a Ffi [signature] to a list of Representations. -// Note that this ignores first argument (receiver) which is dynamic. -ZoneGrowableArray* ArgumentRepresentations( - const Function& signature) { - intptr_t num_arguments = signature.num_fixed_parameters() - 1; - auto result = new ZoneGrowableArray(num_arguments); +// Representations of the arguments to a C signature function. +static ZoneGrowableArray& ArgumentRepresentations( + const Function& signature, + Zone* zone) { + const intptr_t num_arguments = + signature.num_fixed_parameters() - kNativeParamsStartAt; + auto& result = *new ZoneGrowableArray(zone, num_arguments); for (intptr_t i = 0; i < num_arguments; i++) { - AbstractType& arg_type = - AbstractType::Handle(signature.ParameterTypeAt(i + 1)); - Representation rep = TypeRepresentation(arg_type.type_class_id()); - // 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) { - rep = kUnboxedInt64; - } - result->Add(rep); + AbstractType& arg_type = AbstractType::Handle( + zone, signature.ParameterTypeAt(i + kNativeParamsStartAt)); + const auto& rep = NativeType::FromAbstractType(arg_type, zone); + result.Add(&rep); } return result; } -Representation ResultRepresentation(const Function& signature) { - AbstractType& arg_type = AbstractType::Handle(signature.result_type()); - Representation rep = TypeRepresentation(arg_type.type_class_id()); - if (rep == kUnboxedFloat && CallingConventions::kAbiSoftFP) { - rep = kUnboxedInt32; - } else if (rep == kUnboxedDouble && CallingConventions::kAbiSoftFP) { - rep = kUnboxedInt64; - } - return rep; +// Representation of the result of a C signature function. +static NativeType& ResultRepresentation(const Function& signature, Zone* zone) { + AbstractType& result_type = + AbstractType::Handle(zone, signature.result_type()); + return NativeType::FromAbstractType(result_type, zone); } // 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. +// of argument locations. class ArgumentAllocator : public ValueObject { public: - Location AllocateArgument(Representation rep) { - switch (rep) { - case kUnboxedFloat: - case kUnboxedDouble: { - Location result = AllocateFpuRegister(); - if (!result.IsUnallocated()) return result; - break; + explicit ArgumentAllocator(Zone* zone) : zone_(zone) {} + + const NativeLocation& AllocateArgument(const NativeType& payload_type) { + const auto& payload_type_softfp = ConvertToSoftFp(payload_type, zone_); + if (payload_type_softfp.IsFloat()) { + if (fpu_regs_used < CallingConventions::kNumFpuArgRegs) { + return AllocateFpuRegister(payload_type); } - case kUnboxedInt64: - case kUnboxedUint32: - case kUnboxedInt32: { - Location result = rep == kUnboxedInt64 && target::kWordSize == 4 - ? AllocateAlignedRegisterPair() - : AllocateCpuRegister(); - if (!result.IsUnallocated()) return result; - break; + } else { + ASSERT(payload_type_softfp.IsInt()); + // Some calling conventions require the callee to make the lowest 32 bits + // in registers non-garbage. + const auto& container_type = + CallingConventions::kArgumentRegisterExtension == kExtendedTo4 + ? payload_type_softfp.WidenTo4Bytes(zone_) + : payload_type_softfp; + if (target::kWordSize == 4 && payload_type.SizeInBytes() == 8) { + if (CallingConventions::kArgumentRegisterAlignment == + kAlignedToWordSizeBut8AlignedTo8) { + cpu_regs_used += cpu_regs_used % 2; + } + if (cpu_regs_used + 2 <= CallingConventions::kNumArgRegs) { + return *new (zone_) NativeRegistersLocation( + payload_type, container_type, AllocateCpuRegister(), + AllocateCpuRegister()); + } + } else { + ASSERT(payload_type.SizeInBytes() <= target::kWordSize); + if (cpu_regs_used + 1 <= CallingConventions::kNumArgRegs) { + return *new (zone_) NativeRegistersLocation( + payload_type, container_type, AllocateCpuRegister()); + } } - default: - UNREACHABLE(); } - // Argument must be spilled. - if (rep == kUnboxedInt64 && target::kWordSize == 4) { - return AllocateAlignedStackSlots(rep); - } else if (rep == kUnboxedDouble) { - // By convention, we always use DoubleStackSlot for doubles, even on - // 64-bit systems. - ASSERT(!CallingConventions::kAlignArguments); - return AllocateDoubleStackSlot(); - } else { - return AllocateStackSlot(); - } + return AllocateStack(payload_type); } private: - Location AllocateStackSlot() { - return Location::StackSlot(stack_height_in_slots++, - CallingConventions::kStackPointerRegister); - } - - Location AllocateDoubleStackSlot() { - const Location result = Location::DoubleStackSlot( - stack_height_in_slots, CallingConventions::kStackPointerRegister); - stack_height_in_slots += 8 / target::kWordSize; - return result; - } - - // Allocates a pair of stack slots where the first stack slot is aligned to an - // 8-byte boundary, if necessary. - Location AllocateAlignedStackSlots(Representation rep) { - if (CallingConventions::kAlignArguments && target::kWordSize == 4) { - stack_height_in_slots += stack_height_in_slots % 2; - } - - Location result; - if (rep == kUnboxedDouble) { - result = Location::DoubleStackSlot( - stack_height_in_slots, CallingConventions::kStackPointerRegister); - stack_height_in_slots += 2; - } else { - const Location low = AllocateStackSlot(); - const Location high = AllocateStackSlot(); - result = Location::Pair(low, high); - } - return result; - } - - Location AllocateFpuRegister() { - if (fpu_regs_used == CallingConventions::kNumFpuArgRegs) { - return Location::RequiresFpuRegister(); - } + NativeLocation& AllocateFpuRegister(const NativeType& payload_type) { + ASSERT(fpu_regs_used < CallingConventions::kNumFpuArgRegs); - const Location result = Location::FpuRegisterLocation( + NativeLocation& result = *new (zone_) NativeFpuRegistersLocation( + payload_type, payload_type, CallingConventions::FpuArgumentRegisters[fpu_regs_used]); fpu_regs_used++; if (CallingConventions::kArgumentIntRegXorFpuReg) { @@ -186,13 +117,10 @@ class ArgumentAllocator : public ValueObject { return result; } - Location AllocateCpuRegister() { - if (cpu_regs_used == CallingConventions::kNumArgRegs) { - return Location::RequiresRegister(); - } + Register AllocateCpuRegister() { + ASSERT(cpu_regs_used < CallingConventions::kNumArgRegs); - const Location result = Location::RegisterLocation( - CallingConventions::ArgumentRegisters[cpu_regs_used]); + const auto result = CallingConventions::ArgumentRegisters[cpu_regs_used]; cpu_regs_used++; if (CallingConventions::kArgumentIntRegXorFpuReg) { fpu_regs_used++; @@ -200,87 +128,110 @@ class ArgumentAllocator : public ValueObject { return result; } - // Allocates a pair of registers where the first register index is even, if - // necessary. - Location AllocateAlignedRegisterPair() { - if (CallingConventions::kAlignArguments) { - cpu_regs_used += cpu_regs_used % 2; - } - if (cpu_regs_used > CallingConventions::kNumArgRegs - 2) { - return Location::Any(); - } - return Location::Pair(AllocateCpuRegister(), AllocateCpuRegister()); + const NativeLocation& AllocateStack(const NativeType& payload_type) { + align_stack(payload_type.AlignmentInBytesStack()); + const intptr_t size = payload_type.SizeInBytes(); + // If the stack arguments are not packed, the 32 lowest bits should not + // contain garbage. + const auto& container_type = + CallingConventions::kArgumentStackExtension == kExtendedTo4 + ? payload_type.WidenTo4Bytes(zone_) + : payload_type; + const auto& result = *new (zone_) NativeStackLocation( + payload_type, container_type, CallingConventions::kStackPointerRegister, + stack_height_in_bytes); + stack_height_in_bytes += size; + return result; + } + + void align_stack(intptr_t alignment) { + stack_height_in_bytes = Utils::RoundUp(stack_height_in_bytes, alignment); } intptr_t cpu_regs_used = 0; intptr_t fpu_regs_used = 0; - intptr_t stack_height_in_slots = 0; + intptr_t stack_height_in_bytes = 0; + Zone* zone_; }; -// Takes a list of argument representations, and converts it to a list of -// argument locations based on calling convention. - -ZoneGrowableArray* ArgumentLocations( - const ZoneGrowableArray& arg_reps) { +// Location for the arguments of a C signature function. +static NativeLocations& ArgumentLocations( + const ZoneGrowableArray& arg_reps, + Zone* zone) { intptr_t num_arguments = arg_reps.length(); - auto result = new ZoneGrowableArray(num_arguments); + auto& result = *new NativeLocations(zone, num_arguments); // Loop through all arguments and assign a register or a stack location. - ArgumentAllocator frame_state; + ArgumentAllocator frame_state(zone); for (intptr_t i = 0; i < num_arguments; i++) { - Representation rep = arg_reps[i]; - result->Add(frame_state.AllocateArgument(rep)); + const NativeType& rep = *arg_reps[i]; + result.Add(&frame_state.AllocateArgument(rep)); } return result; } -Location ResultLocation(Representation result_rep) { - switch (result_rep) { - case kUnboxedFloat: - case kUnboxedDouble: -#if defined(TARGET_ARCH_IA32) - // The result is returned in ST0, but we don't allocate ST registers, so - // the FFI trampoline will move it to XMM0. - return Location::FpuRegisterLocation(XMM0); -#else - return Location::FpuRegisterLocation(CallingConventions::kReturnFpuReg); -#endif - case kUnboxedInt32: - case kUnboxedUint32: - return Location::RegisterLocation(CallingConventions::kReturnReg); - case kUnboxedInt64: - if (target::kWordSize == 4) { - return Location::Pair( - Location::RegisterLocation(CallingConventions::kReturnReg), - Location::RegisterLocation(CallingConventions::kSecondReturnReg)); - } else { - return Location::RegisterLocation(CallingConventions::kReturnReg); - } - default: - UNREACHABLE(); +// Location for the result of a C signature function. +static NativeLocation& ResultLocation(const NativeType& payload_type, + Zone* zone) { + const auto& payload_type_softfp = ConvertToSoftFp(payload_type, zone); + const auto& container_type = + CallingConventions::kArgumentRegisterExtension == kExtendedTo4 + ? payload_type_softfp.WidenTo4Bytes(zone) + : payload_type_softfp; + if (container_type.IsFloat()) { + return *new (zone) NativeFpuRegistersLocation( + payload_type, container_type, CallingConventions::kReturnFpuReg); + } + + ASSERT(container_type.IsInt() || container_type.IsVoid()); + if (container_type.SizeInBytes() == 8 && target::kWordSize == 4) { + return *new (zone) NativeRegistersLocation( + payload_type, container_type, CallingConventions::kReturnReg, + CallingConventions::kSecondReturnReg); + } + + ASSERT(container_type.SizeInBytes() <= target::kWordSize); + return *new (zone) NativeRegistersLocation(payload_type, container_type, + CallingConventions::kReturnReg); +} + +NativeCallingConvention::NativeCallingConvention(Zone* zone, + const Function& c_signature) + : zone_(ASSERT_NOTNULL(zone)), + c_signature_(c_signature), + arg_locs_(ArgumentLocations(ArgumentRepresentations(c_signature_, zone_), + zone_)), + result_loc_( + ResultLocation(ResultRepresentation(c_signature_, zone_), zone_)) {} + +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; +} + +RawAbstractType* NativeCallingConvention::Type(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); } -// Accounts for alignment, where some stack slots are used as padding. -intptr_t NumStackSlots(const ZoneGrowableArray& locations) { - intptr_t num_arguments = locations.length(); - intptr_t max_height_in_slots = 0; +intptr_t NativeCallingConvention::StackTopInBytes() const { + intptr_t num_arguments = arg_locs_.length(); + intptr_t max_height_in_bytes = 0; for (intptr_t i = 0; i < num_arguments; i++) { - intptr_t height = 0; - if (locations.At(i).IsStackSlot()) { - height = locations.At(i).stack_index() + 1; - } else if (locations.At(i).IsDoubleStackSlot()) { - height = locations.At(i).stack_index() + 8 / target::kWordSize; - } else if (locations.At(i).IsPairLocation()) { - const Location first = locations.At(i).AsPairLocation()->At(0); - const Location second = locations.At(i).AsPairLocation()->At(1); - height = - Utils::Maximum(first.IsStackSlot() ? first.stack_index() + 1 : 0, - second.IsStackSlot() ? second.stack_index() + 1 : 0); + 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_slots = Utils::Maximum(height, max_height_in_slots); } - return max_height_in_slots; + return Utils::RoundUp(max_height_in_bytes, compiler::target::kWordSize); } #endif // !defined(DART_PRECOMPILED_RUNTIME) diff --git a/runtime/vm/compiler/ffi/native_calling_convention.h b/runtime/vm/compiler/ffi/native_calling_convention.h index 8336f1c18e77..f4b6fae073ca 100644 --- a/runtime/vm/compiler/ffi/native_calling_convention.h +++ b/runtime/vm/compiler/ffi/native_calling_convention.h @@ -8,6 +8,8 @@ #include #include "vm/compiler/backend/locations.h" +#include "vm/compiler/ffi/native_location.h" +#include "vm/compiler/ffi/native_type.h" namespace dart { @@ -15,42 +17,49 @@ namespace compiler { namespace ffi { -// Unboxed representation of an FFI type (extends 'ffi.NativeType'). -Representation TypeRepresentation(classid_t class_id); - -// Unboxed representation of an FFI type (extends 'ffi.NativeType') for 8 and 16 -// bit integers. -SmallRepresentation TypeSmallRepresentation(const AbstractType& result_type); - -// Whether a type which extends 'ffi.NativeType' also extends 'ffi.Pointer'. -bool NativeTypeIsPointer(const AbstractType& result_type); - -// Whether a type is 'ffi.Void'. -bool NativeTypeIsVoid(const AbstractType& result_type); - -// Location for the result of a C signature function. -Location ResultLocation(Representation result_rep); - -RawFunction* TrampolineFunction(const Function& dart_signature, - const Function& c_signature); - -RawFunction* NativeCallbackFunction(const Function& c_signature, - const Function& dart_target, - const Instance& exceptional_return); - -// Unboxed representations of the arguments to a C signature function. -ZoneGrowableArray* ArgumentRepresentations( - const Function& signature); - -// Unboxed representation of the result of a C signature function. -Representation ResultRepresentation(const Function& signature); - -// Location for the arguments of a C signature function. -ZoneGrowableArray* ArgumentLocations( - const ZoneGrowableArray& arg_reps); - -// Number of stack slots used in 'locations'. -intptr_t NumStackSlots(const ZoneGrowableArray& locations); +using NativeLocations = ZoneGrowableArray; + +// Whether this argument needs to be Representation-converted. +bool RequiresSoftFpConversion(const NativeType& in); + +// Converted Representation. +const NativeType& ConvertToSoftFp(const NativeType& in, Zone* zone); + +// 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. +class NativeCallingConvention : public ZoneAllocated { + public: + NativeCallingConvention(Zone* zone, const Function& c_signature); + + // Excluding the #0 argument which is the function pointer. + intptr_t num_args() const; + + // Excluding the #0 argument which is the function pointer. + RawAbstractType* Type(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); + } + + 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_; +}; } // namespace ffi diff --git a/runtime/vm/compiler/ffi/native_location.cc b/runtime/vm/compiler/ffi/native_location.cc new file mode 100644 index 000000000000..3061bfa6b852 --- /dev/null +++ b/runtime/vm/compiler/ffi/native_location.cc @@ -0,0 +1,246 @@ +// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#if !defined(DART_PRECOMPILED_RUNTIME) + +#include "vm/compiler/ffi/native_location.h" + +#include "vm/compiler/backend/il_printer.h" + +namespace dart { + +namespace compiler { + +namespace ffi { + +bool NativeLocation::LocationCanBeExpressed(Location loc, Representation rep) { + switch (loc.kind()) { + case Location::Kind::kRegister: + case Location::Kind::kFpuRegister: + case Location::Kind::kStackSlot: + case Location::Kind::kDoubleStackSlot: + return true; + default: + break; + } + if (loc.IsPairLocation()) { + // TODO(36730): We could possibly consume a pair location as struct. + return false; + } + return false; +} + +NativeLocation& NativeLocation::FromLocation(Location loc, + Representation rep, + Zone* zone) { + // TODO(36730): We could possibly consume a pair location as struct. + ASSERT(LocationCanBeExpressed(loc, rep)); + + const NativeType& native_rep = + NativeType::FromUnboxedRepresentation(rep, zone); + + switch (loc.kind()) { + case Location::Kind::kRegister: + return *new (zone) + NativeRegistersLocation(native_rep, native_rep, loc.reg()); + case Location::Kind::kFpuRegister: + return *new (zone) + NativeFpuRegistersLocation(native_rep, native_rep, loc.fpu_reg()); + case Location::Kind::kStackSlot: + return *new (zone) + NativeStackLocation(native_rep, native_rep, loc.base_reg(), + loc.stack_index() * compiler::target::kWordSize); + case Location::Kind::kDoubleStackSlot: + return *new (zone) + NativeStackLocation(native_rep, native_rep, loc.base_reg(), + loc.stack_index() * compiler::target::kWordSize); + default: + break; + } + + UNREACHABLE(); +} + +// TODO(36730): Remove when being able to consume as struct. +NativeLocation& NativeLocation::FromPairLocation(Location pair_loc, + Representation pair_rep, + intptr_t index, + Zone* zone) { + ASSERT(pair_loc.IsPairLocation()); + ASSERT(index == 0 || index == 1); + const Representation rep = + NativeType::FromUnboxedRepresentation(pair_rep, zone) + .Split(index, zone) + .AsRepresentation(); + const Location loc = pair_loc.AsPairLocation()->At(index); + return FromLocation(loc, rep, zone); +} + +const NativeRegistersLocation& NativeLocation::AsRegisters() const { + ASSERT(IsRegisters()); + return static_cast(*this); +} + +const NativeFpuRegistersLocation& NativeLocation::AsFpuRegisters() const { + ASSERT(IsFpuRegisters()); + return static_cast(*this); +} + +const NativeStackLocation& NativeLocation::AsStack() const { + ASSERT(IsStack()); + return static_cast(*this); +} + +Location NativeRegistersLocation::AsLocation() const { + ASSERT(IsExpressibleAsLocation()); + switch (num_regs()) { + case 1: + return Location::RegisterLocation(regs_->At(0)); + case 2: + return Location::Pair(Location::RegisterLocation(regs_->At(0)), + Location::RegisterLocation(regs_->At(1))); + } + UNREACHABLE(); +} + +Location NativeStackLocation::AsLocation() const { + ASSERT(IsExpressibleAsLocation()); + if (payload_type().IsInt()) { + const intptr_t size = payload_type().SizeInBytes(); + const intptr_t size_slots = size / compiler::target::kWordSize; + switch (size_slots) { + case 1: + return Location::StackSlot(offset_in_words(), base_register_); + case 2: + return Location::Pair( + Location::StackSlot(offset_in_words(), base_register_), + Location::StackSlot(offset_in_words() + 1, base_register_)); + } + } else { + ASSERT(payload_type().IsFloat()); + if (payload_type().AsFundamental().representation() == kFloat) { + return Location::StackSlot(offset_in_words(), base_register_); + } else { + ASSERT(payload_type().AsFundamental().representation() == kDouble); + return Location::DoubleStackSlot(offset_in_words(), base_register_); + } + } + UNREACHABLE(); +} +NativeRegistersLocation& NativeRegistersLocation::Split(intptr_t index, + Zone* zone) const { + ASSERT(num_regs() == 2); + return *new (zone) NativeRegistersLocation( + payload_type().Split(index, zone), container_type().Split(index, zone), + reg_at(index)); +} + +NativeStackLocation& NativeStackLocation::Split(intptr_t index, + Zone* zone) const { + ASSERT(index == 0 || index == 1); + const intptr_t size = payload_type().SizeInBytes(); + + return *new (zone) NativeStackLocation( + payload_type().Split(index, zone), container_type().Split(index, zone), + base_register_, offset_in_bytes_ + size / 2 * index); +} + +NativeLocation& NativeLocation::WidenTo4Bytes(Zone* zone) const { + return WithOtherRep(payload_type().WidenTo4Bytes(zone), + container_type().WidenTo4Bytes(zone), zone); +} + +bool NativeRegistersLocation::Equals(const NativeLocation& other) const { + if (!other.IsRegisters()) { + return false; + } + const auto& other_regs = other.AsRegisters(); + if (other_regs.num_regs() != num_regs()) { + return false; + } + for (intptr_t i = 0; i < num_regs(); i++) { + if (other_regs.reg_at(i) != reg_at(i)) { + return false; + } + } + return true; +} + +bool NativeFpuRegistersLocation::Equals(const NativeLocation& other) const { + if (!other.IsFpuRegisters()) { + return false; + } + return other.AsFpuRegisters().fpu_reg_ == fpu_reg_; +} + +bool NativeStackLocation::Equals(const NativeLocation& other) const { + if (!other.IsStack()) { + return false; + } + const auto& other_stack = other.AsStack(); + if (other_stack.base_register_ != base_register_) { + return false; + } + return other_stack.offset_in_bytes_ == offset_in_bytes_; +} + +compiler::Address NativeLocationToStackSlotAddress( + const NativeStackLocation& loc) { + return compiler::Address(loc.base_register(), loc.offset_in_bytes()); +} + +static void PrintRepresentations(BufferFormatter* f, + const NativeLocation& loc) { + f->Print(" "); + loc.container_type().PrintTo(f); + if (!loc.container_type().Equals(loc.payload_type())) { + f->Print("["); + loc.payload_type().PrintTo(f); + f->Print("]"); + } +} + +void NativeLocation::PrintTo(BufferFormatter* f) const { + f->Print("I"); + PrintRepresentations(f, *this); +} + +void NativeRegistersLocation::PrintTo(BufferFormatter* f) const { + if (num_regs() == 1) { + f->Print("%s", RegisterNames::RegisterName(regs_->At(0))); + } else { + f->Print("("); + for (intptr_t i = 0; i < num_regs(); i++) { + if (i != 0) f->Print(", "); + f->Print("%s", RegisterNames::RegisterName(regs_->At(i))); + } + f->Print(")"); + } + PrintRepresentations(f, *this); +} + +void NativeFpuRegistersLocation::PrintTo(BufferFormatter* f) const { + f->Print("%s", RegisterNames::FpuRegisterName(fpu_reg_)); + PrintRepresentations(f, *this); +} + +void NativeStackLocation::PrintTo(BufferFormatter* f) const { + f->Print("S%+" Pd, offset_in_bytes_); + PrintRepresentations(f, *this); +} + +const char* NativeLocation::ToCString() const { + char buffer[1024]; + BufferFormatter bf(buffer, 1024); + PrintTo(&bf); + return Thread::Current()->zone()->MakeCopyOfString(buffer); +} + +} // namespace ffi + +} // namespace compiler + +} // namespace dart + +#endif // !defined(DART_PRECOMPILED_RUNTIME) diff --git a/runtime/vm/compiler/ffi/native_location.h b/runtime/vm/compiler/ffi/native_location.h new file mode 100644 index 000000000000..8fbe7ada8de4 --- /dev/null +++ b/runtime/vm/compiler/ffi/native_location.h @@ -0,0 +1,280 @@ +// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#ifndef RUNTIME_VM_COMPILER_FFI_NATIVE_LOCATION_H_ +#define RUNTIME_VM_COMPILER_FFI_NATIVE_LOCATION_H_ + +#include "vm/compiler/backend/locations.h" +#include "vm/compiler/ffi/native_type.h" +#include "vm/growable_array.h" +#include "vm/thread.h" + +namespace dart { + +class BufferFormatter; + +namespace compiler { + +namespace ffi { + +class NativeRegistersLocation; +class NativeFpuRegistersLocation; +class NativeStackLocation; + +// NativeLocation objects are used in the FFI to describe argument and return +// value locations in all native ABIs that the FFI supports. +// +// NativeLocations contain two NativeTypes. +// * The payload representation. +// * The container representation, 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. +// +// 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. +// * Split between multiple registers and stack. +// +// NativeLocations cannot express the following dart::Locations: +// * No PairLocations. Instead, NativeRegistersLocations can have multiple +// registers, and NativeStackLocations can have arbitrary representations. +// * No ConstantLocations. +// +// NativeLocation does not satisfy the invariant of Location: bitwise +// inequality cannot be used to determine disjointness. +class NativeLocation : public ZoneAllocated { + public: + static bool LocationCanBeExpressed(Location loc, Representation rep); + static NativeLocation& FromLocation(Location loc, + Representation rep, + Zone* zone); + static NativeLocation& FromPairLocation(Location loc, + Representation rep, + intptr_t index, + Zone* zone); + + // The representation of the data at this location. + const NativeType& payload_type() const { return payload_type_; } + + // The location container size, possibly larger than data. + // + // If the container is larger than the data, the remaining bits are _not_ + // undefined. For example a uint8 inside a uint32 has the upper 24 bits set + // to 0. Effectively allowing the value to be read as uint8, uint16 and + // uint32. + const NativeType& container_type() const { return container_type_; } + + virtual NativeLocation& WithOtherRep(const NativeType& new_payload_type, + const NativeType& new_container_type, + Zone* zone) const = 0; + + NativeLocation& WidenTo4Bytes(Zone* zone) const; + + virtual bool IsRegisters() const { return false; } + virtual bool IsFpuRegisters() const { return false; } + virtual bool IsStack() const { return false; } + + virtual bool IsExpressibleAsLocation() const { return false; } + virtual Location AsLocation() const { + ASSERT(IsExpressibleAsLocation()); + UNREACHABLE(); + } + + virtual void PrintTo(BufferFormatter* f) const; + const char* ToCString() const; + + const NativeRegistersLocation& AsRegisters() const; + const NativeFpuRegistersLocation& AsFpuRegisters() const; + const NativeStackLocation& AsStack() const; + + virtual NativeLocation& Split(intptr_t index, Zone* zone) const { + ASSERT(index == 0 || index == 1); + UNREACHABLE(); + } + + // Equality of location, ignores the payload and container representations. + virtual bool Equals(const NativeLocation& other) const { UNREACHABLE(); } + + virtual ~NativeLocation() {} + + protected: + NativeLocation(const NativeType& payload_type, + const NativeType& container_type) + : payload_type_(payload_type), container_type_(container_type) {} + + private: + const NativeType& payload_type_; + // The location container size, possibly larger than data. + // + // If the container is larger than the data, the remaining bits are _not_ + // undefined. For example a uint8 inside a uint32 has the upper 24 bits set + // to 0. Effectively allowing the value to be read as uint8, uint16 and + // uint32. + const NativeType& container_type_; +}; + +class NativeRegistersLocation : public NativeLocation { + public: + NativeRegistersLocation(const NativeType& payload_type, + const NativeType& container_type, + ZoneGrowableArray* registers) + : NativeLocation(payload_type, container_type), regs_(registers) {} + NativeRegistersLocation(const NativeType& payload_type, + const NativeType& container_type, + Register reg) + : NativeLocation(payload_type, container_type) { + regs_ = new ZoneGrowableArray(); + regs_->Add(reg); + } + NativeRegistersLocation(const NativeType& payload_type, + const NativeType& container_type, + Register register1, + Register register2) + : NativeLocation(payload_type, container_type) { + regs_ = new ZoneGrowableArray(); + regs_->Add(register1); + regs_->Add(register2); + } + virtual ~NativeRegistersLocation() {} + + virtual NativeRegistersLocation& WithOtherRep( + const NativeType& new_payload_type, + const NativeType& new_container_type, + Zone* zone) const { + return *new (zone) + NativeRegistersLocation(new_payload_type, new_container_type, regs_); + } + + virtual bool IsRegisters() const { return true; } + virtual bool IsExpressibleAsLocation() const { + return num_regs() == 1 || num_regs() == 2; + } + virtual Location AsLocation() const; + intptr_t num_regs() const { return regs_->length(); } + Register reg_at(intptr_t index) const { return regs_->At(index); } + + virtual NativeRegistersLocation& Split(intptr_t index, Zone* zone) const; + + virtual void PrintTo(BufferFormatter* f) const; + + virtual bool Equals(const NativeLocation& other) const; + + private: + ZoneGrowableArray* regs_; + + DISALLOW_COPY_AND_ASSIGN(NativeRegistersLocation); +}; + +class NativeFpuRegistersLocation : public NativeLocation { + public: + NativeFpuRegistersLocation(const NativeType& payload_type, + const NativeType& container_type, + FpuRegister fpu_register) + : NativeLocation(payload_type, container_type), fpu_reg_(fpu_register) { + ASSERT(container_type.IsFloat()); + // Currently we do not store ints in floating point registers. + ASSERT(container_type.Equals(payload_type)); + } + virtual ~NativeFpuRegistersLocation() {} + + virtual NativeFpuRegistersLocation& WithOtherRep( + const NativeType& new_payload_type, + const NativeType& new_container_type, + Zone* zone) const { + return *new (zone) NativeFpuRegistersLocation(new_payload_type, + new_container_type, fpu_reg_); + } + + virtual bool IsFpuRegisters() const { return true; } + virtual bool IsExpressibleAsLocation() const { return true; } + virtual Location AsLocation() const { + ASSERT(IsExpressibleAsLocation()); + return Location::FpuRegisterLocation(fpu_reg_); + } + FpuRegister fpu_reg() const { return fpu_reg_; } + + virtual void PrintTo(BufferFormatter* f) const; + + virtual bool Equals(const NativeLocation& other) const; + + private: + // TODO(36309): Support having multiple fpu registers. + FpuRegister fpu_reg_; + + DISALLOW_COPY_AND_ASSIGN(NativeFpuRegistersLocation); +}; + +class NativeStackLocation : public NativeLocation { + public: + NativeStackLocation(const NativeType& payload_type, + const NativeType& container_type, + Register base_register, + intptr_t offset_in_bytes) + : NativeLocation(payload_type, container_type), + base_register_(base_register), + offset_in_bytes_(offset_in_bytes) {} + virtual ~NativeStackLocation() {} + + virtual NativeStackLocation& WithOtherRep( + const NativeType& new_payload_type, + const NativeType& new_container_type, + Zone* zone) const { + return *new (zone) NativeStackLocation(new_payload_type, new_container_type, + base_register_, offset_in_bytes_); + } + + virtual bool IsStack() const { return true; } + virtual bool IsExpressibleAsLocation() const { + const intptr_t size = payload_type().SizeInBytes(); + const intptr_t size_slots = size / compiler::target::kWordSize; + return offset_in_bytes_ % compiler::target::kWordSize == 0 && + size % compiler::target::kWordSize == 0 && + (size_slots == 1 || size_slots == 2); + } + virtual Location AsLocation() const; + + // ConstantInstr expects DoubleStackSlot for doubles, even on 64-bit systems. + // + // So this return a wrong-sized Location on purpose. + Location AsDoubleStackSlotLocation() const { + ASSERT(compiler::target::kWordSize == 8); + return Location::DoubleStackSlot(offset_in_words(), base_register_); + } + + virtual NativeStackLocation& Split(intptr_t index, Zone* zone) const; + + virtual void PrintTo(BufferFormatter* f) const; + + virtual bool Equals(const NativeLocation& other) const; + + Register base_register() const { return base_register_; } + intptr_t offset_in_bytes() const { return offset_in_bytes_; } + + private: + intptr_t offset_in_words() const { + ASSERT(offset_in_bytes_ % compiler::target::kWordSize == 0); + return offset_in_bytes_ / compiler::target::kWordSize; + } + + Register base_register_; + intptr_t offset_in_bytes_; + + DISALLOW_COPY_AND_ASSIGN(NativeStackLocation); +}; + +// Return a memory operand for stack slot locations. +compiler::Address NativeLocationToStackSlotAddress( + const NativeStackLocation& loc); + +} // namespace ffi + +} // namespace compiler + +} // namespace dart + +#endif // RUNTIME_VM_COMPILER_FFI_NATIVE_LOCATION_H_ diff --git a/runtime/vm/compiler/ffi/native_representation.cc b/runtime/vm/compiler/ffi/native_representation.cc deleted file mode 100644 index 1ccd6065d6c5..000000000000 --- a/runtime/vm/compiler/ffi/native_representation.cc +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file -// for details. All rights reserved. Use of this source code is governed by a -// BSD-style license that can be found in the LICENSE file. - -#include "vm/compiler/ffi/native_representation.h" - -#include "platform/assert.h" -#include "platform/globals.h" -#include "vm/compiler/backend/locations.h" -#include "vm/compiler/runtime_api.h" -#include "vm/object.h" - -namespace dart { - -namespace compiler { - -namespace ffi { - -static const size_t kSizeUnknown = 0; - -static const intptr_t kNumElementSizes = kFfiVoidCid - kFfiPointerCid + 1; - -static const size_t element_size_table[kNumElementSizes] = { - target::kWordSize, // kFfiPointerCid - kSizeUnknown, // kFfiNativeFunctionCid - 1, // kFfiInt8Cid - 2, // kFfiInt16Cid - 4, // kFfiInt32Cid - 8, // kFfiInt64Cid - 1, // kFfiUint8Cid - 2, // kFfiUint16Cid - 4, // kFfiUint32Cid - 8, // kFfiUint64Cid - target::kWordSize, // kFfiIntPtrCid - 4, // kFfiFloatCid - 8, // kFfiDoubleCid - kSizeUnknown, // kFfiVoidCid -}; - -size_t ElementSizeInBytes(intptr_t class_id) { - ASSERT(class_id != kFfiNativeFunctionCid); - ASSERT(class_id != kFfiVoidCid); - if (!RawObject::IsFfiTypeClassId(class_id)) { - // subtype of Pointer - class_id = kFfiPointerCid; - } - intptr_t index = class_id - kFfiPointerCid; - return element_size_table[index]; -} - -} // namespace ffi - -} // namespace compiler - -} // namespace dart diff --git a/runtime/vm/compiler/ffi/native_representation.h b/runtime/vm/compiler/ffi/native_representation.h deleted file mode 100644 index 48bcd1a0c708..000000000000 --- a/runtime/vm/compiler/ffi/native_representation.h +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file -// for details. All rights reserved. Use of this source code is governed by a -// BSD-style license that can be found in the LICENSE file. - -#ifndef RUNTIME_VM_COMPILER_FFI_NATIVE_REPRESENTATION_H_ -#define RUNTIME_VM_COMPILER_FFI_NATIVE_REPRESENTATION_H_ - -#include - -#include "platform/assert.h" -#include "vm/allocation.h" -#include "vm/compiler/backend/locations.h" -#include "vm/compiler/runtime_api.h" - -namespace dart { - -namespace compiler { - -namespace ffi { - -// Storage size for an FFI type (extends 'ffi.NativeType'). -size_t ElementSizeInBytes(intptr_t class_id); - -} // namespace ffi - -} // namespace compiler - -} // namespace dart - -#endif // RUNTIME_VM_COMPILER_FFI_NATIVE_REPRESENTATION_H_ diff --git a/runtime/vm/compiler/ffi/native_type.cc b/runtime/vm/compiler/ffi/native_type.cc new file mode 100644 index 000000000000..e69380c3617c --- /dev/null +++ b/runtime/vm/compiler/ffi/native_type.cc @@ -0,0 +1,329 @@ +// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include "vm/compiler/ffi/native_type.h" + +#include "platform/assert.h" +#include "platform/globals.h" +#include "vm/compiler/backend/locations.h" +#include "vm/compiler/runtime_api.h" +#include "vm/object.h" + +namespace dart { + +namespace compiler { + +namespace ffi { + +const NativeFundamentalType& NativeType::AsFundamental() const { + ASSERT(IsFundamental()); + return static_cast(*this); +} + +bool NativeFundamentalType::IsInt() const { + switch (representation_) { + case kInt8: + case kUint8: + case kInt16: + case kUint16: + case kInt32: + case kUint32: + case kInt64: + case kUint64: + return true; + default: + return false; + } +} + +bool NativeFundamentalType::IsFloat() const { + return representation_ == kFloat || representation_ == kDouble || + representation_ == kHalfDouble; +} + +bool NativeFundamentalType::IsVoid() const { + return representation_ == kVoid; +} + +bool NativeFundamentalType::IsSigned() const { + ASSERT(IsInt() || IsFloat()); + switch (representation_) { + case kInt8: + case kInt16: + case kInt32: + case kInt64: + case kFloat: + case kDouble: + case kHalfDouble: + return true; + case kUint8: + case kUint16: + case kUint32: + case kUint64: + default: + return false; + } +} + +static const intptr_t fundamental_size_in_bytes[kVoid + 1] = { + 1, // kInt8, + 1, // kUint8, + 2, // kInt16, + 2, // kUint16, + 4, // kInt32, + 4, // kUint32, + 8, // kInt64, + 8, // kUint64, + 4, // kFloat, + 8, // kDouble, + 4, // kHalfDouble + 0, // kVoid, +}; + +intptr_t NativeFundamentalType::SizeInBytes() const { + return fundamental_size_in_bytes[representation_]; +} + +intptr_t NativeFundamentalType::AlignmentInBytesStack() const { + switch (CallingConventions::kArgumentStackAlignment) { + case kAlignedToWordSize: + // The default is to align stack arguments to word size. + return compiler::target::kWordSize; + case kAlignedToWordSizeBut8AlignedTo8: { + // However, arm32 deviates slightly. + if (SizeInBytes() == 8) { + return 8; + } + return compiler::target::kWordSize; + } + case kAlignedToValueSize: + // iOS on arm64 only aligns to size. + return SizeInBytes(); + } + UNREACHABLE(); +} + +intptr_t NativeFundamentalType::AlignmentInBytesField() const { + switch (CallingConventions::kFieldAlignment) { + case kAlignedToValueSize: + // The default is to align fields to their own size. + return SizeInBytes(); + case kAlignedToValueSizeBut8AlignedTo4: { + // However, on some 32-bit architectures, 8-byte fields are only aligned + // to 4 bytes. + if (SizeInBytes() == 8) { + return 4; + } + return SizeInBytes(); + } + } + UNREACHABLE(); +} + +bool NativeFundamentalType::IsExpressibleAsRepresentation() const { + switch (representation_) { + case kInt8: + case kUint8: + case kInt16: + case kUint16: + case kHalfDouble: + return false; + case kInt32: + case kUint32: + case kInt64: + case kUint64: + case kFloat: + case kDouble: + return true; + case kVoid: + return true; + default: + UNREACHABLE(); // Make MSVC happy. + } +} + +Representation NativeFundamentalType::AsRepresentation() const { + ASSERT(IsExpressibleAsRepresentation()); + switch (representation_) { + case kInt32: + return kUnboxedInt32; + case kUint32: + return kUnboxedUint32; + case kInt64: + case kUint64: + return kUnboxedInt64; + case kFloat: + return kUnboxedFloat; + case kDouble: + return kUnboxedDouble; + case kVoid: + return kUnboxedFfiIntPtr; + default: + UNREACHABLE(); + } +} + +bool NativeFundamentalType::Equals(const NativeType& other) const { + if (!other.IsFundamental()) { + return false; + } + return other.AsFundamental().representation_ == representation_; +} + +static FundamentalType split_fundamental(FundamentalType in) { + switch (in) { + case kInt16: + return kInt8; + case kInt32: + return kInt16; + case kInt64: + return kInt32; + case kUint16: + return kUint8; + case kUint32: + return kUint16; + case kUint64: + return kUint32; + case kDouble: + return kHalfDouble; + default: + UNREACHABLE(); + } +} + +NativeFundamentalType& NativeFundamentalType::Split(intptr_t index, + Zone* zone) const { + ASSERT(index == 0 || index == 1); + auto new_rep = split_fundamental(representation()); + return *new (zone) NativeFundamentalType(new_rep); +} + +static FundamentalType TypeRepresentation(classid_t class_id) { + switch (class_id) { + case kFfiInt8Cid: + return kInt8; + case kFfiInt16Cid: + return kInt16; + case kFfiInt32Cid: + return kInt32; + case kFfiUint8Cid: + return kUint8; + case kFfiUint16Cid: + return kUint16; + case kFfiUint32Cid: + return kUint32; + case kFfiInt64Cid: + case kFfiUint64Cid: + return kInt64; + case kFfiIntPtrCid: + return compiler::target::kWordSize == 4 ? kInt32 : kInt64; + case kFfiFloatCid: + return kFloat; + case kFfiDoubleCid: + return kDouble; + case kFfiPointerCid: + return compiler::target::kWordSize == 4 ? kUint32 : kInt64; + case kFfiVoidCid: + return kVoid; + default: + UNREACHABLE(); + } +} + +NativeType& NativeType::FromTypedDataClassId(classid_t class_id, Zone* zone) { + // TODO(36730): Support composites. + const auto fundamental_rep = TypeRepresentation(class_id); + return *new (zone) NativeFundamentalType(fundamental_rep); +} + +NativeType& NativeType::FromAbstractType(const AbstractType& type, Zone* zone) { + // TODO(36730): Support composites. + return NativeType::FromTypedDataClassId(type.type_class_id(), zone); +} + +static FundamentalType fundamental_rep(Representation rep) { + switch (rep) { + case kUnboxedDouble: + return kDouble; + case kUnboxedFloat: + return kFloat; + case kUnboxedInt32: + return kInt32; + case kUnboxedUint32: + return kUint32; + case kUnboxedInt64: + return kInt64; + default: + break; + } + UNREACHABLE(); +} + +NativeFundamentalType& NativeType::FromUnboxedRepresentation(Representation rep, + Zone* zone) { + return *new (zone) NativeFundamentalType(fundamental_rep(rep)); +} + +const char* NativeType::ToCString() const { + char buffer[1024]; + BufferFormatter bf(buffer, 1024); + PrintTo(&bf); + return Thread::Current()->zone()->MakeCopyOfString(buffer); +} + +static const char* FundamentalTypeToCString(FundamentalType rep) { + switch (rep) { + case kInt8: + return "int8"; + case kUint8: + return "uint8"; + case kInt16: + return "int16"; + case kUint16: + return "uint16"; + case kInt32: + return "int32"; + case kUint32: + return "uint32"; + case kInt64: + return "int64"; + case kUint64: + return "uint64"; + case kFloat: + return "float"; + case kDouble: + return "double"; + case kHalfDouble: + return "half-double"; + case kVoid: + return "void"; + default: + UNREACHABLE(); + } +} + +void NativeType::PrintTo(BufferFormatter* f) const { + f->Print("I"); +} + +void NativeFundamentalType::PrintTo(BufferFormatter* f) const { + f->Print("%s", FundamentalTypeToCString(representation_)); +} + +const NativeType& NativeType::WidenTo4Bytes(Zone* zone) const { + if (IsInt() && SizeInBytes() <= 2) { + if (IsSigned()) { + return *new (zone) NativeFundamentalType(kInt32); + } else { + return *new (zone) NativeFundamentalType(kUint32); + } + } + return *this; +} + +} // namespace ffi + +} // namespace compiler + +} // namespace dart diff --git a/runtime/vm/compiler/ffi/native_type.h b/runtime/vm/compiler/ffi/native_type.h new file mode 100644 index 000000000000..ddd694182dbb --- /dev/null +++ b/runtime/vm/compiler/ffi/native_type.h @@ -0,0 +1,158 @@ +// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#ifndef RUNTIME_VM_COMPILER_FFI_NATIVE_TYPE_H_ +#define RUNTIME_VM_COMPILER_FFI_NATIVE_TYPE_H_ + +#include + +#include "platform/assert.h" +#include "vm/allocation.h" +#include "vm/compiler/backend/locations.h" +#include "vm/compiler/runtime_api.h" + +namespace dart { + +namespace compiler { + +namespace ffi { + +class NativeFundamentalType; + +// NativeTypes are the types used in calling convention specifications: +// integers, floats, and composites. +// +// NativeTypes exclude C types which are not discussed in calling conventions: +// pointer types (they are lowered to integers). +// +// The NativeTypes are a partially overlapping with unboxed Representations. +// NativeTypes do not have Dart representations such as the following: +// * tagged +// * untagged +// +// Instead, NativeTypes support representations not supprted in Dart's unboxed +// Representations, such as: +// * Fundamental types (https://en.cppreference.com/w/cpp/language/types): +// * int8_t +// * int16_t +// * uint8_t +// * uint16t +// * void +// * Compound types (https://en.cppreference.com/w/cpp/language/type): +// * Struct +// * Union +// +// TODO(36730): Add composites. +class NativeType : public ZoneAllocated { + public: + static NativeType& FromAbstractType(const AbstractType& type, Zone* zone); + static NativeType& FromTypedDataClassId(classid_t class_id, Zone* zone); + static NativeFundamentalType& FromUnboxedRepresentation(Representation rep, + Zone* zone); + + virtual bool IsFundamental() const { return false; } + const NativeFundamentalType& AsFundamental() const; + + virtual bool IsInt() const { return false; } + virtual bool IsFloat() const { return false; } + virtual bool IsVoid() const { return false; } + + virtual bool IsSigned() const = 0; + + // The size in bytes of this representation. + // + // Does not take into account padding required if repeating. + virtual intptr_t SizeInBytes() const = 0; + + // The alignment in bytes of this represntation on the stack. + virtual intptr_t AlignmentInBytesStack() const = 0; + + // The alignment in bytes of this representation as member of a composite. + virtual intptr_t AlignmentInBytesField() const = 0; + + // NativeTypes which are available as unboxed Representations. + virtual bool IsExpressibleAsRepresentation() const { return false; } + + // Unboxed Representation if it exists. + virtual Representation AsRepresentation() const = 0; + + // Unboxed Representation, over approximates if needed. + Representation AsRepresentationOverApprox(Zone* zone_) const { + const auto& widened = WidenTo4Bytes(zone_); + return widened.AsRepresentation(); + } + + virtual bool Equals(const NativeType& other) const { UNREACHABLE(); } + + // Split representation in two. + virtual NativeType& Split(intptr_t index, Zone* zone) const { UNREACHABLE(); } + + // If this is a 8 or 16 bit int, returns a 32 bit container. + // Otherwise, return original representation. + const NativeType& WidenTo4Bytes(Zone* zone) const; + + virtual void PrintTo(BufferFormatter* f) const; + const char* ToCString() const; + + virtual ~NativeType() {} + + protected: + NativeType() {} +}; + +enum FundamentalType { + kInt8, + kUint8, + kInt16, + kUint16, + kInt32, + kUint32, + kInt64, + kUint64, + kFloat, + kDouble, + kHalfDouble, // When doubles are split over two 32 bit locations. + kVoid, + // TODO(37470): Add packed data structures. +}; + +class NativeFundamentalType : public NativeType { + public: + explicit NativeFundamentalType(FundamentalType rep) : representation_(rep) {} + + FundamentalType representation() const { return representation_; } + + virtual bool IsFundamental() const { return true; } + + virtual bool IsInt() const; + virtual bool IsFloat() const; + virtual bool IsVoid() const; + + virtual bool IsSigned() const; + + virtual intptr_t SizeInBytes() const; + virtual intptr_t AlignmentInBytesStack() const; + virtual intptr_t AlignmentInBytesField() const; + + virtual bool IsExpressibleAsRepresentation() const; + virtual Representation AsRepresentation() const; + + virtual bool Equals(const NativeType& other) const; + virtual NativeFundamentalType& Split(intptr_t part, Zone* zone) const; + + virtual void PrintTo(BufferFormatter* f) const; + + virtual ~NativeFundamentalType() {} + + private: + const FundamentalType representation_; +}; + +} // namespace ffi + +} // namespace compiler + +} // namespace dart + +#endif // RUNTIME_VM_COMPILER_FFI_NATIVE_TYPE_H_ diff --git a/runtime/vm/compiler/frontend/bytecode_flow_graph_builder.cc b/runtime/vm/compiler/frontend/bytecode_flow_graph_builder.cc index 58641e6fcb3c..cfda6cea12a7 100644 --- a/runtime/vm/compiler/frontend/bytecode_flow_graph_builder.cc +++ b/runtime/vm/compiler/frontend/bytecode_flow_graph_builder.cc @@ -1928,7 +1928,7 @@ void BytecodeFlowGraphBuilder::BuildFfiNativeCallbackFunction() { TypeArguments::Cast(B->Peek(/*depth=*/2)->AsConstant()->value()); ASSERT(type_args.IsInstantiated() && type_args.Length() == 1); const Function& native_sig = Function::Handle( - Z, Type::Cast(AbstractType::Handle(Z, type_args.TypeAt(0))).signature()); + Z, Type::CheckedHandle(Z, type_args.TypeAt(0)).signature()); const Closure& target_closure = Closure::Cast(B->Peek(/*depth=*/1)->AsConstant()->value()); diff --git a/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc b/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc index e8d6d12de89d..fd826d905254 100644 --- a/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc +++ b/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc @@ -5168,8 +5168,7 @@ Fragment StreamingFlowGraphBuilder::BuildFfiNativeCallbackFunction() { T.BuildTypeArguments(list_length); // read types. ASSERT(type_arguments.Length() == 1 && type_arguments.IsInstantiated()); const Function& native_sig = Function::Handle( - Z, Type::Cast(AbstractType::Handle(Z, type_arguments.TypeAt(0))) - .signature()); + Z, Type::CheckedHandle(Z, type_arguments.TypeAt(0)).signature()); Fragment code; const intptr_t positional_count = diff --git a/runtime/vm/compiler/frontend/kernel_to_il.cc b/runtime/vm/compiler/frontend/kernel_to_il.cc index 2a4574dd76a1..03a185c7a534 100644 --- a/runtime/vm/compiler/frontend/kernel_to_il.cc +++ b/runtime/vm/compiler/frontend/kernel_to_il.cc @@ -11,7 +11,6 @@ #include "vm/compiler/backend/locations.h" #include "vm/compiler/ffi/abi.h" #include "vm/compiler/ffi/marshaller.h" -#include "vm/compiler/ffi/native_representation.h" #include "vm/compiler/ffi/recognized_method.h" #include "vm/compiler/frontend/kernel_binary_flowgraph.h" #include "vm/compiler/frontend/kernel_translation_helper.h" @@ -372,13 +371,11 @@ Fragment FlowGraphBuilder::InstanceCall( } Fragment FlowGraphBuilder::FfiCall( - const Function& signature, - const ZoneGrowableArray& arg_reps, - const ZoneGrowableArray& arg_locs) { + const compiler::ffi::CallMarshaller& marshaller) { Fragment body; FfiCallInstr* const call = - new (Z) FfiCallInstr(Z, GetNextDeoptId(), signature, arg_reps, arg_locs); + new (Z) FfiCallInstr(Z, GetNextDeoptId(), marshaller); for (intptr_t i = call->InputCount() - 1; i >= 0; --i) { call->SetInputAt(i, Pop()); @@ -1211,8 +1208,8 @@ FlowGraph* FlowGraphBuilder::BuildGraphOfRecognizedMethod( compiler::ffi::RecognizedMethodTypeArgCid(kind); const classid_t typed_data_cid = compiler::ffi::ElementTypedDataCid(ffi_type_arg_cid); - const Representation representation = - compiler::ffi::TypeRepresentation(ffi_type_arg_cid); + const auto& native_rep = compiler::ffi::NativeType::FromTypedDataClassId( + ffi_type_arg_cid, zone_); // Check Dart signature type. const auto& receiver_type = @@ -1233,7 +1230,7 @@ FlowGraph* FlowGraphBuilder::BuildGraphOfRecognizedMethod( body += CheckNullOptimized(TokenPosition::kNoSource, String::ZoneHandle(Z, function.name())); body += UnboxTruncate(kUnboxedFfiIntPtr); - body += IntConstant(compiler::ffi::ElementSizeInBytes(ffi_type_arg_cid)); + body += IntConstant(native_rep.SizeInBytes()); body += UnboxTruncate(kUnboxedIntPtr); // TODO(38831): Implement Shift for Uint32, and use that instead. body += @@ -1250,7 +1247,7 @@ FlowGraph* FlowGraphBuilder::BuildGraphOfRecognizedMethod( } body += Box(kUnboxedDouble); } else { - body += Box(representation); + body += Box(native_rep.AsRepresentationOverApprox(zone_)); if (kind == MethodRecognizer::kFfiLoadPointer) { const auto class_table = thread_->isolate()->class_table(); ASSERT(class_table->HasValidClassAt(kFfiPointerCid)); @@ -1301,8 +1298,8 @@ FlowGraph* FlowGraphBuilder::BuildGraphOfRecognizedMethod( compiler::ffi::RecognizedMethodTypeArgCid(kind); const classid_t typed_data_cid = compiler::ffi::ElementTypedDataCid(ffi_type_arg_cid); - const Representation representation = - compiler::ffi::TypeRepresentation(ffi_type_arg_cid); + const auto& native_rep = compiler::ffi::NativeType::FromTypedDataClassId( + ffi_type_arg_cid, zone_); // Check Dart signature type. const auto& receiver_type = @@ -1365,7 +1362,7 @@ FlowGraph* FlowGraphBuilder::BuildGraphOfRecognizedMethod( body += CheckNullOptimized(TokenPosition::kNoSource, String::ZoneHandle(Z, function.name())); body += UnboxTruncate(kUnboxedFfiIntPtr); - body += IntConstant(compiler::ffi::ElementSizeInBytes(ffi_type_arg_cid)); + body += IntConstant(native_rep.SizeInBytes()); body += UnboxTruncate(kUnboxedFfiIntPtr); // TODO(38831): Implement Shift for Uint32, and use that instead. body += @@ -1386,7 +1383,7 @@ FlowGraph* FlowGraphBuilder::BuildGraphOfRecognizedMethod( body += DoubleToFloat(); } } else { - body += UnboxTruncate(representation); + body += UnboxTruncate(native_rep.AsRepresentationOverApprox(zone_)); } body += StoreIndexedTypedData(typed_data_cid); body += NullConstant(); @@ -2799,22 +2796,10 @@ Fragment FlowGraphBuilder::Box(Representation from) { return Fragment(box); } -Fragment FlowGraphBuilder::FfiUnboxedExtend(Representation representation, - const AbstractType& ffi_type) { - const SmallRepresentation from_representation = - compiler::ffi::TypeSmallRepresentation(ffi_type); - if (from_representation == kNoSmallRepresentation) return {}; - - auto* extend = new (Z) - UnboxedWidthExtenderInstr(Pop(), representation, from_representation); - Push(extend); - return Fragment(extend); -} - -Fragment FlowGraphBuilder::NativeReturn(Representation result) { - auto* instr = new (Z) - NativeReturnInstr(TokenPosition::kNoSource, Pop(), result, - compiler::ffi::ResultLocation(result), DeoptId::kNone); +Fragment FlowGraphBuilder::NativeReturn( + const compiler::ffi::CallbackMarshaller& marshaller) { + auto* instr = new (Z) NativeReturnInstr(TokenPosition::kNoSource, Pop(), + marshaller, DeoptId::kNone); return Fragment(instr); } @@ -2858,54 +2843,48 @@ Fragment FlowGraphBuilder::BitCast(Representation from, Representation to) { } Fragment FlowGraphBuilder::FfiConvertArgumentToDart( - const AbstractType& ffi_type, - const Representation native_representation) { + const compiler::ffi::BaseMarshaller& marshaller, + intptr_t arg_index) { Fragment body; - if (compiler::ffi::NativeTypeIsPointer(ffi_type)) { + if (marshaller.IsPointer(arg_index)) { body += Box(kUnboxedFfiIntPtr); - body += FfiPointerFromAddress(Type::Cast(ffi_type)); - } else if (compiler::ffi::NativeTypeIsVoid(ffi_type)) { + body += FfiPointerFromAddress( + Type::CheckedHandle(Z, marshaller.Type(arg_index))); + } else if (marshaller.IsVoid(arg_index)) { body += Drop(); body += NullConstant(); } else { - const Representation from_rep = native_representation; - const Representation to_rep = - compiler::ffi::TypeRepresentation(ffi_type.type_class_id()); - if (from_rep != to_rep) { - body += BitCast(from_rep, to_rep); - } else { - body += FfiUnboxedExtend(from_rep, ffi_type); + if (marshaller.RequiresBitCast(arg_index)) { + body += BitCast(marshaller.RepInFfiCall(arg_index), + marshaller.RepInDart(arg_index)); } - body += Box(to_rep); + + body += Box(marshaller.RepInDart(arg_index)); } return body; } Fragment FlowGraphBuilder::FfiConvertArgumentToNative( - const Function& function, - const AbstractType& ffi_type, - const Representation native_representation) { + const compiler::ffi::BaseMarshaller& marshaller, + intptr_t arg_index) { Fragment body; // Check for 'null'. + // TODO(36780): Mention the param name instead of function name and reciever. body += CheckNullOptimized(TokenPosition::kNoSource, - String::ZoneHandle(Z, function.name())); + String::ZoneHandle(Z, marshaller.function_name())); - if (compiler::ffi::NativeTypeIsPointer(ffi_type)) { + if (marshaller.IsPointer(arg_index)) { body += LoadNativeField(Slot::Pointer_c_memory_address()); - body += UnboxTruncate(kUnboxedFfiIntPtr); - } else { - Representation from_rep = - compiler::ffi::TypeRepresentation(ffi_type.type_class_id()); - body += UnboxTruncate(from_rep); + } - Representation to_rep = native_representation; - if (from_rep != to_rep) { - body += BitCast(from_rep, to_rep); - } else { - body += FfiUnboxedExtend(from_rep, ffi_type); - } + body += UnboxTruncate(marshaller.RepInDart(arg_index)); + + if (marshaller.RequiresBitCast(arg_index)) { + body += BitCast(marshaller.RepInDart(arg_index), + marshaller.RepInFfiCall(arg_index)); } + return body; } @@ -2933,19 +2912,15 @@ FlowGraph* FlowGraphBuilder::BuildGraphOfFfiNative(const Function& function) { Fragment body(instruction_cursor); body += CheckStackOverflowInPrologue(function.token_pos()); - const Function& signature = Function::ZoneHandle(Z, function.FfiCSignature()); - const auto& arg_reps = *compiler::ffi::ArgumentRepresentations(signature); - const auto& arg_locs = *compiler::ffi::ArgumentLocations(arg_reps); + const auto& marshaller = *new (Z) compiler::ffi::CallMarshaller(Z, function); BuildArgumentTypeChecks(TypeChecksToBuild::kCheckAllTypeParameterBounds, &body, &body, &body); // Unbox and push the arguments. - AbstractType& ffi_type = AbstractType::Handle(Z); - for (intptr_t pos = 1; pos < function.num_fixed_parameters(); pos++) { - body += LoadLocal(parsed_function_->ParameterVariable(pos)); - ffi_type = signature.ParameterTypeAt(pos); - body += FfiConvertArgumentToNative(function, ffi_type, arg_reps[pos - 1]); + for (intptr_t i = 0; i < marshaller.num_args(); i++) { + body += LoadLocal(parsed_function_->ParameterVariable(i + 1)); + body += FfiConvertArgumentToNative(marshaller, i); } // Push the function pointer, which is stored (boxed) in the first slot of the @@ -2957,12 +2932,10 @@ FlowGraph* FlowGraphBuilder::BuildGraphOfFfiNative(const Function& function) { Z, Class::Handle(I->object_store()->ffi_pointer_class())) ->context_variables()[0])); body += UnboxTruncate(kUnboxedFfiIntPtr); - body += FfiCall(signature, arg_reps, arg_locs); + body += FfiCall(marshaller); + + body += FfiConvertArgumentToDart(marshaller, compiler::ffi::kResultIndex); - ffi_type = signature.result_type(); - const Representation from_rep = - compiler::ffi::ResultRepresentation(signature); - body += FfiConvertArgumentToDart(ffi_type, from_rep); body += Return(TokenPosition::kNoSource); return new (Z) FlowGraph(*parsed_function_, graph_entry_, last_used_block_id_, @@ -2970,18 +2943,14 @@ FlowGraph* FlowGraphBuilder::BuildGraphOfFfiNative(const Function& function) { } FlowGraph* FlowGraphBuilder::BuildGraphOfFfiCallback(const Function& function) { - const Function& signature = Function::ZoneHandle(Z, function.FfiCSignature()); - const auto& arg_reps = *compiler::ffi::ArgumentRepresentations(signature); - const auto& arg_locs = *compiler::ffi::ArgumentLocations(arg_reps); - const auto& callback_locs = - *compiler::ffi::CallbackArgumentTranslator::TranslateArgumentLocations( - arg_locs); + const auto& marshaller = + *new (Z) compiler::ffi::CallbackMarshaller(Z, function); graph_entry_ = new (Z) GraphEntryInstr(*parsed_function_, Compiler::kNoOSRDeoptId); auto* const native_entry = new (Z) NativeEntryInstr( - &arg_locs, graph_entry_, AllocateBlockId(), CurrentTryIndex(), + marshaller, graph_entry_, AllocateBlockId(), CurrentTryIndex(), GetNextDeoptId(), function.FfiCallbackId()); graph_entry_->set_normal_entry(native_entry); @@ -2996,14 +2965,11 @@ FlowGraph* FlowGraphBuilder::BuildGraphOfFfiCallback(const Function& function) { ++try_depth_; // Box and push the arguments. - AbstractType& ffi_type = AbstractType::Handle(Z); - for (intptr_t i = 0, n = callback_locs.length(); i < n; ++i) { - ffi_type = signature.ParameterTypeAt(i + 1); - auto* parameter = - new (Z) NativeParameterInstr(callback_locs[i], arg_reps[i]); + for (intptr_t i = 0; i < marshaller.num_args(); i++) { + auto* parameter = new (Z) NativeParameterInstr(marshaller, i); Push(parameter); body <<= parameter; - body += FfiConvertArgumentToDart(ffi_type, arg_reps[i]); + body += FfiConvertArgumentToDart(marshaller, i); } // Call the target. @@ -3012,14 +2978,11 @@ FlowGraph* FlowGraphBuilder::BuildGraphOfFfiCallback(const Function& function) { // rebind-rule accordingly. body += StaticCall(TokenPosition::kNoSource, Function::ZoneHandle(Z, function.FfiCallbackTarget()), - callback_locs.length(), Array::empty_array(), + marshaller.num_args(), Array::empty_array(), ICData::kNoRebind); - ffi_type = signature.result_type(); - const Representation result_rep = - compiler::ffi::ResultRepresentation(signature); - body += FfiConvertArgumentToNative(function, ffi_type, result_rep); - body += NativeReturn(result_rep); + body += FfiConvertArgumentToNative(marshaller, compiler::ffi::kResultIndex); + body += NativeReturn(marshaller); --try_depth_; function_body += body; @@ -3033,18 +2996,19 @@ FlowGraph* FlowGraphBuilder::BuildGraphOfFfiCallback(const Function& function) { // // For pointer and void return types, the exceptional return is always null -- // return 0 instead. - if (compiler::ffi::NativeTypeIsPointer(ffi_type) || - compiler::ffi::NativeTypeIsVoid(ffi_type)) { + if (marshaller.IsPointer(compiler::ffi::kResultIndex) || + marshaller.IsVoid(compiler::ffi::kResultIndex)) { ASSERT(function.FfiCallbackExceptionalReturn() == Object::null()); catch_body += IntConstant(0); catch_body += UnboxTruncate(kUnboxedFfiIntPtr); } else { catch_body += Constant( Instance::ZoneHandle(Z, function.FfiCallbackExceptionalReturn())); - catch_body += FfiConvertArgumentToNative(function, ffi_type, result_rep); + catch_body += + FfiConvertArgumentToNative(marshaller, compiler::ffi::kResultIndex); } - catch_body += NativeReturn(result_rep); + catch_body += NativeReturn(marshaller); --catch_depth_; PrologueInfo prologue_info(-1, -1); @@ -3059,6 +3023,7 @@ void FlowGraphBuilder::SetCurrentTryCatchBlock(TryCatchBlock* try_catch_block) { } } // namespace kernel + } // namespace dart #endif // !defined(DART_PRECOMPILED_RUNTIME) diff --git a/runtime/vm/compiler/frontend/kernel_to_il.h b/runtime/vm/compiler/frontend/kernel_to_il.h index 595977a59ddc..564fb8f2d22b 100644 --- a/runtime/vm/compiler/frontend/kernel_to_il.h +++ b/runtime/vm/compiler/frontend/kernel_to_il.h @@ -12,6 +12,8 @@ #include "vm/compiler/backend/flow_graph.h" #include "vm/compiler/backend/il.h" +#include "vm/compiler/ffi/marshaller.h" +#include "vm/compiler/ffi/native_type.h" #include "vm/compiler/frontend/base_flow_graph_builder.h" #include "vm/compiler/frontend/kernel_translation_helper.h" #include "vm/compiler/frontend/scope_builder.h" @@ -124,9 +126,7 @@ class FlowGraphBuilder : public BaseFlowGraphBuilder { const CallSiteAttributesMetadata* call_site_attrs = nullptr, bool receiver_is_not_smi = false); - Fragment FfiCall(const Function& signature, - const ZoneGrowableArray& arg_reps, - const ZoneGrowableArray& arg_locs); + Fragment FfiCall(const compiler::ffi::CallMarshaller& marshaller); Fragment ThrowException(TokenPosition position); Fragment RethrowException(TokenPosition position, int catch_try_index); @@ -193,11 +193,6 @@ class FlowGraphBuilder : public BaseFlowGraphBuilder { // Sign-extends kUnboxedInt32 and zero-extends kUnboxedUint32. Fragment Box(Representation from); - // Sign- or zero-extends an integer parameter or return value for an FFI call - // as necessary. - Fragment FfiUnboxedExtend(Representation representation, - const AbstractType& ffi_type); - // Creates an ffi.Pointer holding a given address (TOS). Fragment FfiPointerFromAddress(const Type& result_type); @@ -209,17 +204,17 @@ class FlowGraphBuilder : public BaseFlowGraphBuilder { // Pops a Dart object and push the unboxed native version, according to the // semantics of FFI argument translation. Fragment FfiConvertArgumentToNative( - const Function& function, - const AbstractType& ffi_type, - const Representation native_representation); + const compiler::ffi::BaseMarshaller& marshaller, + intptr_t arg_index); // Reverse of 'FfiConvertArgumentToNative'. - Fragment FfiConvertArgumentToDart(const AbstractType& ffi_type, - const Representation native_representation); + Fragment FfiConvertArgumentToDart( + const compiler::ffi::BaseMarshaller& marshaller, + intptr_t arg_index); // Return from a native -> Dart callback. Can only be used in conjunction with // NativeEntry and NativeParameter are used. - Fragment NativeReturn(Representation result); + Fragment NativeReturn(const compiler::ffi::CallbackMarshaller& marshaller); // Bit-wise cast between representations. // Pops the input and pushes the converted result. diff --git a/runtime/vm/constants.h b/runtime/vm/constants.h index c84b00611208..4b21200f3c8c 100644 --- a/runtime/vm/constants.h +++ b/runtime/vm/constants.h @@ -5,6 +5,33 @@ #ifndef RUNTIME_VM_CONSTANTS_H_ #define RUNTIME_VM_CONSTANTS_H_ +namespace dart { + +// Alignment strategies for how to align values. +enum AlignmentStrategy { + // Align to the size of the value. + kAlignedToValueSize, + // Align to the size of the value, but align 8 byte-sized values to 4 bytes. + // Both double and int64. + kAlignedToValueSizeBut8AlignedTo4, + // Align to the architecture size. + kAlignedToWordSize, + // Align to the architecture size, but align 8 byte-sized values to 8 bytes. + // Both double and int64. + kAlignedToWordSizeBut8AlignedTo8, +}; + +// Minimum size strategies for how to store values. +enum ExtensionStrategy { + // Values can have arbitrary small size with the upper bits undefined. + kNotExtended, + // Values smaller than 4 bytes are passed around zero- or signextended to + // 4 bytes. + kExtendedTo4 +}; + +} // namespace dart + #if defined(TARGET_ARCH_IA32) #include "vm/constants_ia32.h" #elif defined(TARGET_ARCH_X64) diff --git a/runtime/vm/constants_arm.cc b/runtime/vm/constants_arm.cc index ab93625b643e..53cb128fb9d7 100644 --- a/runtime/vm/constants_arm.cc +++ b/runtime/vm/constants_arm.cc @@ -4,8 +4,7 @@ #if defined(TARGET_ARCH_ARM) -#define RUNTIME_VM_CONSTANTS_H_ // To work around include guard. -#include "vm/constants_arm.h" +#include "vm/constants.h" namespace dart { diff --git a/runtime/vm/constants_arm.h b/runtime/vm/constants_arm.h index af7da40f6d0a..f98cf4931bf3 100644 --- a/runtime/vm/constants_arm.h +++ b/runtime/vm/constants_arm.h @@ -363,14 +363,30 @@ class CallingConventions { static constexpr bool kAbiSoftFP = false; #endif - // Whether 64-bit arguments must be aligned to an even register or 8-byte - // stack address. True for ARM 32-bit, see "Procedure Call Standard for the - // ARM Architecture". - static constexpr bool kAlignArguments = true; + // Whether larger than wordsize arguments are aligned to even registers. + static constexpr AlignmentStrategy kArgumentRegisterAlignment = + kAlignedToWordSizeBut8AlignedTo8; + + // How stack arguments are aligned. + static constexpr AlignmentStrategy kArgumentStackAlignment = + kAlignedToWordSizeBut8AlignedTo8; + + // How fields in composites are aligned. +#if defined(TARGET_OS_MACOS_IOS) + static constexpr AlignmentStrategy kFieldAlignment = + kAlignedToValueSizeBut8AlignedTo4; +#else + static constexpr AlignmentStrategy kFieldAlignment = kAlignedToValueSize; +#endif + + // Whether 1 or 2 byte-sized arguments or return values are passed extended + // to 4 bytes. + static constexpr ExtensionStrategy kArgumentRegisterExtension = kExtendedTo4; + static constexpr ExtensionStrategy kArgumentStackExtension = kExtendedTo4; static constexpr Register kReturnReg = R0; static constexpr Register kSecondReturnReg = R1; - static constexpr FpuRegister kReturnFpuReg = kNoFpuRegister; + static constexpr FpuRegister kReturnFpuReg = Q0; // We choose these to avoid overlap between themselves and reserved registers. static constexpr Register kFirstNonArgumentRegister = R8; diff --git a/runtime/vm/constants_arm64.cc b/runtime/vm/constants_arm64.cc index e5a4461c51c8..e81fe3fed2e6 100644 --- a/runtime/vm/constants_arm64.cc +++ b/runtime/vm/constants_arm64.cc @@ -4,8 +4,7 @@ #if defined(TARGET_ARCH_ARM64) -#define RUNTIME_VM_CONSTANTS_H_ // To work around include guard. -#include "vm/constants_arm64.h" +#include "vm/constants.h" namespace dart { diff --git a/runtime/vm/constants_arm64.h b/runtime/vm/constants_arm64.h index 9db2eac03183..b9f3ea2963b1 100644 --- a/runtime/vm/constants_arm64.h +++ b/runtime/vm/constants_arm64.h @@ -10,6 +10,7 @@ #endif #include "platform/assert.h" +#include "platform/globals.h" namespace dart { @@ -217,9 +218,30 @@ class CallingConventions { // "hardfp"). static constexpr bool kAbiSoftFP = false; - // Whether 64-bit arguments must be aligned to an even register or 8-byte - // stack address. Not relevant on X64 since the word size is 64-bits already. - static constexpr bool kAlignArguments = false; + // Whether larger than wordsize arguments are aligned to even registers. + static constexpr AlignmentStrategy kArgumentRegisterAlignment = + kAlignedToWordSize; + + // How stack arguments are aligned. +#if defined(TARGET_OS_MACOS_IOS) + static constexpr AlignmentStrategy kArgumentStackAlignment = + kAlignedToValueSize; +#else + static constexpr AlignmentStrategy kArgumentStackAlignment = + kAlignedToWordSize; +#endif + + // How fields in composites are aligned. + static constexpr AlignmentStrategy kFieldAlignment = kAlignedToValueSize; + + // Whether 1 or 2 byte-sized arguments or return values are passed extended + // to 4 bytes. +#if defined(TARGET_OS_MACOS_IOS) + static constexpr ExtensionStrategy kArgumentRegisterExtension = kExtendedTo4; +#else + static constexpr ExtensionStrategy kArgumentRegisterExtension = kNotExtended; +#endif + static constexpr ExtensionStrategy kArgumentStackExtension = kNotExtended; static constexpr Register kReturnReg = R0; static constexpr Register kSecondReturnReg = kNoRegister; diff --git a/runtime/vm/constants_ia32.cc b/runtime/vm/constants_ia32.cc index e072552ea899..1f1bd20d81ac 100644 --- a/runtime/vm/constants_ia32.cc +++ b/runtime/vm/constants_ia32.cc @@ -4,8 +4,7 @@ #if defined(TARGET_ARCH_IA32) -#define RUNTIME_VM_CONSTANTS_H_ // To work around include guard. -#include "vm/constants_ia32.h" +#include "vm/constants.h" namespace dart { diff --git a/runtime/vm/constants_ia32.h b/runtime/vm/constants_ia32.h index de704d91122d..973e358b0793 100644 --- a/runtime/vm/constants_ia32.h +++ b/runtime/vm/constants_ia32.h @@ -10,6 +10,7 @@ #endif #include "platform/assert.h" +#include "platform/globals.h" namespace dart { @@ -38,6 +39,10 @@ enum ByteRegister { kNoByteRegister = -1 // Signals an illegal register. }; +inline ByteRegister ByteRegisterOf(Register reg) { + return static_cast(reg); +} + enum XmmRegister { XMM0 = 0, XMM1 = 1, @@ -153,17 +158,36 @@ class CallingConventions { static constexpr Register kSecondReturnReg = EDX; // Floating point values are returned on the "FPU stack" (in "ST" registers). - static constexpr XmmRegister kReturnFpuReg = kNoXmmRegister; + // However, we use XMM0 in our compiler pipeline as the location. + // The move from and to ST is done in FfiCallInstr::EmitNativeCode and + // NativeReturnInstr::EmitNativeCode. + static constexpr XmmRegister kReturnFpuReg = XMM0; 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* - // need to be 8-byte aligned. - static constexpr bool kAlignArguments = false; + // Whether larger than wordsize arguments are aligned to even registers. + static constexpr AlignmentStrategy kArgumentRegisterAlignment = + kAlignedToWordSize; + + // How stack arguments are aligned. + static constexpr AlignmentStrategy kArgumentStackAlignment = + kAlignedToWordSize; + + // How fields in composites are aligned. +#if defined(_WIN32) + static constexpr AlignmentStrategy kFieldAlignment = kAlignedToValueSize; +#else + static constexpr AlignmentStrategy kFieldAlignment = + kAlignedToValueSizeBut8AlignedTo4; +#endif + + // Whether 1 or 2 byte-sized arguments or return values are passed extended + // to 4 bytes. + static constexpr ExtensionStrategy kArgumentRegisterExtension = kNotExtended; + static constexpr ExtensionStrategy kArgumentStackExtension = kExtendedTo4; }; } // namespace dart diff --git a/runtime/vm/constants_x64.cc b/runtime/vm/constants_x64.cc index c96a9e3c22c7..056843aeaa16 100644 --- a/runtime/vm/constants_x64.cc +++ b/runtime/vm/constants_x64.cc @@ -4,8 +4,7 @@ #if defined(TARGET_ARCH_X64) -#define RUNTIME_VM_CONSTANTS_H_ // To work around include guard. -#include "vm/constants_x64.h" +#include "vm/constants.h" namespace dart { diff --git a/runtime/vm/constants_x64.h b/runtime/vm/constants_x64.h index 0537545829ab..1f69e56bc23d 100644 --- a/runtime/vm/constants_x64.h +++ b/runtime/vm/constants_x64.h @@ -205,9 +205,22 @@ class CallingConventions { // "hardfp"). static constexpr bool kAbiSoftFP = false; - // Whether 64-bit arguments must be aligned to an even register or 8-byte - // stack address. Not relevant on X64 since the word size is 64-bits already. - static constexpr bool kAlignArguments = false; + // Whether larger than wordsize arguments are aligned to even registers. + static constexpr AlignmentStrategy kArgumentRegisterAlignment = + kAlignedToWordSize; + + // How stack arguments are aligned. + static constexpr AlignmentStrategy kArgumentStackAlignment = + kAlignedToWordSize; + + // How fields in composites are aligned. + static constexpr AlignmentStrategy kFieldAlignment = kAlignedToValueSize; + + // Whether 1 or 2 byte-sized arguments or return values are passed extended + // to 4 bytes. + static constexpr ExtensionStrategy kArgumentRegisterExtension = kNotExtended; + static constexpr ExtensionStrategy kArgumentStackExtension = kNotExtended; + #else static const Register kArg1Reg = RDI; static const Register kArg2Reg = RSI; @@ -257,9 +270,22 @@ class CallingConventions { // "hardfp"). static constexpr bool kAbiSoftFP = false; - // Whether 64-bit arguments must be aligned to an even register or 8-byte - // stack address. Not relevant on X64 since the word size is 64-bits already. - static constexpr bool kAlignArguments = false; + // Whether larger than wordsize arguments are aligned to even registers. + static constexpr AlignmentStrategy kArgumentRegisterAlignment = + kAlignedToWordSize; + + // How stack arguments are aligned. + static constexpr AlignmentStrategy kArgumentStackAlignment = + kAlignedToWordSize; + + // How fields in composites are aligned. + static constexpr AlignmentStrategy kFieldAlignment = kAlignedToValueSize; + + // Whether 1 or 2 byte-sized arguments or return values are passed extended + // to 4 bytes. + static constexpr ExtensionStrategy kArgumentRegisterExtension = kNotExtended; + static constexpr ExtensionStrategy kArgumentStackExtension = kExtendedTo4; + #endif COMPILE_ASSERT((kArgumentRegisters & kReservedCpuRegisters) == 0);