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 | |
17 | namespace dart { |
18 | |
19 | class BaseTextBuffer; |
20 | |
21 | namespace compiler { |
22 | |
23 | namespace ffi { |
24 | |
25 | class NativeRegistersLocation; |
26 | class NativeFpuRegistersLocation; |
27 | class 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. |
54 | class 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 | |
130 | class 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 | |
182 | enum FpuRegisterKind { |
183 | kQuadFpuReg, // 16 bytes |
184 | kDoubleFpuReg, // 8 bytes, a double |
185 | kSingleFpuReg // 4 bytes, a float |
186 | }; |
187 | |
188 | intptr_t SizeFromFpuRegisterKind(FpuRegisterKind kind); |
189 | FpuRegisterKind FpuRegisterKindFromSize(intptr_t size_in_bytes); |
190 | |
191 | class 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 | |
267 | class 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. |
326 | compiler::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 | |