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
34namespace dart {
35
36class LocalScope;
37class LocalVariable;
38class 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.
91class 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