| // 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_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<const NativeRegistersLocation&>(*this); |
| } |
| |
| const NativeFpuRegistersLocation& NativeLocation::AsFpuRegisters() const { |
| ASSERT(IsFpuRegisters()); |
| return static_cast<const NativeFpuRegistersLocation&>(*this); |
| } |
| |
| const NativeStackLocation& NativeLocation::AsStack() const { |
| ASSERT(IsStack()); |
| return static_cast<const NativeStackLocation&>(*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 WithOtherNativeType(payload_type().WidenTo4Bytes(zone), |
| container_type().WidenTo4Bytes(zone), zone); |
| } |
| |
| #if defined(TARGET_ARCH_ARM) |
| const NativeLocation& NativeLocation::WidenToQFpuRegister(Zone* zone) const { |
| if (!IsFpuRegisters()) { |
| return *this; |
| } |
| const auto& fpu_loc = AsFpuRegisters(); |
| switch (fpu_loc.fpu_reg_kind()) { |
| case kQuadFpuReg: |
| return *this; |
| case kDoubleFpuReg: { |
| return *new (zone) NativeFpuRegistersLocation( |
| payload_type_, container_type_, QRegisterOf(fpu_loc.fpu_d_reg())); |
| } |
| case kSingleFpuReg: { |
| return *new (zone) NativeFpuRegistersLocation( |
| payload_type_, container_type_, QRegisterOf(fpu_loc.fpu_s_reg())); |
| } |
| } |
| UNREACHABLE(); |
| } |
| #endif // defined(TARGET_ARCH_ARM) |
| |
| 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 { |
| switch (fpu_reg_kind()) { |
| case kQuadFpuReg: |
| f->Print("%s", RegisterNames::FpuRegisterName(fpu_reg())); |
| break; |
| #if defined(TARGET_ARCH_ARM) |
| case kDoubleFpuReg: |
| f->Print("%s", RegisterNames::FpuDRegisterName(fpu_d_reg())); |
| break; |
| case kSingleFpuReg: |
| f->Print("%s", RegisterNames::FpuSRegisterName(fpu_s_reg())); |
| break; |
| #endif // defined(TARGET_ARCH_ARM) |
| default: |
| UNREACHABLE(); |
| } |
| |
| 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); |
| } |
| |
| intptr_t SizeFromFpuRegisterKind(enum FpuRegisterKind kind) { |
| switch (kind) { |
| case kQuadFpuReg: |
| return 16; |
| case kDoubleFpuReg: |
| return 8; |
| case kSingleFpuReg: |
| return 4; |
| } |
| UNREACHABLE(); |
| } |
| enum FpuRegisterKind FpuRegisterKindFromSize(intptr_t size_in_bytes) { |
| switch (size_in_bytes) { |
| case 16: |
| return kQuadFpuReg; |
| case 8: |
| return kDoubleFpuReg; |
| case 4: |
| return kSingleFpuReg; |
| } |
| UNREACHABLE(); |
| } |
| |
| #if defined(TARGET_ARCH_ARM) |
| DRegister NativeFpuRegistersLocation::fpu_as_d_reg() const { |
| switch (fpu_reg_kind_) { |
| case kQuadFpuReg: |
| return EvenDRegisterOf(fpu_reg()); |
| case kDoubleFpuReg: |
| return fpu_d_reg(); |
| case kSingleFpuReg: |
| return DRegisterOf(fpu_s_reg()); |
| } |
| } |
| |
| SRegister NativeFpuRegistersLocation::fpu_as_s_reg() const { |
| switch (fpu_reg_kind_) { |
| case kQuadFpuReg: |
| return EvenSRegisterOf(EvenDRegisterOf(fpu_reg())); |
| case kDoubleFpuReg: |
| return EvenSRegisterOf(fpu_d_reg()); |
| case kSingleFpuReg: |
| return fpu_s_reg(); |
| } |
| } |
| |
| bool NativeFpuRegistersLocation::IsLowestBits() const { |
| switch (fpu_reg_kind()) { |
| case kQuadFpuReg: |
| return true; |
| case kDoubleFpuReg: { |
| return fpu_d_reg() % 2 == 0; |
| } |
| case kSingleFpuReg: { |
| return fpu_s_reg() % 4 == 0; |
| } |
| } |
| UNREACHABLE(); |
| } |
| #endif // defined(TARGET_ARCH_ARM) |
| |
| } // namespace ffi |
| |
| } // namespace compiler |
| |
| } // namespace dart |