// 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_ #if defined(DART_PRECOMPILED_RUNTIME) #error "AOT runtime should not use compiler sources (including header files)" #endif // defined(DART_PRECOMPILED_RUNTIME) #include "platform/assert.h" #include "vm/compiler/ffi/native_type.h" #include "vm/compiler/runtime_api.h" #include "vm/constants.h" #include "vm/growable_array.h" #if !defined(FFI_UNIT_TESTS) #include "vm/compiler/backend/locations.h" #endif namespace dart { class BaseTextBuffer; namespace compiler { namespace ffi { class NativeRegistersLocation; class NativeFpuRegistersLocation; class NativeStackLocation; class MultipleNativeLocations; class PointerToMemoryLocation; // 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 type. // * The container type, equal to or larger than the payload. If the // container is larger than the payload, the upper bits are defined by sign // or zero extension. // Container type is also used to denote an integer container when floating // point values are passed in integer registers. // // NativeLocations can express things that dart::Locations cannot express: // * Multiple consecutive registers. // * Multiple sizes of FPU registers (e.g. S, D, and Q on Arm32). // * Arbitrary byte-size stack locations, at byte-size offsets. // (The Location class uses word-size offsets.) // * Pointers to a memory 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 types. // * No ConstantLocations. // // NativeLocation does not satisfy the invariant of Location: bitwise // inequality cannot be used to determine disjointness. class NativeLocation : public ZoneAllocated { public: #if !defined(FFI_UNIT_TESTS) static bool LocationCanBeExpressed(Location loc, Representation rep); static NativeLocation& FromLocation(Zone* zone, Location loc, Representation rep); static NativeLocation& FromPairLocation(Zone* zone, Location loc, Representation rep, intptr_t index); #endif // The type 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& WithOtherNativeType( Zone* zone, const NativeType& new_payload_type, const NativeType& new_container_type) const = 0; #if defined(TARGET_ARCH_ARM) const NativeLocation& WidenToQFpuRegister(Zone* zone) const; #endif // defined(TARGET_ARCH_ARM) 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 IsMultiple() const { return false; } virtual bool IsPointerToMemory() const { return false; } virtual bool IsExpressibleAsLocation() const { return false; } #if !defined(FFI_UNIT_TESTS) virtual Location AsLocation() const { ASSERT(IsExpressibleAsLocation()); UNREACHABLE(); } #endif virtual void PrintTo(BaseTextBuffer* f) const; const char* ToCString(Zone* zone) const; #if !defined(FFI_UNIT_TESTS) const char* ToCString() const; #endif const NativeRegistersLocation& AsRegisters() const; const NativeFpuRegistersLocation& AsFpuRegisters() const; const NativeStackLocation& AsStack() const; const MultipleNativeLocations& AsMultiple() const; const PointerToMemoryLocation& AsPointerToMemory() const; // Retrieve one part from this location when it is split into multiple parts. virtual NativeLocation& Split(Zone* zone, intptr_t num_parts, intptr_t index) const { UNREACHABLE(); } // Return the top of the stack in bytes. Recurses over its constituents when // MultipleNativeLocations. virtual intptr_t StackTopInBytes() const { return 0; } // Equality of location, ignores the payload and container native types. virtual bool Equals(const NativeLocation& other) const { UNREACHABLE(); } 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(Zone* zone, const NativeType& payload_type, const NativeType& container_type, Register reg) : NativeLocation(payload_type, container_type) { regs_ = new (zone) ZoneGrowableArray(zone, 1); regs_->Add(reg); } NativeRegistersLocation(Zone* zone, const NativeType& payload_type, const NativeType& container_type, Register register1, Register register2) : NativeLocation(payload_type, container_type) { regs_ = new (zone) ZoneGrowableArray(zone, 2); regs_->Add(register1); regs_->Add(register2); } virtual ~NativeRegistersLocation() {} virtual NativeRegistersLocation& WithOtherNativeType( Zone* zone, const NativeType& new_payload_type, const NativeType& new_container_type) 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; } #if !defined(FFI_UNIT_TESTS) virtual Location AsLocation() const; #endif intptr_t num_regs() const { return regs_->length(); } Register reg_at(intptr_t index) const { return regs_->At(index); } virtual NativeRegistersLocation& Split(Zone* zone, intptr_t num_parts, intptr_t index) const; virtual void PrintTo(BaseTextBuffer* f) const; virtual bool Equals(const NativeLocation& other) const; private: ZoneGrowableArray* regs_; DISALLOW_COPY_AND_ASSIGN(NativeRegistersLocation); }; enum FpuRegisterKind { kQuadFpuReg, // 16 bytes kDoubleFpuReg, // 8 bytes, a double kSingleFpuReg // 4 bytes, a float }; intptr_t SizeFromFpuRegisterKind(FpuRegisterKind kind); FpuRegisterKind FpuRegisterKindFromSize(intptr_t size_in_bytes); class NativeFpuRegistersLocation : public NativeLocation { public: NativeFpuRegistersLocation(const NativeType& payload_type, const NativeType& container_type, FpuRegisterKind fpu_reg_kind, intptr_t fpu_register) : NativeLocation(payload_type, container_type), fpu_reg_kind_(fpu_reg_kind), fpu_reg_(fpu_register) {} NativeFpuRegistersLocation(const NativeType& payload_type, const NativeType& container_type, FpuRegister fpu_register) : NativeLocation(payload_type, container_type), fpu_reg_kind_(kQuadFpuReg), fpu_reg_(fpu_register) {} #if defined(TARGET_ARCH_ARM) NativeFpuRegistersLocation(const NativeType& payload_type, const NativeType& container_type, DRegister fpu_register) : NativeLocation(payload_type, container_type), fpu_reg_kind_(kDoubleFpuReg), fpu_reg_(fpu_register) {} NativeFpuRegistersLocation(const NativeType& payload_type, const NativeType& container_type, SRegister fpu_register) : NativeLocation(payload_type, container_type), fpu_reg_kind_(kSingleFpuReg), fpu_reg_(fpu_register) {} #endif // defined(TARGET_ARCH_ARM) virtual ~NativeFpuRegistersLocation() {} virtual NativeFpuRegistersLocation& WithOtherNativeType( Zone* zone, const NativeType& new_payload_type, const NativeType& new_container_type) const { return *new (zone) NativeFpuRegistersLocation( new_payload_type, new_container_type, fpu_reg_kind_, fpu_reg_); } virtual bool IsFpuRegisters() const { return true; } virtual bool IsExpressibleAsLocation() const { return fpu_reg_kind_ == kQuadFpuReg; } #if !defined(FFI_UNIT_TESTS) virtual Location AsLocation() const { ASSERT(IsExpressibleAsLocation()); return Location::FpuRegisterLocation(fpu_reg()); } #endif FpuRegisterKind fpu_reg_kind() const { return fpu_reg_kind_; } FpuRegister fpu_reg() const { ASSERT(fpu_reg_kind_ == kQuadFpuReg); return static_cast(fpu_reg_); } #if defined(TARGET_ARCH_ARM) DRegister fpu_d_reg() const { ASSERT(fpu_reg_kind_ == kDoubleFpuReg); return static_cast(fpu_reg_); } SRegister fpu_s_reg() const { ASSERT(fpu_reg_kind_ == kSingleFpuReg); return static_cast(fpu_reg_); } DRegister fpu_as_d_reg() const; SRegister fpu_as_s_reg() const; bool IsLowestBits() const; #endif // defined(TARGET_ARCH_ARM) virtual void PrintTo(BaseTextBuffer* f) const; virtual bool Equals(const NativeLocation& other) const; private: FpuRegisterKind fpu_reg_kind_; intptr_t 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& WithOtherNativeType( Zone* zone, const NativeType& new_payload_type, const NativeType& new_container_type) 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); } #if !defined(FFI_UNIT_TESTS) 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_); } #endif virtual NativeStackLocation& Split(Zone* zone, intptr_t num_parts, intptr_t index) const; virtual intptr_t StackTopInBytes() const { return offset_in_bytes() + container_type().SizeInBytes(); } virtual void PrintTo(BaseTextBuffer* f) const; virtual bool Equals(const NativeLocation& other) const; 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); }; // The location of a pointer pointing to a compound. // // For arguments a pointer to a copy of an object. The backing copy of the // object typically resides on the stack. // // For return values a pointer to empty space that should hold the object. This // space also typically resides on the stack. class PointerToMemoryLocation : public NativeLocation { public: PointerToMemoryLocation(const NativeLocation& pointer_location, const NativeCompoundType& object_pointed_to) : NativeLocation(object_pointed_to, object_pointed_to), pointer_location_(pointer_location), pointer_return_location_(pointer_location) { ASSERT(pointer_location.IsRegisters() || pointer_location.IsStack()); } PointerToMemoryLocation(const NativeLocation& pointer_location, const NativeLocation& pointer_return_location, const NativeCompoundType& object_pointed_to) : NativeLocation(object_pointed_to, object_pointed_to), pointer_location_(pointer_location), pointer_return_location_(pointer_return_location) { ASSERT(pointer_location.IsRegisters() || pointer_location.IsStack()); } virtual ~PointerToMemoryLocation() {} virtual bool IsPointerToMemory() const { return true; } virtual void PrintTo(BaseTextBuffer* f) const; virtual bool Equals(const NativeLocation& other) const; virtual NativeLocation& WithOtherNativeType( Zone* zone, const NativeType& new_payload_type, const NativeType& new_container_type) const { UNREACHABLE(); } virtual intptr_t StackTopInBytes() const { return pointer_location().StackTopInBytes(); } // The location where the pointer is passed to the function. const NativeLocation& pointer_location() const { return pointer_location_; } // The location where the pointer is returned from the function. const NativeLocation& pointer_return_location() const { return pointer_return_location_; } private: const NativeLocation& pointer_location_; // The return location is only in use for return values, not for arguments. const NativeLocation& pointer_return_location_; DISALLOW_COPY_AND_ASSIGN(PointerToMemoryLocation); }; using NativeLocations = ZoneGrowableArray; // A struct broken up over multiple native locations. class MultipleNativeLocations : public NativeLocation { public: MultipleNativeLocations(const NativeCompoundType& payload_type, const NativeLocations& locations) : NativeLocation(payload_type, payload_type), locations_(locations) {} virtual ~MultipleNativeLocations() {} virtual bool IsMultiple() const { return true; } virtual void PrintTo(BaseTextBuffer* f) const; virtual NativeLocation& WithOtherNativeType( Zone* zone, const NativeType& new_payload_type, const NativeType& new_container_type) const { UNREACHABLE(); } virtual intptr_t StackTopInBytes() const; const NativeLocations& locations() const { return locations_; } private: const NativeLocations& locations_; DISALLOW_COPY_AND_ASSIGN(MultipleNativeLocations); }; #if !defined(FFI_UNIT_TESTS) // Return a memory operand for stack slot locations. compiler::Address NativeLocationToStackSlotAddress( const NativeStackLocation& loc); #endif } // namespace ffi } // namespace compiler } // namespace dart #endif // RUNTIME_VM_COMPILER_FFI_NATIVE_LOCATION_H_