1// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
2// for details. All rights reserved. Use of this source code is governed by a
3// BSD-style license that can be found in the LICENSE file.
4
5#ifndef RUNTIME_VM_COMPILER_FFI_NATIVE_LOCATION_H_
6#define RUNTIME_VM_COMPILER_FFI_NATIVE_LOCATION_H_
7
8#if defined(DART_PRECOMPILED_RUNTIME)
9#error "AOT runtime should not use compiler sources (including header files)"
10#endif // defined(DART_PRECOMPILED_RUNTIME)
11
12#include "vm/compiler/backend/locations.h"
13#include "vm/compiler/ffi/native_type.h"
14#include "vm/growable_array.h"
15#include "vm/thread.h"
16
17namespace dart {
18
19class BaseTextBuffer;
20
21namespace compiler {
22
23namespace ffi {
24
25class NativeRegistersLocation;
26class NativeFpuRegistersLocation;
27class NativeStackLocation;
28
29// NativeLocation objects are used in the FFI to describe argument and return
30// value locations in all native ABIs that the FFI supports.
31//
32// NativeLocations contain two NativeTypes.
33// * The payload type.
34// * The container type, equal to or larger than the payload. If the
35// container is larger than the payload, the upper bits are defined by sign
36// or zero extension.
37//
38// NativeLocations can express things that dart::Locations cannot express:
39// * Multiple consecutive registers.
40// * Multiple sizes of FPU registers (e.g. S, D, and Q on Arm32).
41// * Arbitrary byte-size stack locations, at byte-size offsets.
42// (The Location class uses word-size offsets.)
43// * Pointers including a backing location on the stack.
44// * No location.
45// * Split between multiple registers and stack.
46//
47// NativeLocations cannot express the following dart::Locations:
48// * No PairLocations. Instead, NativeRegistersLocations can have multiple
49// registers, and NativeStackLocations can have arbitrary types.
50// * No ConstantLocations.
51//
52// NativeLocation does not satisfy the invariant of Location: bitwise
53// inequality cannot be used to determine disjointness.
54class NativeLocation : public ZoneAllocated {
55 public:
56 static bool LocationCanBeExpressed(Location loc, Representation rep);
57 static NativeLocation& FromLocation(Location loc,
58 Representation rep,
59 Zone* zone);
60 static NativeLocation& FromPairLocation(Location loc,
61 Representation rep,
62 intptr_t index,
63 Zone* zone);
64
65 // The type of the data at this location.
66 const NativeType& payload_type() const { return payload_type_; }
67
68 // The location container size, possibly larger than data.
69 //
70 // If the container is larger than the data, the remaining bits are _not_
71 // undefined. For example a uint8 inside a uint32 has the upper 24 bits set
72 // to 0. Effectively allowing the value to be read as uint8, uint16 and
73 // uint32.
74 const NativeType& container_type() const { return container_type_; }
75
76 virtual NativeLocation& WithOtherNativeType(
77 const NativeType& new_payload_type,
78 const NativeType& new_container_type,
79 Zone* zone) const = 0;
80
81#if defined(TARGET_ARCH_ARM)
82 const NativeLocation& WidenToQFpuRegister(Zone* zone) const;
83#endif // defined(TARGET_ARCH_ARM)
84
85 NativeLocation& WidenTo4Bytes(Zone* zone) const;
86
87 virtual bool IsRegisters() const { return false; }
88 virtual bool IsFpuRegisters() const { return false; }
89 virtual bool IsStack() const { return false; }
90
91 virtual bool IsExpressibleAsLocation() const { return false; }
92 virtual Location AsLocation() const {
93 ASSERT(IsExpressibleAsLocation());
94 UNREACHABLE();
95 }
96
97 virtual void PrintTo(BaseTextBuffer* f) const;
98 const char* ToCString() const;
99
100 const NativeRegistersLocation& AsRegisters() const;
101 const NativeFpuRegistersLocation& AsFpuRegisters() const;
102 const NativeStackLocation& AsStack() const;
103
104 virtual NativeLocation& Split(intptr_t index, Zone* zone) const {
105 ASSERT(index == 0 || index == 1);
106 UNREACHABLE();
107 }
108
109 // Equality of location, ignores the payload and container native types.
110 virtual bool Equals(const NativeLocation& other) const { UNREACHABLE(); }
111
112 virtual ~NativeLocation() {}
113
114 protected:
115 NativeLocation(const NativeType& payload_type,
116 const NativeType& container_type)
117 : payload_type_(payload_type), container_type_(container_type) {}
118
119 private:
120 const NativeType& payload_type_;
121 // The location container size, possibly larger than data.
122 //
123 // If the container is larger than the data, the remaining bits are _not_
124 // undefined. For example a uint8 inside a uint32 has the upper 24 bits set
125 // to 0. Effectively allowing the value to be read as uint8, uint16 and
126 // uint32.
127 const NativeType& container_type_;
128};
129
130class NativeRegistersLocation : public NativeLocation {
131 public:
132 NativeRegistersLocation(const NativeType& payload_type,
133 const NativeType& container_type,
134 ZoneGrowableArray<Register>* registers)
135 : NativeLocation(payload_type, container_type), regs_(registers) {}
136 NativeRegistersLocation(const NativeType& payload_type,
137 const NativeType& container_type,
138 Register reg)
139 : NativeLocation(payload_type, container_type) {
140 regs_ = new ZoneGrowableArray<Register>();
141 regs_->Add(reg);
142 }
143 NativeRegistersLocation(const NativeType& payload_type,
144 const NativeType& container_type,
145 Register register1,
146 Register register2)
147 : NativeLocation(payload_type, container_type) {
148 regs_ = new ZoneGrowableArray<Register>();
149 regs_->Add(register1);
150 regs_->Add(register2);
151 }
152 virtual ~NativeRegistersLocation() {}
153
154 virtual NativeRegistersLocation& WithOtherNativeType(
155 const NativeType& new_payload_type,
156 const NativeType& new_container_type,
157 Zone* zone) const {
158 return *new (zone)
159 NativeRegistersLocation(new_payload_type, new_container_type, regs_);
160 }
161
162 virtual bool IsRegisters() const { return true; }
163 virtual bool IsExpressibleAsLocation() const {
164 return num_regs() == 1 || num_regs() == 2;
165 }
166 virtual Location AsLocation() const;
167 intptr_t num_regs() const { return regs_->length(); }
168 Register reg_at(intptr_t index) const { return regs_->At(index); }
169
170 virtual NativeRegistersLocation& Split(intptr_t index, Zone* zone) const;
171
172 virtual void PrintTo(BaseTextBuffer* f) const;
173
174 virtual bool Equals(const NativeLocation& other) const;
175
176 private:
177 ZoneGrowableArray<Register>* regs_;
178
179 DISALLOW_COPY_AND_ASSIGN(NativeRegistersLocation);
180};
181
182enum FpuRegisterKind {
183 kQuadFpuReg, // 16 bytes
184 kDoubleFpuReg, // 8 bytes, a double
185 kSingleFpuReg // 4 bytes, a float
186};
187
188intptr_t SizeFromFpuRegisterKind(FpuRegisterKind kind);
189FpuRegisterKind FpuRegisterKindFromSize(intptr_t size_in_bytes);
190
191class NativeFpuRegistersLocation : public NativeLocation {
192 public:
193 NativeFpuRegistersLocation(const NativeType& payload_type,
194 const NativeType& container_type,
195 FpuRegisterKind fpu_reg_kind,
196 intptr_t fpu_register)
197 : NativeLocation(payload_type, container_type),
198 fpu_reg_kind_(fpu_reg_kind),
199 fpu_reg_(fpu_register) {}
200 NativeFpuRegistersLocation(const NativeType& payload_type,
201 const NativeType& container_type,
202 FpuRegister fpu_register)
203 : NativeLocation(payload_type, container_type),
204 fpu_reg_kind_(kQuadFpuReg),
205 fpu_reg_(fpu_register) {}
206#if defined(TARGET_ARCH_ARM)
207 NativeFpuRegistersLocation(const NativeType& payload_type,
208 const NativeType& container_type,
209 DRegister fpu_register)
210 : NativeLocation(payload_type, container_type),
211 fpu_reg_kind_(kDoubleFpuReg),
212 fpu_reg_(fpu_register) {}
213 NativeFpuRegistersLocation(const NativeType& payload_type,
214 const NativeType& container_type,
215 SRegister fpu_register)
216 : NativeLocation(payload_type, container_type),
217 fpu_reg_kind_(kSingleFpuReg),
218 fpu_reg_(fpu_register) {}
219#endif // defined(TARGET_ARCH_ARM)
220 virtual ~NativeFpuRegistersLocation() {}
221
222 virtual NativeFpuRegistersLocation& WithOtherNativeType(
223 const NativeType& new_payload_type,
224 const NativeType& new_container_type,
225 Zone* zone) const {
226 return *new (zone) NativeFpuRegistersLocation(
227 new_payload_type, new_container_type, fpu_reg_kind_, fpu_reg_);
228 }
229 virtual bool IsFpuRegisters() const { return true; }
230 virtual bool IsExpressibleAsLocation() const {
231 return fpu_reg_kind_ == kQuadFpuReg;
232 }
233 virtual Location AsLocation() const {
234 ASSERT(IsExpressibleAsLocation());
235 return Location::FpuRegisterLocation(fpu_reg());
236 }
237 FpuRegisterKind fpu_reg_kind() const { return fpu_reg_kind_; }
238 FpuRegister fpu_reg() const {
239 ASSERT(fpu_reg_kind_ == kQuadFpuReg);
240 return static_cast<FpuRegister>(fpu_reg_);
241 }
242#if defined(TARGET_ARCH_ARM)
243 DRegister fpu_d_reg() const {
244 ASSERT(fpu_reg_kind_ == kDoubleFpuReg);
245 return static_cast<DRegister>(fpu_reg_);
246 }
247 SRegister fpu_s_reg() const {
248 ASSERT(fpu_reg_kind_ == kSingleFpuReg);
249 return static_cast<SRegister>(fpu_reg_);
250 }
251 DRegister fpu_as_d_reg() const;
252 SRegister fpu_as_s_reg() const;
253
254 bool IsLowestBits() const;
255#endif // defined(TARGET_ARCH_ARM)
256
257 virtual void PrintTo(BaseTextBuffer* f) const;
258
259 virtual bool Equals(const NativeLocation& other) const;
260
261 private:
262 FpuRegisterKind fpu_reg_kind_;
263 intptr_t fpu_reg_;
264 DISALLOW_COPY_AND_ASSIGN(NativeFpuRegistersLocation);
265};
266
267class NativeStackLocation : public NativeLocation {
268 public:
269 NativeStackLocation(const NativeType& payload_type,
270 const NativeType& container_type,
271 Register base_register,
272 intptr_t offset_in_bytes)
273 : NativeLocation(payload_type, container_type),
274 base_register_(base_register),
275 offset_in_bytes_(offset_in_bytes) {}
276 virtual ~NativeStackLocation() {}
277
278 virtual NativeStackLocation& WithOtherNativeType(
279 const NativeType& new_payload_type,
280 const NativeType& new_container_type,
281 Zone* zone) const {
282 return *new (zone) NativeStackLocation(new_payload_type, new_container_type,
283 base_register_, offset_in_bytes_);
284 }
285
286 virtual bool IsStack() const { return true; }
287 virtual bool IsExpressibleAsLocation() const {
288 const intptr_t size = payload_type().SizeInBytes();
289 const intptr_t size_slots = size / compiler::target::kWordSize;
290 return offset_in_bytes_ % compiler::target::kWordSize == 0 &&
291 size % compiler::target::kWordSize == 0 &&
292 (size_slots == 1 || size_slots == 2);
293 }
294 virtual Location AsLocation() const;
295
296 // ConstantInstr expects DoubleStackSlot for doubles, even on 64-bit systems.
297 //
298 // So this return a wrong-sized Location on purpose.
299 Location AsDoubleStackSlotLocation() const {
300 ASSERT(compiler::target::kWordSize == 8);
301 return Location::DoubleStackSlot(offset_in_words(), base_register_);
302 }
303
304 virtual NativeStackLocation& Split(intptr_t index, Zone* zone) const;
305
306 virtual void PrintTo(BaseTextBuffer* f) const;
307
308 virtual bool Equals(const NativeLocation& other) const;
309
310 Register base_register() const { return base_register_; }
311 intptr_t offset_in_bytes() const { return offset_in_bytes_; }
312
313 private:
314 intptr_t offset_in_words() const {
315 ASSERT(offset_in_bytes_ % compiler::target::kWordSize == 0);
316 return offset_in_bytes_ / compiler::target::kWordSize;
317 }
318
319 Register base_register_;
320 intptr_t offset_in_bytes_;
321
322 DISALLOW_COPY_AND_ASSIGN(NativeStackLocation);
323};
324
325// Return a memory operand for stack slot locations.
326compiler::Address NativeLocationToStackSlotAddress(
327 const NativeStackLocation& loc);
328
329} // namespace ffi
330
331} // namespace compiler
332
333} // namespace dart
334
335#endif // RUNTIME_VM_COMPILER_FFI_NATIVE_LOCATION_H_
336