1 | // Copyright (c) 2018, 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 | // Slot is an abstraction that describes an readable (and possibly writeable) |
6 | // location within an object. |
7 | // |
8 | // In general slots follow the memory model for normal Dart fields - but can |
9 | // also be used to describe locations that don't have corresponding Field |
10 | // object, i.e. fields within native objects like arrays or contexts. |
11 | // |
12 | // Slot objects created by the compiler have an identity. If two slots F and G |
13 | // are different then compiler assumes that store into F can't alias a load |
14 | // from G and vice versa. |
15 | // |
16 | // All slots can be split into 4 categories: |
17 | // |
18 | // - slots for fields of native classes (Array, Closure, etc); |
19 | // - slots for type arguments; |
20 | // - slots for captured variable; |
21 | // - slots for normal Dart fields (e.g. those that Field object). |
22 | // |
23 | |
24 | #ifndef RUNTIME_VM_COMPILER_BACKEND_SLOT_H_ |
25 | #define RUNTIME_VM_COMPILER_BACKEND_SLOT_H_ |
26 | |
27 | #if defined(DART_PRECOMPILED_RUNTIME) |
28 | #error "AOT runtime should not use compiler sources (including header files)" |
29 | #endif // defined(DART_PRECOMPILED_RUNTIME) |
30 | |
31 | #include "vm/compiler/backend/compile_type.h" |
32 | #include "vm/thread.h" |
33 | |
34 | namespace dart { |
35 | |
36 | class LocalScope; |
37 | class LocalVariable; |
38 | class ParsedFunction; |
39 | |
40 | // List of slots that correspond to fields of native objects in the following |
41 | // format: |
42 | // |
43 | // V(class_name, underlying_type, field_name, exact_type, FINAL|VAR) |
44 | // |
45 | // - class_name and field_name specify the name of the host class and the name |
46 | // of the field respectively; |
47 | // - underlying_type: the Raw class which holds the field; |
48 | // - exact_type specifies exact type of the field (any load from this field |
49 | // would only yield instances of this type); |
50 | // - the last component specifies whether field behaves like a final field |
51 | // (i.e. initialized once at construction time and does not change after |
52 | // that) or like a non-final field. |
53 | // |
54 | // Note: native slots are expected to be non-nullable. |
55 | #define NATIVE_SLOTS_LIST(V) \ |
56 | V(Array, ArrayLayout, length, Smi, FINAL) \ |
57 | V(Context, ContextLayout, parent, Context, FINAL) \ |
58 | V(Closure, ClosureLayout, instantiator_type_arguments, TypeArguments, FINAL) \ |
59 | V(Closure, ClosureLayout, delayed_type_arguments, TypeArguments, FINAL) \ |
60 | V(Closure, ClosureLayout, function_type_arguments, TypeArguments, FINAL) \ |
61 | V(Closure, ClosureLayout, function, Function, FINAL) \ |
62 | V(Closure, ClosureLayout, context, Context, FINAL) \ |
63 | V(Closure, ClosureLayout, hash, Context, VAR) \ |
64 | V(GrowableObjectArray, GrowableObjectArrayLayout, length, Smi, VAR) \ |
65 | V(GrowableObjectArray, GrowableObjectArrayLayout, data, Array, VAR) \ |
66 | V(TypedDataBase, TypedDataBaseLayout, length, Smi, FINAL) \ |
67 | V(TypedDataView, TypedDataViewLayout, offset_in_bytes, Smi, FINAL) \ |
68 | V(TypedDataView, TypedDataViewLayout, data, Dynamic, FINAL) \ |
69 | V(String, StringLayout, length, Smi, FINAL) \ |
70 | V(LinkedHashMap, LinkedHashMapLayout, index, TypedDataUint32Array, VAR) \ |
71 | V(LinkedHashMap, LinkedHashMapLayout, data, Array, VAR) \ |
72 | V(LinkedHashMap, LinkedHashMapLayout, hash_mask, Smi, VAR) \ |
73 | V(LinkedHashMap, LinkedHashMapLayout, used_data, Smi, VAR) \ |
74 | V(LinkedHashMap, LinkedHashMapLayout, deleted_keys, Smi, VAR) \ |
75 | V(ArgumentsDescriptor, ArrayLayout, type_args_len, Smi, FINAL) \ |
76 | V(ArgumentsDescriptor, ArrayLayout, positional_count, Smi, FINAL) \ |
77 | V(ArgumentsDescriptor, ArrayLayout, count, Smi, FINAL) \ |
78 | V(ArgumentsDescriptor, ArrayLayout, size, Smi, FINAL) \ |
79 | V(PointerBase, PointerBaseLayout, data_field, Dynamic, FINAL) \ |
80 | V(Type, TypeLayout, arguments, TypeArguments, FINAL) \ |
81 | V(UnhandledException, UnhandledExceptionLayout, exception, Dynamic, FINAL) \ |
82 | V(UnhandledException, UnhandledExceptionLayout, stacktrace, Dynamic, FINAL) |
83 | |
84 | // Slot is an abstraction that describes an readable (and possibly writeable) |
85 | // location within an object. |
86 | // |
87 | // Slot objects returned by Slot::Get* methods have identity and can be |
88 | // compared by pointer. If two slots are different they must not alias. |
89 | // If two slots can alias - they must be represented by identical |
90 | // slot object. |
91 | class Slot : public ZoneAllocated { |
92 | public: |
93 | // clang-format off |
94 | enum class Kind : uint8_t { |
95 | // Native slots are identified by their kind - each native slot has its own. |
96 | #define DECLARE_KIND(ClassName, UnderlyingType, FieldName, cid, mutability) \ |
97 | k##ClassName##_##FieldName, |
98 | NATIVE_SLOTS_LIST(DECLARE_KIND) |
99 | #undef DECLARE_KIND |
100 | |
101 | // A slot used to store type arguments. |
102 | kTypeArguments, |
103 | |
104 | // A slot at a specific [index] in a [RawTypeArgument] vector. |
105 | kTypeArgumentsIndex, |
106 | |
107 | // A slot within a Context object that contains a value of a captured |
108 | // local variable. |
109 | kCapturedVariable, |
110 | |
111 | // A slot that corresponds to a Dart field (has corresponding Field object). |
112 | kDartField, |
113 | }; |
114 | // clang-format on |
115 | |
116 | static const char* KindToCString(Kind k); |
117 | static bool ParseKind(const char* str, Kind* k); |
118 | |
119 | // Returns a slot that represents length field for the given [array_cid]. |
120 | static const Slot& GetLengthFieldForArrayCid(intptr_t array_cid); |
121 | |
122 | // Return a slot that represents type arguments field at the given offset |
123 | // or for the given class. |
124 | // |
125 | // We do not distinguish type argument fields within disjoint |
126 | // class hierarchies: type argument fields at the same offset would be |
127 | // represented by the same Slot object. Type argument slots are final |
128 | // so disambiguating type arguments fields does not improve alias analysis. |
129 | static const Slot& GetTypeArgumentsSlotAt(Thread* thread, intptr_t offset); |
130 | static const Slot& GetTypeArgumentsSlotFor(Thread* thread, const Class& cls); |
131 | |
132 | // Returns a slot at a specific [index] in a [RawTypeArgument] vector. |
133 | static const Slot& GetTypeArgumentsIndexSlot(Thread* thread, intptr_t index); |
134 | |
135 | // Returns a slot that represents the given captured local variable. |
136 | static const Slot& GetContextVariableSlotFor(Thread* thread, |
137 | const LocalVariable& var); |
138 | |
139 | // Returns a slot that represents the given Dart field. |
140 | static const Slot& Get(const Field& field, |
141 | const ParsedFunction* parsed_function); |
142 | |
143 | // Convenience getters for native slots. |
144 | #define DEFINE_GETTER(ClassName, UnderlyingType, FieldName, cid, mutability) \ |
145 | static const Slot& ClassName##_##FieldName() { \ |
146 | return GetNativeSlot(Kind::k##ClassName##_##FieldName); \ |
147 | } |
148 | |
149 | NATIVE_SLOTS_LIST(DEFINE_GETTER) |
150 | #undef DEFINE_GETTER |
151 | |
152 | Kind kind() const { return kind_; } |
153 | bool IsDartField() const { return kind() == Kind::kDartField; } |
154 | bool IsLocalVariable() const { return kind() == Kind::kCapturedVariable; } |
155 | bool IsTypeArguments() const { return kind() == Kind::kTypeArguments; } |
156 | bool IsArgumentOfType() const { return kind() == Kind::kTypeArgumentsIndex; } |
157 | |
158 | const char* Name() const; |
159 | |
160 | intptr_t offset_in_bytes() const { return offset_in_bytes_; } |
161 | |
162 | bool is_immutable() const { return IsImmutableBit::decode(flags_); } |
163 | |
164 | intptr_t nullable_cid() const { return cid_; } |
165 | bool is_nullable() const { return IsNullableBit::decode(flags_); } |
166 | |
167 | // Returns true if properties of this slot were based on the guarded state |
168 | // of the corresponding Dart field. |
169 | bool is_guarded_field() const { return IsGuardedBit::decode(flags_); } |
170 | |
171 | // Static type of the slots if any. |
172 | // |
173 | // A value that is read from the slot is guaranteed to be assignable to its |
174 | // static type. |
175 | const AbstractType& static_type() const; |
176 | |
177 | // More precise type information about values that can be read from this slot. |
178 | CompileType ComputeCompileType() const; |
179 | |
180 | const Field& field() const { |
181 | ASSERT(IsDartField()); |
182 | ASSERT(data_ != nullptr); |
183 | return *DataAs<const Field>(); |
184 | } |
185 | |
186 | bool Equals(const Slot* other) const; |
187 | intptr_t Hashcode() const; |
188 | |
189 | bool IsIdentical(const Slot& other) const { return this == &other; } |
190 | |
191 | bool IsContextSlot() const { |
192 | return kind() == Kind::kCapturedVariable || kind() == Kind::kContext_parent; |
193 | } |
194 | |
195 | private: |
196 | friend class FlowGraphDeserializer; // For GetNativeSlot. |
197 | |
198 | Slot(Kind kind, |
199 | int8_t bits, |
200 | ClassIdTagType cid, |
201 | intptr_t offset_in_bytes, |
202 | const void* data, |
203 | const AbstractType* static_type) |
204 | : kind_(kind), |
205 | flags_(bits), |
206 | cid_(cid), |
207 | offset_in_bytes_(offset_in_bytes), |
208 | data_(data), |
209 | static_type_(static_type) {} |
210 | |
211 | Slot(const Slot& other) |
212 | : Slot(other.kind_, |
213 | other.flags_, |
214 | other.cid_, |
215 | other.offset_in_bytes_, |
216 | other.data_, |
217 | other.static_type_) {} |
218 | |
219 | using IsImmutableBit = BitField<int8_t, bool, 0, 1>; |
220 | using IsNullableBit = BitField<int8_t, bool, IsImmutableBit::kNextBit, 1>; |
221 | using IsGuardedBit = BitField<int8_t, bool, IsNullableBit::kNextBit, 1>; |
222 | |
223 | template <typename T> |
224 | const T* DataAs() const { |
225 | return static_cast<const T*>(data_); |
226 | } |
227 | |
228 | static const Slot& GetNativeSlot(Kind kind); |
229 | |
230 | const Kind kind_; |
231 | const int8_t flags_; // is_immutable, is_nullable |
232 | const ClassIdTagType cid_; // Concrete cid of a value or kDynamicCid. |
233 | |
234 | const intptr_t offset_in_bytes_; |
235 | |
236 | // Kind dependent data: |
237 | // - name as a Dart String object for local variables; |
238 | // - name as a C string for native slots; |
239 | // - Field object for Dart fields; |
240 | const void* data_; |
241 | |
242 | const AbstractType* static_type_; |
243 | |
244 | friend class SlotCache; |
245 | }; |
246 | |
247 | } // namespace dart |
248 | |
249 | #endif // RUNTIME_VM_COMPILER_BACKEND_SLOT_H_ |
250 | |