1 | // Copyright 2015 the V8 project authors. All rights reserved. |
2 | // Use of this source code is governed by a BSD-style license that can be |
3 | // found in the LICENSE file. |
4 | |
5 | #ifndef V8_COMPILER_CODE_ASSEMBLER_H_ |
6 | #define V8_COMPILER_CODE_ASSEMBLER_H_ |
7 | |
8 | #include <map> |
9 | #include <memory> |
10 | #include <initializer_list> |
11 | |
12 | // Clients of this interface shouldn't depend on lots of compiler internals. |
13 | // Do not include anything from src/compiler here! |
14 | #include "src/allocation.h" |
15 | #include "src/base/macros.h" |
16 | #include "src/builtins/builtins.h" |
17 | #include "src/code-factory.h" |
18 | #include "src/globals.h" |
19 | #include "src/heap/heap.h" |
20 | #include "src/machine-type.h" |
21 | #include "src/objects.h" |
22 | #include "src/objects/arguments.h" |
23 | #include "src/objects/data-handler.h" |
24 | #include "src/objects/heap-number.h" |
25 | #include "src/objects/js-array-buffer.h" |
26 | #include "src/objects/js-collection.h" |
27 | #include "src/objects/js-proxy.h" |
28 | #include "src/objects/map.h" |
29 | #include "src/objects/maybe-object.h" |
30 | #include "src/objects/oddball.h" |
31 | #include "src/runtime/runtime.h" |
32 | #include "src/source-position.h" |
33 | #include "src/type-traits.h" |
34 | #include "src/zone/zone-containers.h" |
35 | |
36 | namespace v8 { |
37 | namespace internal { |
38 | |
39 | // Forward declarations. |
40 | class AsmWasmData; |
41 | class AsyncGeneratorRequest; |
42 | struct AssemblerOptions; |
43 | class BigInt; |
44 | class CallInterfaceDescriptor; |
45 | class Callable; |
46 | class Factory; |
47 | class FinalizationGroupCleanupJobTask; |
48 | class InterpreterData; |
49 | class Isolate; |
50 | class JSAsyncFunctionObject; |
51 | class JSAsyncGeneratorObject; |
52 | class JSCollator; |
53 | class JSCollection; |
54 | class JSDateTimeFormat; |
55 | class JSListFormat; |
56 | class JSLocale; |
57 | class JSNumberFormat; |
58 | class JSPluralRules; |
59 | class JSRegExpStringIterator; |
60 | class JSRelativeTimeFormat; |
61 | class JSSegmentIterator; |
62 | class JSSegmenter; |
63 | class JSV8BreakIterator; |
64 | class JSWeakCollection; |
65 | class JSFinalizationGroup; |
66 | class JSFinalizationGroupCleanupIterator; |
67 | class JSWeakMap; |
68 | class JSWeakRef; |
69 | class JSWeakSet; |
70 | class MaybeObject; |
71 | class PromiseCapability; |
72 | class PromiseFulfillReactionJobTask; |
73 | class PromiseReaction; |
74 | class PromiseReactionJobTask; |
75 | class PromiseRejectReactionJobTask; |
76 | class WasmDebugInfo; |
77 | class WeakCell; |
78 | class Zone; |
79 | |
80 | template <typename T> |
81 | class Signature; |
82 | |
83 | struct UntaggedT {}; |
84 | |
85 | struct IntegralT : UntaggedT {}; |
86 | |
87 | struct WordT : IntegralT { |
88 | static const MachineRepresentation kMachineRepresentation = |
89 | (kSystemPointerSize == 4) ? MachineRepresentation::kWord32 |
90 | : MachineRepresentation::kWord64; |
91 | }; |
92 | |
93 | struct RawPtrT : WordT { |
94 | static constexpr MachineType kMachineType = MachineType::Pointer(); |
95 | }; |
96 | |
97 | template <class To> |
98 | struct RawPtr : RawPtrT {}; |
99 | |
100 | struct Word32T : IntegralT { |
101 | static const MachineRepresentation kMachineRepresentation = |
102 | MachineRepresentation::kWord32; |
103 | }; |
104 | struct Int32T : Word32T { |
105 | static constexpr MachineType kMachineType = MachineType::Int32(); |
106 | }; |
107 | struct Uint32T : Word32T { |
108 | static constexpr MachineType kMachineType = MachineType::Uint32(); |
109 | }; |
110 | struct Int16T : Int32T { |
111 | static constexpr MachineType kMachineType = MachineType::Int16(); |
112 | }; |
113 | struct Uint16T : Uint32T { |
114 | static constexpr MachineType kMachineType = MachineType::Uint16(); |
115 | }; |
116 | struct Int8T : Int16T { |
117 | static constexpr MachineType kMachineType = MachineType::Int8(); |
118 | }; |
119 | struct Uint8T : Uint16T { |
120 | static constexpr MachineType kMachineType = MachineType::Uint8(); |
121 | }; |
122 | |
123 | struct Word64T : IntegralT { |
124 | static const MachineRepresentation kMachineRepresentation = |
125 | MachineRepresentation::kWord64; |
126 | }; |
127 | struct Int64T : Word64T { |
128 | static constexpr MachineType kMachineType = MachineType::Int64(); |
129 | }; |
130 | struct Uint64T : Word64T { |
131 | static constexpr MachineType kMachineType = MachineType::Uint64(); |
132 | }; |
133 | |
134 | struct IntPtrT : WordT { |
135 | static constexpr MachineType kMachineType = MachineType::IntPtr(); |
136 | }; |
137 | struct UintPtrT : WordT { |
138 | static constexpr MachineType kMachineType = MachineType::UintPtr(); |
139 | }; |
140 | |
141 | struct Float32T : UntaggedT { |
142 | static const MachineRepresentation kMachineRepresentation = |
143 | MachineRepresentation::kFloat32; |
144 | static constexpr MachineType kMachineType = MachineType::Float32(); |
145 | }; |
146 | |
147 | struct Float64T : UntaggedT { |
148 | static const MachineRepresentation kMachineRepresentation = |
149 | MachineRepresentation::kFloat64; |
150 | static constexpr MachineType kMachineType = MachineType::Float64(); |
151 | }; |
152 | |
153 | // Result of a comparison operation. |
154 | struct BoolT : Word32T {}; |
155 | |
156 | // Value type of a Turbofan node with two results. |
157 | template <class T1, class T2> |
158 | struct PairT {}; |
159 | |
160 | inline constexpr MachineType CommonMachineType(MachineType type1, |
161 | MachineType type2) { |
162 | return (type1 == type2) ? type1 |
163 | : ((type1.IsTagged() && type2.IsTagged()) |
164 | ? MachineType::AnyTagged() |
165 | : MachineType::None()); |
166 | } |
167 | |
168 | template <class Type, class Enable = void> |
169 | struct MachineTypeOf { |
170 | static constexpr MachineType value = Type::kMachineType; |
171 | }; |
172 | |
173 | template <class Type, class Enable> |
174 | constexpr MachineType MachineTypeOf<Type, Enable>::value; |
175 | |
176 | template <> |
177 | struct MachineTypeOf<Object> { |
178 | static constexpr MachineType value = MachineType::AnyTagged(); |
179 | }; |
180 | template <> |
181 | struct MachineTypeOf<MaybeObject> { |
182 | static constexpr MachineType value = MachineType::AnyTagged(); |
183 | }; |
184 | template <> |
185 | struct MachineTypeOf<Smi> { |
186 | static constexpr MachineType value = MachineType::TaggedSigned(); |
187 | }; |
188 | template <class HeapObjectSubtype> |
189 | struct MachineTypeOf<HeapObjectSubtype, |
190 | typename std::enable_if<std::is_base_of< |
191 | HeapObject, HeapObjectSubtype>::value>::type> { |
192 | static constexpr MachineType value = MachineType::TaggedPointer(); |
193 | }; |
194 | |
195 | template <class HeapObjectSubtype> |
196 | constexpr MachineType MachineTypeOf< |
197 | HeapObjectSubtype, typename std::enable_if<std::is_base_of< |
198 | HeapObject, HeapObjectSubtype>::value>::type>::value; |
199 | |
200 | template <class Type, class Enable = void> |
201 | struct MachineRepresentationOf { |
202 | static const MachineRepresentation value = Type::kMachineRepresentation; |
203 | }; |
204 | template <class T> |
205 | struct MachineRepresentationOf< |
206 | T, typename std::enable_if<std::is_base_of<Object, T>::value>::type> { |
207 | static const MachineRepresentation value = |
208 | MachineTypeOf<T>::value.representation(); |
209 | }; |
210 | template <class T> |
211 | struct MachineRepresentationOf< |
212 | T, typename std::enable_if<std::is_base_of<MaybeObject, T>::value>::type> { |
213 | static const MachineRepresentation value = |
214 | MachineTypeOf<T>::value.representation(); |
215 | }; |
216 | |
217 | template <class T> |
218 | struct is_valid_type_tag { |
219 | static const bool value = std::is_base_of<Object, T>::value || |
220 | std::is_base_of<UntaggedT, T>::value || |
221 | std::is_base_of<MaybeObject, T>::value || |
222 | std::is_same<ExternalReference, T>::value; |
223 | static const bool is_tagged = std::is_base_of<Object, T>::value || |
224 | std::is_base_of<MaybeObject, T>::value; |
225 | }; |
226 | |
227 | template <class T1, class T2> |
228 | struct is_valid_type_tag<PairT<T1, T2>> { |
229 | static const bool value = |
230 | is_valid_type_tag<T1>::value && is_valid_type_tag<T2>::value; |
231 | static const bool is_tagged = false; |
232 | }; |
233 | |
234 | template <class T1, class T2> |
235 | struct UnionT; |
236 | |
237 | template <class T1, class T2> |
238 | struct is_valid_type_tag<UnionT<T1, T2>> { |
239 | static const bool is_tagged = |
240 | is_valid_type_tag<T1>::is_tagged && is_valid_type_tag<T2>::is_tagged; |
241 | static const bool value = is_tagged; |
242 | }; |
243 | |
244 | template <class T1, class T2> |
245 | struct UnionT { |
246 | static constexpr MachineType kMachineType = |
247 | CommonMachineType(MachineTypeOf<T1>::value, MachineTypeOf<T2>::value); |
248 | static const MachineRepresentation kMachineRepresentation = |
249 | kMachineType.representation(); |
250 | static_assert(kMachineRepresentation != MachineRepresentation::kNone, |
251 | "no common representation" ); |
252 | static_assert(is_valid_type_tag<T1>::is_tagged && |
253 | is_valid_type_tag<T2>::is_tagged, |
254 | "union types are only possible for tagged values" ); |
255 | }; |
256 | |
257 | using Number = UnionT<Smi, HeapNumber>; |
258 | using Numeric = UnionT<Number, BigInt>; |
259 | |
260 | // A pointer to a builtin function, used by Torque's function pointers. |
261 | using BuiltinPtr = Smi; |
262 | |
263 | class int31_t { |
264 | public: |
265 | int31_t() : value_(0) {} |
266 | int31_t(int value) : value_(value) { // NOLINT(runtime/explicit) |
267 | DCHECK_EQ((value & 0x80000000) != 0, (value & 0x40000000) != 0); |
268 | } |
269 | int31_t& operator=(int value) { |
270 | DCHECK_EQ((value & 0x80000000) != 0, (value & 0x40000000) != 0); |
271 | value_ = value; |
272 | return *this; |
273 | } |
274 | int32_t value() const { return value_; } |
275 | operator int32_t() const { return value_; } |
276 | |
277 | private: |
278 | int32_t value_; |
279 | }; |
280 | |
281 | #define ENUM_ELEMENT(Name) k##Name, |
282 | #define ENUM_STRUCT_ELEMENT(NAME, Name, name) k##Name, |
283 | enum class ObjectType { |
284 | kObject, |
285 | OBJECT_TYPE_LIST(ENUM_ELEMENT) HEAP_OBJECT_TYPE_LIST(ENUM_ELEMENT) |
286 | STRUCT_LIST(ENUM_STRUCT_ELEMENT) |
287 | }; |
288 | #undef ENUM_ELEMENT |
289 | #undef ENUM_STRUCT_ELEMENT |
290 | |
291 | enum class CheckBounds { kAlways, kDebugOnly }; |
292 | inline bool NeedsBoundsCheck(CheckBounds check_bounds) { |
293 | switch (check_bounds) { |
294 | case CheckBounds::kAlways: |
295 | return true; |
296 | case CheckBounds::kDebugOnly: |
297 | return DEBUG_BOOL; |
298 | } |
299 | } |
300 | |
301 | class AccessCheckNeeded; |
302 | class BigIntWrapper; |
303 | class ClassBoilerplate; |
304 | class BooleanWrapper; |
305 | class CompilationCacheTable; |
306 | class Constructor; |
307 | class Filler; |
308 | class FunctionTemplateRareData; |
309 | class InternalizedString; |
310 | class JSArgumentsObject; |
311 | class JSArrayBufferView; |
312 | class JSContextExtensionObject; |
313 | class JSError; |
314 | class JSSloppyArgumentsObject; |
315 | class MapCache; |
316 | class MutableHeapNumber; |
317 | class NativeContext; |
318 | class NumberWrapper; |
319 | class ScriptWrapper; |
320 | class SloppyArgumentsElements; |
321 | class StringWrapper; |
322 | class SymbolWrapper; |
323 | class Undetectable; |
324 | class UniqueName; |
325 | class WasmExceptionObject; |
326 | class WasmExceptionTag; |
327 | class WasmExportedFunctionData; |
328 | class WasmGlobalObject; |
329 | class WasmMemoryObject; |
330 | class WasmModuleObject; |
331 | class WasmTableObject; |
332 | |
333 | template <class T> |
334 | struct ObjectTypeOf {}; |
335 | |
336 | #define OBJECT_TYPE_CASE(Name) \ |
337 | template <> \ |
338 | struct ObjectTypeOf<Name> { \ |
339 | static const ObjectType value = ObjectType::k##Name; \ |
340 | }; |
341 | #define OBJECT_TYPE_STRUCT_CASE(NAME, Name, name) \ |
342 | template <> \ |
343 | struct ObjectTypeOf<Name> { \ |
344 | static const ObjectType value = ObjectType::k##Name; \ |
345 | }; |
346 | #define OBJECT_TYPE_TEMPLATE_CASE(Name) \ |
347 | template <class... Args> \ |
348 | struct ObjectTypeOf<Name<Args...>> { \ |
349 | static const ObjectType value = ObjectType::k##Name; \ |
350 | }; |
351 | OBJECT_TYPE_CASE(Object) |
352 | OBJECT_TYPE_LIST(OBJECT_TYPE_CASE) |
353 | HEAP_OBJECT_ORDINARY_TYPE_LIST(OBJECT_TYPE_CASE) |
354 | STRUCT_LIST(OBJECT_TYPE_STRUCT_CASE) |
355 | HEAP_OBJECT_TEMPLATE_TYPE_LIST(OBJECT_TYPE_TEMPLATE_CASE) |
356 | #undef OBJECT_TYPE_CASE |
357 | #undef OBJECT_TYPE_STRUCT_CASE |
358 | #undef OBJECT_TYPE_TEMPLATE_CASE |
359 | |
360 | // {raw_value} must be a tagged Object. |
361 | // {raw_type} must be a tagged Smi. |
362 | // {raw_location} must be a tagged String. |
363 | // Returns a tagged Smi. |
364 | Address CheckObjectType(Address raw_value, Address raw_type, |
365 | Address raw_location); |
366 | |
367 | namespace compiler { |
368 | |
369 | class CallDescriptor; |
370 | class CodeAssemblerLabel; |
371 | class CodeAssemblerVariable; |
372 | template <class T> |
373 | class TypedCodeAssemblerVariable; |
374 | class CodeAssemblerState; |
375 | class Node; |
376 | class RawMachineAssembler; |
377 | class RawMachineLabel; |
378 | class SourcePositionTable; |
379 | |
380 | using CodeAssemblerVariableList = ZoneVector<CodeAssemblerVariable*>; |
381 | |
382 | using CodeAssemblerCallback = std::function<void()>; |
383 | |
384 | template <class T, class U> |
385 | struct is_subtype { |
386 | static const bool value = std::is_base_of<U, T>::value; |
387 | }; |
388 | template <class T1, class T2, class U> |
389 | struct is_subtype<UnionT<T1, T2>, U> { |
390 | static const bool value = |
391 | is_subtype<T1, U>::value && is_subtype<T2, U>::value; |
392 | }; |
393 | template <class T, class U1, class U2> |
394 | struct is_subtype<T, UnionT<U1, U2>> { |
395 | static const bool value = |
396 | is_subtype<T, U1>::value || is_subtype<T, U2>::value; |
397 | }; |
398 | template <class T1, class T2, class U1, class U2> |
399 | struct is_subtype<UnionT<T1, T2>, UnionT<U1, U2>> { |
400 | static const bool value = |
401 | (is_subtype<T1, U1>::value || is_subtype<T1, U2>::value) && |
402 | (is_subtype<T2, U1>::value || is_subtype<T2, U2>::value); |
403 | }; |
404 | |
405 | template <class T, class U> |
406 | struct types_have_common_values { |
407 | static const bool value = is_subtype<T, U>::value || is_subtype<U, T>::value; |
408 | }; |
409 | template <class U> |
410 | struct types_have_common_values<Uint32T, U> { |
411 | static const bool value = types_have_common_values<Word32T, U>::value; |
412 | }; |
413 | template <class U> |
414 | struct types_have_common_values<Int32T, U> { |
415 | static const bool value = types_have_common_values<Word32T, U>::value; |
416 | }; |
417 | template <class U> |
418 | struct types_have_common_values<Uint64T, U> { |
419 | static const bool value = types_have_common_values<Word64T, U>::value; |
420 | }; |
421 | template <class U> |
422 | struct types_have_common_values<Int64T, U> { |
423 | static const bool value = types_have_common_values<Word64T, U>::value; |
424 | }; |
425 | template <class U> |
426 | struct types_have_common_values<IntPtrT, U> { |
427 | static const bool value = types_have_common_values<WordT, U>::value; |
428 | }; |
429 | template <class U> |
430 | struct types_have_common_values<UintPtrT, U> { |
431 | static const bool value = types_have_common_values<WordT, U>::value; |
432 | }; |
433 | template <class T1, class T2, class U> |
434 | struct types_have_common_values<UnionT<T1, T2>, U> { |
435 | static const bool value = types_have_common_values<T1, U>::value || |
436 | types_have_common_values<T2, U>::value; |
437 | }; |
438 | |
439 | template <class T, class U1, class U2> |
440 | struct types_have_common_values<T, UnionT<U1, U2>> { |
441 | static const bool value = types_have_common_values<T, U1>::value || |
442 | types_have_common_values<T, U2>::value; |
443 | }; |
444 | template <class T1, class T2, class U1, class U2> |
445 | struct types_have_common_values<UnionT<T1, T2>, UnionT<U1, U2>> { |
446 | static const bool value = types_have_common_values<T1, U1>::value || |
447 | types_have_common_values<T1, U2>::value || |
448 | types_have_common_values<T2, U1>::value || |
449 | types_have_common_values<T2, U2>::value; |
450 | }; |
451 | |
452 | template <class T> |
453 | struct types_have_common_values<T, MaybeObject> { |
454 | static const bool value = types_have_common_values<T, Object>::value; |
455 | }; |
456 | |
457 | template <class T> |
458 | struct types_have_common_values<MaybeObject, T> { |
459 | static const bool value = types_have_common_values<Object, T>::value; |
460 | }; |
461 | |
462 | // TNode<T> is an SSA value with the static type tag T, which is one of the |
463 | // following: |
464 | // - a subclass of internal::Object represents a tagged type |
465 | // - a subclass of internal::UntaggedT represents an untagged type |
466 | // - ExternalReference |
467 | // - PairT<T1, T2> for an operation returning two values, with types T1 |
468 | // and T2 |
469 | // - UnionT<T1, T2> represents either a value of type T1 or of type T2. |
470 | template <class T> |
471 | class TNode { |
472 | public: |
473 | static_assert(is_valid_type_tag<T>::value, "invalid type tag" ); |
474 | |
475 | template <class U, |
476 | typename std::enable_if<is_subtype<U, T>::value, int>::type = 0> |
477 | TNode(const TNode<U>& other) : node_(other) {} |
478 | TNode() : node_(nullptr) {} |
479 | |
480 | TNode operator=(TNode other) { |
481 | DCHECK_NOT_NULL(other.node_); |
482 | node_ = other.node_; |
483 | return *this; |
484 | } |
485 | |
486 | operator compiler::Node*() const { return node_; } |
487 | |
488 | static TNode UncheckedCast(compiler::Node* node) { return TNode(node); } |
489 | |
490 | protected: |
491 | explicit TNode(compiler::Node* node) : node_(node) {} |
492 | |
493 | private: |
494 | compiler::Node* node_; |
495 | }; |
496 | |
497 | // SloppyTNode<T> is a variant of TNode<T> and allows implicit casts from |
498 | // Node*. It is intended for function arguments as long as some call sites |
499 | // still use untyped Node* arguments. |
500 | // TODO(tebbi): Delete this class once transition is finished. |
501 | template <class T> |
502 | class SloppyTNode : public TNode<T> { |
503 | public: |
504 | SloppyTNode(compiler::Node* node) // NOLINT(runtime/explicit) |
505 | : TNode<T>(node) {} |
506 | template <class U, typename std::enable_if<is_subtype<U, T>::value, |
507 | int>::type = 0> |
508 | SloppyTNode(const TNode<U>& other) // NOLINT(runtime/explicit) |
509 | : TNode<T>(other) {} |
510 | }; |
511 | |
512 | template <class... Types> |
513 | class CodeAssemblerParameterizedLabel; |
514 | |
515 | // This macro alias allows to use PairT<T1, T2> as a macro argument. |
516 | #define PAIR_TYPE(T1, T2) PairT<T1, T2> |
517 | |
518 | #define CODE_ASSEMBLER_COMPARE_BINARY_OP_LIST(V) \ |
519 | V(Float32Equal, BoolT, Float32T, Float32T) \ |
520 | V(Float32LessThan, BoolT, Float32T, Float32T) \ |
521 | V(Float32LessThanOrEqual, BoolT, Float32T, Float32T) \ |
522 | V(Float32GreaterThan, BoolT, Float32T, Float32T) \ |
523 | V(Float32GreaterThanOrEqual, BoolT, Float32T, Float32T) \ |
524 | V(Float64Equal, BoolT, Float64T, Float64T) \ |
525 | V(Float64NotEqual, BoolT, Float64T, Float64T) \ |
526 | V(Float64LessThan, BoolT, Float64T, Float64T) \ |
527 | V(Float64LessThanOrEqual, BoolT, Float64T, Float64T) \ |
528 | V(Float64GreaterThan, BoolT, Float64T, Float64T) \ |
529 | V(Float64GreaterThanOrEqual, BoolT, Float64T, Float64T) \ |
530 | /* Use Word32Equal if you need Int32Equal */ \ |
531 | V(Int32GreaterThan, BoolT, Word32T, Word32T) \ |
532 | V(Int32GreaterThanOrEqual, BoolT, Word32T, Word32T) \ |
533 | V(Int32LessThan, BoolT, Word32T, Word32T) \ |
534 | V(Int32LessThanOrEqual, BoolT, Word32T, Word32T) \ |
535 | /* Use WordEqual if you need IntPtrEqual */ \ |
536 | V(IntPtrLessThan, BoolT, WordT, WordT) \ |
537 | V(IntPtrLessThanOrEqual, BoolT, WordT, WordT) \ |
538 | V(IntPtrGreaterThan, BoolT, WordT, WordT) \ |
539 | V(IntPtrGreaterThanOrEqual, BoolT, WordT, WordT) \ |
540 | /* Use Word32Equal if you need Uint32Equal */ \ |
541 | V(Uint32LessThan, BoolT, Word32T, Word32T) \ |
542 | V(Uint32LessThanOrEqual, BoolT, Word32T, Word32T) \ |
543 | V(Uint32GreaterThan, BoolT, Word32T, Word32T) \ |
544 | V(Uint32GreaterThanOrEqual, BoolT, Word32T, Word32T) \ |
545 | /* Use WordEqual if you need UintPtrEqual */ \ |
546 | V(UintPtrLessThan, BoolT, WordT, WordT) \ |
547 | V(UintPtrLessThanOrEqual, BoolT, WordT, WordT) \ |
548 | V(UintPtrGreaterThan, BoolT, WordT, WordT) \ |
549 | V(UintPtrGreaterThanOrEqual, BoolT, WordT, WordT) |
550 | |
551 | #define CODE_ASSEMBLER_BINARY_OP_LIST(V) \ |
552 | CODE_ASSEMBLER_COMPARE_BINARY_OP_LIST(V) \ |
553 | V(Float64Add, Float64T, Float64T, Float64T) \ |
554 | V(Float64Sub, Float64T, Float64T, Float64T) \ |
555 | V(Float64Mul, Float64T, Float64T, Float64T) \ |
556 | V(Float64Div, Float64T, Float64T, Float64T) \ |
557 | V(Float64Mod, Float64T, Float64T, Float64T) \ |
558 | V(Float64Atan2, Float64T, Float64T, Float64T) \ |
559 | V(Float64Pow, Float64T, Float64T, Float64T) \ |
560 | V(Float64Max, Float64T, Float64T, Float64T) \ |
561 | V(Float64Min, Float64T, Float64T, Float64T) \ |
562 | V(Float64InsertLowWord32, Float64T, Float64T, Word32T) \ |
563 | V(Float64InsertHighWord32, Float64T, Float64T, Word32T) \ |
564 | V(IntPtrAddWithOverflow, PAIR_TYPE(IntPtrT, BoolT), IntPtrT, IntPtrT) \ |
565 | V(IntPtrSubWithOverflow, PAIR_TYPE(IntPtrT, BoolT), IntPtrT, IntPtrT) \ |
566 | V(Int32Add, Word32T, Word32T, Word32T) \ |
567 | V(Int32AddWithOverflow, PAIR_TYPE(Int32T, BoolT), Int32T, Int32T) \ |
568 | V(Int32Sub, Word32T, Word32T, Word32T) \ |
569 | V(Int32SubWithOverflow, PAIR_TYPE(Int32T, BoolT), Int32T, Int32T) \ |
570 | V(Int32Mul, Word32T, Word32T, Word32T) \ |
571 | V(Int32MulWithOverflow, PAIR_TYPE(Int32T, BoolT), Int32T, Int32T) \ |
572 | V(Int32Div, Int32T, Int32T, Int32T) \ |
573 | V(Int32Mod, Int32T, Int32T, Int32T) \ |
574 | V(WordRor, WordT, WordT, IntegralT) \ |
575 | V(Word32Ror, Word32T, Word32T, Word32T) \ |
576 | V(Word64Ror, Word64T, Word64T, Word64T) |
577 | |
578 | TNode<Float64T> Float64Add(TNode<Float64T> a, TNode<Float64T> b); |
579 | |
580 | #define CODE_ASSEMBLER_UNARY_OP_LIST(V) \ |
581 | V(Float64Abs, Float64T, Float64T) \ |
582 | V(Float64Acos, Float64T, Float64T) \ |
583 | V(Float64Acosh, Float64T, Float64T) \ |
584 | V(Float64Asin, Float64T, Float64T) \ |
585 | V(Float64Asinh, Float64T, Float64T) \ |
586 | V(Float64Atan, Float64T, Float64T) \ |
587 | V(Float64Atanh, Float64T, Float64T) \ |
588 | V(Float64Cos, Float64T, Float64T) \ |
589 | V(Float64Cosh, Float64T, Float64T) \ |
590 | V(Float64Exp, Float64T, Float64T) \ |
591 | V(Float64Expm1, Float64T, Float64T) \ |
592 | V(Float64Log, Float64T, Float64T) \ |
593 | V(Float64Log1p, Float64T, Float64T) \ |
594 | V(Float64Log2, Float64T, Float64T) \ |
595 | V(Float64Log10, Float64T, Float64T) \ |
596 | V(Float64Cbrt, Float64T, Float64T) \ |
597 | V(Float64Neg, Float64T, Float64T) \ |
598 | V(Float64Sin, Float64T, Float64T) \ |
599 | V(Float64Sinh, Float64T, Float64T) \ |
600 | V(Float64Sqrt, Float64T, Float64T) \ |
601 | V(Float64Tan, Float64T, Float64T) \ |
602 | V(Float64Tanh, Float64T, Float64T) \ |
603 | V(, Word32T, Float64T) \ |
604 | V(, Word32T, Float64T) \ |
605 | V(BitcastTaggedToWord, IntPtrT, Object) \ |
606 | V(BitcastMaybeObjectToWord, IntPtrT, MaybeObject) \ |
607 | V(BitcastWordToTagged, Object, WordT) \ |
608 | V(BitcastWordToTaggedSigned, Smi, WordT) \ |
609 | V(TruncateFloat64ToFloat32, Float32T, Float64T) \ |
610 | V(TruncateFloat64ToWord32, Word32T, Float64T) \ |
611 | V(TruncateInt64ToInt32, Int32T, Int64T) \ |
612 | V(ChangeFloat32ToFloat64, Float64T, Float32T) \ |
613 | V(ChangeFloat64ToUint32, Uint32T, Float64T) \ |
614 | V(ChangeFloat64ToUint64, Uint64T, Float64T) \ |
615 | V(ChangeInt32ToFloat64, Float64T, Int32T) \ |
616 | V(ChangeInt32ToInt64, Int64T, Int32T) \ |
617 | V(ChangeUint32ToFloat64, Float64T, Word32T) \ |
618 | V(ChangeUint32ToUint64, Uint64T, Word32T) \ |
619 | V(BitcastInt32ToFloat32, Float32T, Word32T) \ |
620 | V(BitcastFloat32ToInt32, Word32T, Float32T) \ |
621 | V(RoundFloat64ToInt32, Int32T, Float64T) \ |
622 | V(RoundInt32ToFloat32, Int32T, Float32T) \ |
623 | V(Float64SilenceNaN, Float64T, Float64T) \ |
624 | V(Float64RoundDown, Float64T, Float64T) \ |
625 | V(Float64RoundUp, Float64T, Float64T) \ |
626 | V(Float64RoundTiesEven, Float64T, Float64T) \ |
627 | V(Float64RoundTruncate, Float64T, Float64T) \ |
628 | V(Word32Clz, Int32T, Word32T) \ |
629 | V(Word32BitwiseNot, Word32T, Word32T) \ |
630 | V(WordNot, WordT, WordT) \ |
631 | V(Int32AbsWithOverflow, PAIR_TYPE(Int32T, BoolT), Int32T) \ |
632 | V(Int64AbsWithOverflow, PAIR_TYPE(Int64T, BoolT), Int64T) \ |
633 | V(IntPtrAbsWithOverflow, PAIR_TYPE(IntPtrT, BoolT), IntPtrT) \ |
634 | V(Word32BinaryNot, BoolT, Word32T) |
635 | |
636 | // A "public" interface used by components outside of compiler directory to |
637 | // create code objects with TurboFan's backend. This class is mostly a thin |
638 | // shim around the RawMachineAssembler, and its primary job is to ensure that |
639 | // the innards of the RawMachineAssembler and other compiler implementation |
640 | // details don't leak outside of the the compiler directory.. |
641 | // |
642 | // V8 components that need to generate low-level code using this interface |
643 | // should include this header--and this header only--from the compiler |
644 | // directory (this is actually enforced). Since all interesting data |
645 | // structures are forward declared, it's not possible for clients to peek |
646 | // inside the compiler internals. |
647 | // |
648 | // In addition to providing isolation between TurboFan and code generation |
649 | // clients, CodeAssembler also provides an abstraction for creating variables |
650 | // and enhanced Label functionality to merge variable values along paths where |
651 | // they have differing values, including loops. |
652 | // |
653 | // The CodeAssembler itself is stateless (and instances are expected to be |
654 | // temporary-scoped and short-lived); all its state is encapsulated into |
655 | // a CodeAssemblerState instance. |
656 | class V8_EXPORT_PRIVATE CodeAssembler { |
657 | public: |
658 | explicit CodeAssembler(CodeAssemblerState* state) : state_(state) {} |
659 | ~CodeAssembler(); |
660 | |
661 | static Handle<Code> GenerateCode(CodeAssemblerState* state, |
662 | const AssemblerOptions& options); |
663 | |
664 | bool Is64() const; |
665 | bool IsFloat64RoundUpSupported() const; |
666 | bool IsFloat64RoundDownSupported() const; |
667 | bool IsFloat64RoundTiesEvenSupported() const; |
668 | bool IsFloat64RoundTruncateSupported() const; |
669 | bool IsInt32AbsWithOverflowSupported() const; |
670 | bool IsInt64AbsWithOverflowSupported() const; |
671 | bool IsIntPtrAbsWithOverflowSupported() const; |
672 | |
673 | // Shortened aliases for use in CodeAssembler subclasses. |
674 | using Label = CodeAssemblerLabel; |
675 | using Variable = CodeAssemblerVariable; |
676 | template <class T> |
677 | using TVariable = TypedCodeAssemblerVariable<T>; |
678 | using VariableList = CodeAssemblerVariableList; |
679 | |
680 | // =========================================================================== |
681 | // Base Assembler |
682 | // =========================================================================== |
683 | |
684 | template <class PreviousType, bool FromTyped> |
685 | class CheckedNode { |
686 | public: |
687 | #ifdef DEBUG |
688 | CheckedNode(Node* node, CodeAssembler* code_assembler, const char* location) |
689 | : node_(node), code_assembler_(code_assembler), location_(location) {} |
690 | #else |
691 | CheckedNode(compiler::Node* node, CodeAssembler*, const char*) |
692 | : node_(node) {} |
693 | #endif |
694 | |
695 | template <class A> |
696 | operator TNode<A>() { |
697 | static_assert( |
698 | !std::is_same<A, MaybeObject>::value, |
699 | "Can't cast to MaybeObject, use explicit conversion functions. " ); |
700 | |
701 | static_assert(types_have_common_values<A, PreviousType>::value, |
702 | "Incompatible types: this cast can never succeed." ); |
703 | static_assert(std::is_convertible<TNode<A>, TNode<Object>>::value, |
704 | "Coercion to untagged values cannot be " |
705 | "checked." ); |
706 | static_assert( |
707 | !FromTyped || |
708 | !std::is_convertible<TNode<PreviousType>, TNode<A>>::value, |
709 | "Unnecessary CAST: types are convertible." ); |
710 | #ifdef DEBUG |
711 | if (FLAG_debug_code) { |
712 | if (std::is_same<PreviousType, MaybeObject>::value) { |
713 | code_assembler_->GenerateCheckMaybeObjectIsObject(node_, location_); |
714 | } |
715 | Node* function = code_assembler_->ExternalConstant( |
716 | ExternalReference::check_object_type()); |
717 | code_assembler_->CallCFunction( |
718 | function, MachineType::AnyTagged(), |
719 | std::make_pair(MachineType::AnyTagged(), node_), |
720 | std::make_pair(MachineType::TaggedSigned(), |
721 | code_assembler_->SmiConstant( |
722 | static_cast<int>(ObjectTypeOf<A>::value))), |
723 | std::make_pair(MachineType::AnyTagged(), |
724 | code_assembler_->StringConstant(location_))); |
725 | } |
726 | #endif |
727 | return TNode<A>::UncheckedCast(node_); |
728 | } |
729 | |
730 | template <class A> |
731 | operator SloppyTNode<A>() { |
732 | return implicit_cast<TNode<A>>(*this); |
733 | } |
734 | |
735 | Node* node() const { return node_; } |
736 | |
737 | private: |
738 | Node* node_; |
739 | #ifdef DEBUG |
740 | CodeAssembler* code_assembler_; |
741 | const char* location_; |
742 | #endif |
743 | }; |
744 | |
745 | template <class T> |
746 | TNode<T> UncheckedCast(Node* value) { |
747 | return TNode<T>::UncheckedCast(value); |
748 | } |
749 | template <class T, class U> |
750 | TNode<T> UncheckedCast(TNode<U> value) { |
751 | static_assert(types_have_common_values<T, U>::value, |
752 | "Incompatible types: this cast can never succeed." ); |
753 | return TNode<T>::UncheckedCast(value); |
754 | } |
755 | |
756 | // ReinterpretCast<T>(v) has the power to cast even when the type of v is |
757 | // unrelated to T. Use with care. |
758 | template <class T> |
759 | TNode<T> ReinterpretCast(Node* value) { |
760 | return TNode<T>::UncheckedCast(value); |
761 | } |
762 | |
763 | CheckedNode<Object, false> Cast(Node* value, const char* location = "" ) { |
764 | return {value, this, location}; |
765 | } |
766 | |
767 | template <class T> |
768 | CheckedNode<T, true> Cast(TNode<T> value, const char* location = "" ) { |
769 | return {value, this, location}; |
770 | } |
771 | |
772 | #ifdef DEBUG |
773 | #define STRINGIFY(x) #x |
774 | #define TO_STRING_LITERAL(x) STRINGIFY(x) |
775 | #define CAST(x) \ |
776 | Cast(x, "CAST(" #x ") at " __FILE__ ":" TO_STRING_LITERAL(__LINE__)) |
777 | #define TORQUE_CAST(x) \ |
778 | ca_.Cast(x, "CAST(" #x ") at " __FILE__ ":" TO_STRING_LITERAL(__LINE__)) |
779 | #else |
780 | #define CAST(x) Cast(x) |
781 | #define TORQUE_CAST(x) ca_.Cast(x) |
782 | #endif |
783 | |
784 | #ifdef DEBUG |
785 | void GenerateCheckMaybeObjectIsObject(Node* node, const char* location); |
786 | #endif |
787 | |
788 | // Constants. |
789 | TNode<Int32T> Int32Constant(int32_t value); |
790 | TNode<Int64T> Int64Constant(int64_t value); |
791 | TNode<IntPtrT> IntPtrConstant(intptr_t value); |
792 | TNode<Uint32T> Uint32Constant(uint32_t value) { |
793 | return Unsigned(Int32Constant(bit_cast<int32_t>(value))); |
794 | } |
795 | TNode<UintPtrT> UintPtrConstant(uintptr_t value) { |
796 | return Unsigned(IntPtrConstant(bit_cast<intptr_t>(value))); |
797 | } |
798 | TNode<Number> NumberConstant(double value); |
799 | TNode<Smi> SmiConstant(Smi value); |
800 | TNode<Smi> SmiConstant(int value); |
801 | template <typename E, |
802 | typename = typename std::enable_if<std::is_enum<E>::value>::type> |
803 | TNode<Smi> SmiConstant(E value) { |
804 | STATIC_ASSERT(sizeof(E) <= sizeof(int)); |
805 | return SmiConstant(static_cast<int>(value)); |
806 | } |
807 | TNode<HeapObject> UntypedHeapConstant(Handle<HeapObject> object); |
808 | template <class Type> |
809 | TNode<Type> HeapConstant(Handle<Type> object) { |
810 | return UncheckedCast<Type>(UntypedHeapConstant(object)); |
811 | } |
812 | TNode<String> StringConstant(const char* str); |
813 | TNode<Oddball> BooleanConstant(bool value); |
814 | TNode<ExternalReference> ExternalConstant(ExternalReference address); |
815 | TNode<Float64T> Float64Constant(double value); |
816 | TNode<HeapNumber> NaNConstant(); |
817 | TNode<BoolT> Int32TrueConstant() { |
818 | return ReinterpretCast<BoolT>(Int32Constant(1)); |
819 | } |
820 | TNode<BoolT> Int32FalseConstant() { |
821 | return ReinterpretCast<BoolT>(Int32Constant(0)); |
822 | } |
823 | TNode<BoolT> BoolConstant(bool value) { |
824 | return value ? Int32TrueConstant() : Int32FalseConstant(); |
825 | } |
826 | |
827 | // TODO(jkummerow): The style guide wants pointers for output parameters. |
828 | // https://google.github.io/styleguide/cppguide.html#Output_Parameters |
829 | bool ToInt32Constant(Node* node, int32_t& out_value); |
830 | bool ToInt64Constant(Node* node, int64_t& out_value); |
831 | bool ToSmiConstant(Node* node, Smi* out_value); |
832 | bool ToIntPtrConstant(Node* node, intptr_t& out_value); |
833 | |
834 | bool IsUndefinedConstant(TNode<Object> node); |
835 | bool IsNullConstant(TNode<Object> node); |
836 | |
837 | TNode<Int32T> Signed(TNode<Word32T> x) { return UncheckedCast<Int32T>(x); } |
838 | TNode<IntPtrT> Signed(TNode<WordT> x) { return UncheckedCast<IntPtrT>(x); } |
839 | TNode<Uint32T> Unsigned(TNode<Word32T> x) { |
840 | return UncheckedCast<Uint32T>(x); |
841 | } |
842 | TNode<UintPtrT> Unsigned(TNode<WordT> x) { |
843 | return UncheckedCast<UintPtrT>(x); |
844 | } |
845 | |
846 | static constexpr int kTargetParameterIndex = -1; |
847 | |
848 | Node* Parameter(int value); |
849 | |
850 | TNode<Context> GetJSContextParameter(); |
851 | void Return(SloppyTNode<Object> value); |
852 | void Return(SloppyTNode<Object> value1, SloppyTNode<Object> value2); |
853 | void Return(SloppyTNode<Object> value1, SloppyTNode<Object> value2, |
854 | SloppyTNode<Object> value3); |
855 | void PopAndReturn(Node* pop, Node* value); |
856 | |
857 | void ReturnIf(Node* condition, Node* value); |
858 | |
859 | void ReturnRaw(Node* value); |
860 | |
861 | void DebugAbort(Node* message); |
862 | void DebugBreak(); |
863 | void Unreachable(); |
864 | void (const char* msg) { |
865 | if (!FLAG_code_comments) return; |
866 | Comment(std::string(msg)); |
867 | } |
868 | void (std::string msg); |
869 | template <class... Args> |
870 | void (Args&&... args) { |
871 | if (!FLAG_code_comments) return; |
872 | std::ostringstream s; |
873 | USE((s << std::forward<Args>(args))...); |
874 | Comment(s.str()); |
875 | } |
876 | |
877 | void SetSourcePosition(const char* file, int line); |
878 | |
879 | void Bind(Label* label); |
880 | #if DEBUG |
881 | void Bind(Label* label, AssemblerDebugInfo debug_info); |
882 | #endif // DEBUG |
883 | void Goto(Label* label); |
884 | void GotoIf(SloppyTNode<IntegralT> condition, Label* true_label); |
885 | void GotoIfNot(SloppyTNode<IntegralT> condition, Label* false_label); |
886 | void Branch(SloppyTNode<IntegralT> condition, Label* true_label, |
887 | Label* false_label); |
888 | |
889 | template <class T> |
890 | TNode<T> Uninitialized() { |
891 | return {}; |
892 | } |
893 | |
894 | template <class... T> |
895 | void Bind(CodeAssemblerParameterizedLabel<T...>* label, TNode<T>*... phis) { |
896 | Bind(label->plain_label()); |
897 | label->CreatePhis(phis...); |
898 | } |
899 | template <class... T, class... Args> |
900 | void Branch(TNode<BoolT> condition, |
901 | CodeAssemblerParameterizedLabel<T...>* if_true, |
902 | CodeAssemblerParameterizedLabel<T...>* if_false, Args... args) { |
903 | if_true->AddInputs(args...); |
904 | if_false->AddInputs(args...); |
905 | Branch(condition, if_true->plain_label(), if_false->plain_label()); |
906 | } |
907 | |
908 | template <class... T, class... Args> |
909 | void Goto(CodeAssemblerParameterizedLabel<T...>* label, Args... args) { |
910 | label->AddInputs(args...); |
911 | Goto(label->plain_label()); |
912 | } |
913 | |
914 | void Branch(TNode<BoolT> condition, const std::function<void()>& true_body, |
915 | const std::function<void()>& false_body); |
916 | void Branch(TNode<BoolT> condition, Label* true_label, |
917 | const std::function<void()>& false_body); |
918 | void Branch(TNode<BoolT> condition, const std::function<void()>& true_body, |
919 | Label* false_label); |
920 | |
921 | void Switch(Node* index, Label* default_label, const int32_t* case_values, |
922 | Label** case_labels, size_t case_count); |
923 | |
924 | // Access to the frame pointer |
925 | Node* LoadFramePointer(); |
926 | Node* LoadParentFramePointer(); |
927 | |
928 | // Access to the stack pointer |
929 | Node* LoadStackPointer(); |
930 | |
931 | // Poison |value| on speculative paths. |
932 | TNode<Object> TaggedPoisonOnSpeculation(SloppyTNode<Object> value); |
933 | TNode<WordT> WordPoisonOnSpeculation(SloppyTNode<WordT> value); |
934 | |
935 | // Load raw memory location. |
936 | Node* Load(MachineType rep, Node* base, |
937 | LoadSensitivity needs_poisoning = LoadSensitivity::kSafe); |
938 | template <class Type> |
939 | TNode<Type> Load(MachineType rep, TNode<RawPtr<Type>> base) { |
940 | DCHECK( |
941 | IsSubtype(rep.representation(), MachineRepresentationOf<Type>::value)); |
942 | return UncheckedCast<Type>(Load(rep, static_cast<Node*>(base))); |
943 | } |
944 | Node* Load(MachineType rep, Node* base, Node* offset, |
945 | LoadSensitivity needs_poisoning = LoadSensitivity::kSafe); |
946 | Node* AtomicLoad(MachineType rep, Node* base, Node* offset); |
947 | // Load uncompressed tagged value from (most likely off JS heap) memory |
948 | // location. |
949 | Node* LoadFullTagged( |
950 | Node* base, LoadSensitivity needs_poisoning = LoadSensitivity::kSafe); |
951 | Node* LoadFullTagged( |
952 | Node* base, Node* offset, |
953 | LoadSensitivity needs_poisoning = LoadSensitivity::kSafe); |
954 | |
955 | // Load a value from the root array. |
956 | TNode<Object> LoadRoot(RootIndex root_index); |
957 | |
958 | // Store value to raw memory location. |
959 | Node* Store(Node* base, Node* value); |
960 | Node* Store(Node* base, Node* offset, Node* value); |
961 | Node* StoreEphemeronKey(Node* base, Node* offset, Node* value); |
962 | Node* StoreNoWriteBarrier(MachineRepresentation rep, Node* base, Node* value); |
963 | Node* StoreNoWriteBarrier(MachineRepresentation rep, Node* base, Node* offset, |
964 | Node* value); |
965 | // Stores uncompressed tagged value to (most likely off JS heap) memory |
966 | // location without write barrier. |
967 | Node* StoreFullTaggedNoWriteBarrier(Node* base, Node* tagged_value); |
968 | Node* StoreFullTaggedNoWriteBarrier(Node* base, Node* offset, |
969 | Node* tagged_value); |
970 | |
971 | // Optimized memory operations that map to Turbofan simplified nodes. |
972 | TNode<HeapObject> OptimizedAllocate(TNode<IntPtrT> size, |
973 | AllocationType allocation); |
974 | void OptimizedStoreField(MachineRepresentation rep, TNode<HeapObject> object, |
975 | int offset, Node* value, |
976 | WriteBarrierKind write_barrier); |
977 | void OptimizedStoreMap(TNode<HeapObject> object, TNode<Map>); |
978 | // {value_high} is used for 64-bit stores on 32-bit platforms, must be |
979 | // nullptr in other cases. |
980 | Node* AtomicStore(MachineRepresentation rep, Node* base, Node* offset, |
981 | Node* value, Node* value_high = nullptr); |
982 | |
983 | // Exchange value at raw memory location |
984 | Node* AtomicExchange(MachineType type, Node* base, Node* offset, Node* value, |
985 | Node* value_high = nullptr); |
986 | |
987 | // Compare and Exchange value at raw memory location |
988 | Node* AtomicCompareExchange(MachineType type, Node* base, Node* offset, |
989 | Node* old_value, Node* new_value, |
990 | Node* old_value_high = nullptr, |
991 | Node* new_value_high = nullptr); |
992 | |
993 | Node* AtomicAdd(MachineType type, Node* base, Node* offset, Node* value, |
994 | Node* value_high = nullptr); |
995 | |
996 | Node* AtomicSub(MachineType type, Node* base, Node* offset, Node* value, |
997 | Node* value_high = nullptr); |
998 | |
999 | Node* AtomicAnd(MachineType type, Node* base, Node* offset, Node* value, |
1000 | Node* value_high = nullptr); |
1001 | |
1002 | Node* AtomicOr(MachineType type, Node* base, Node* offset, Node* value, |
1003 | Node* value_high = nullptr); |
1004 | |
1005 | Node* AtomicXor(MachineType type, Node* base, Node* offset, Node* value, |
1006 | Node* value_high = nullptr); |
1007 | |
1008 | // Store a value to the root array. |
1009 | Node* StoreRoot(RootIndex root_index, Node* value); |
1010 | |
1011 | // Basic arithmetic operations. |
1012 | #define DECLARE_CODE_ASSEMBLER_BINARY_OP(name, ResType, Arg1Type, Arg2Type) \ |
1013 | TNode<ResType> name(SloppyTNode<Arg1Type> a, SloppyTNode<Arg2Type> b); |
1014 | CODE_ASSEMBLER_BINARY_OP_LIST(DECLARE_CODE_ASSEMBLER_BINARY_OP) |
1015 | #undef DECLARE_CODE_ASSEMBLER_BINARY_OP |
1016 | |
1017 | TNode<IntPtrT> WordShr(TNode<IntPtrT> left, TNode<IntegralT> right) { |
1018 | return UncheckedCast<IntPtrT>( |
1019 | WordShr(static_cast<Node*>(left), static_cast<Node*>(right))); |
1020 | } |
1021 | TNode<IntPtrT> WordSar(TNode<IntPtrT> left, TNode<IntegralT> right) { |
1022 | return UncheckedCast<IntPtrT>( |
1023 | WordSar(static_cast<Node*>(left), static_cast<Node*>(right))); |
1024 | } |
1025 | |
1026 | TNode<IntPtrT> WordAnd(TNode<IntPtrT> left, TNode<IntPtrT> right) { |
1027 | return UncheckedCast<IntPtrT>( |
1028 | WordAnd(static_cast<Node*>(left), static_cast<Node*>(right))); |
1029 | } |
1030 | |
1031 | template <class Left, class Right, |
1032 | class = typename std::enable_if< |
1033 | std::is_base_of<Object, Left>::value && |
1034 | std::is_base_of<Object, Right>::value>::type> |
1035 | TNode<BoolT> WordEqual(TNode<Left> left, TNode<Right> right) { |
1036 | return WordEqual(ReinterpretCast<WordT>(left), |
1037 | ReinterpretCast<WordT>(right)); |
1038 | } |
1039 | TNode<BoolT> WordEqual(TNode<Object> left, Node* right) { |
1040 | return WordEqual(ReinterpretCast<WordT>(left), |
1041 | ReinterpretCast<WordT>(right)); |
1042 | } |
1043 | TNode<BoolT> WordEqual(Node* left, TNode<Object> right) { |
1044 | return WordEqual(ReinterpretCast<WordT>(left), |
1045 | ReinterpretCast<WordT>(right)); |
1046 | } |
1047 | template <class Left, class Right, |
1048 | class = typename std::enable_if< |
1049 | std::is_base_of<Object, Left>::value && |
1050 | std::is_base_of<Object, Right>::value>::type> |
1051 | TNode<BoolT> WordNotEqual(TNode<Left> left, TNode<Right> right) { |
1052 | return WordNotEqual(ReinterpretCast<WordT>(left), |
1053 | ReinterpretCast<WordT>(right)); |
1054 | } |
1055 | TNode<BoolT> WordNotEqual(TNode<Object> left, Node* right) { |
1056 | return WordNotEqual(ReinterpretCast<WordT>(left), |
1057 | ReinterpretCast<WordT>(right)); |
1058 | } |
1059 | TNode<BoolT> WordNotEqual(Node* left, TNode<Object> right) { |
1060 | return WordNotEqual(ReinterpretCast<WordT>(left), |
1061 | ReinterpretCast<WordT>(right)); |
1062 | } |
1063 | |
1064 | TNode<BoolT> IntPtrEqual(SloppyTNode<WordT> left, SloppyTNode<WordT> right); |
1065 | TNode<BoolT> WordEqual(SloppyTNode<WordT> left, SloppyTNode<WordT> right); |
1066 | TNode<BoolT> WordNotEqual(SloppyTNode<WordT> left, SloppyTNode<WordT> right); |
1067 | TNode<BoolT> Word32Equal(SloppyTNode<Word32T> left, |
1068 | SloppyTNode<Word32T> right); |
1069 | TNode<BoolT> Word32NotEqual(SloppyTNode<Word32T> left, |
1070 | SloppyTNode<Word32T> right); |
1071 | TNode<BoolT> Word64Equal(SloppyTNode<Word64T> left, |
1072 | SloppyTNode<Word64T> right); |
1073 | TNode<BoolT> Word64NotEqual(SloppyTNode<Word64T> left, |
1074 | SloppyTNode<Word64T> right); |
1075 | |
1076 | TNode<Int32T> Int32Add(TNode<Int32T> left, TNode<Int32T> right) { |
1077 | return Signed( |
1078 | Int32Add(static_cast<Node*>(left), static_cast<Node*>(right))); |
1079 | } |
1080 | |
1081 | TNode<Uint32T> Uint32Add(TNode<Uint32T> left, TNode<Uint32T> right) { |
1082 | return Unsigned( |
1083 | Int32Add(static_cast<Node*>(left), static_cast<Node*>(right))); |
1084 | } |
1085 | |
1086 | TNode<WordT> IntPtrAdd(SloppyTNode<WordT> left, SloppyTNode<WordT> right); |
1087 | TNode<IntPtrT> IntPtrDiv(TNode<IntPtrT> left, TNode<IntPtrT> right); |
1088 | TNode<WordT> IntPtrSub(SloppyTNode<WordT> left, SloppyTNode<WordT> right); |
1089 | TNode<WordT> IntPtrMul(SloppyTNode<WordT> left, SloppyTNode<WordT> right); |
1090 | TNode<IntPtrT> IntPtrAdd(TNode<IntPtrT> left, TNode<IntPtrT> right) { |
1091 | return Signed( |
1092 | IntPtrAdd(static_cast<Node*>(left), static_cast<Node*>(right))); |
1093 | } |
1094 | TNode<IntPtrT> IntPtrSub(TNode<IntPtrT> left, TNode<IntPtrT> right) { |
1095 | return Signed( |
1096 | IntPtrSub(static_cast<Node*>(left), static_cast<Node*>(right))); |
1097 | } |
1098 | TNode<IntPtrT> IntPtrMul(TNode<IntPtrT> left, TNode<IntPtrT> right) { |
1099 | return Signed( |
1100 | IntPtrMul(static_cast<Node*>(left), static_cast<Node*>(right))); |
1101 | } |
1102 | TNode<UintPtrT> UintPtrAdd(TNode<UintPtrT> left, TNode<UintPtrT> right) { |
1103 | return Unsigned( |
1104 | IntPtrAdd(static_cast<Node*>(left), static_cast<Node*>(right))); |
1105 | } |
1106 | TNode<UintPtrT> UintPtrSub(TNode<UintPtrT> left, TNode<UintPtrT> right) { |
1107 | return Unsigned( |
1108 | IntPtrSub(static_cast<Node*>(left), static_cast<Node*>(right))); |
1109 | } |
1110 | TNode<RawPtrT> RawPtrAdd(TNode<RawPtrT> left, TNode<IntPtrT> right) { |
1111 | return ReinterpretCast<RawPtrT>(IntPtrAdd(left, right)); |
1112 | } |
1113 | TNode<RawPtrT> RawPtrAdd(TNode<IntPtrT> left, TNode<RawPtrT> right) { |
1114 | return ReinterpretCast<RawPtrT>(IntPtrAdd(left, right)); |
1115 | } |
1116 | |
1117 | TNode<WordT> WordShl(SloppyTNode<WordT> value, int shift); |
1118 | TNode<WordT> WordShr(SloppyTNode<WordT> value, int shift); |
1119 | TNode<WordT> WordSar(SloppyTNode<WordT> value, int shift); |
1120 | TNode<IntPtrT> WordShr(TNode<IntPtrT> value, int shift) { |
1121 | return UncheckedCast<IntPtrT>(WordShr(static_cast<Node*>(value), shift)); |
1122 | } |
1123 | TNode<IntPtrT> WordSar(TNode<IntPtrT> value, int shift) { |
1124 | return UncheckedCast<IntPtrT>(WordSar(static_cast<Node*>(value), shift)); |
1125 | } |
1126 | TNode<Word32T> Word32Shr(SloppyTNode<Word32T> value, int shift); |
1127 | |
1128 | TNode<WordT> WordOr(SloppyTNode<WordT> left, SloppyTNode<WordT> right); |
1129 | TNode<WordT> WordAnd(SloppyTNode<WordT> left, SloppyTNode<WordT> right); |
1130 | TNode<WordT> WordXor(SloppyTNode<WordT> left, SloppyTNode<WordT> right); |
1131 | TNode<WordT> WordShl(SloppyTNode<WordT> left, SloppyTNode<IntegralT> right); |
1132 | TNode<WordT> WordShr(SloppyTNode<WordT> left, SloppyTNode<IntegralT> right); |
1133 | TNode<WordT> WordSar(SloppyTNode<WordT> left, SloppyTNode<IntegralT> right); |
1134 | TNode<Word32T> Word32Or(SloppyTNode<Word32T> left, |
1135 | SloppyTNode<Word32T> right); |
1136 | TNode<Word32T> Word32And(SloppyTNode<Word32T> left, |
1137 | SloppyTNode<Word32T> right); |
1138 | TNode<Word32T> Word32Xor(SloppyTNode<Word32T> left, |
1139 | SloppyTNode<Word32T> right); |
1140 | TNode<Word32T> Word32Shl(SloppyTNode<Word32T> left, |
1141 | SloppyTNode<Word32T> right); |
1142 | TNode<Word32T> Word32Shr(SloppyTNode<Word32T> left, |
1143 | SloppyTNode<Word32T> right); |
1144 | TNode<Word32T> Word32Sar(SloppyTNode<Word32T> left, |
1145 | SloppyTNode<Word32T> right); |
1146 | TNode<Word64T> Word64Or(SloppyTNode<Word64T> left, |
1147 | SloppyTNode<Word64T> right); |
1148 | TNode<Word64T> Word64And(SloppyTNode<Word64T> left, |
1149 | SloppyTNode<Word64T> right); |
1150 | TNode<Word64T> Word64Xor(SloppyTNode<Word64T> left, |
1151 | SloppyTNode<Word64T> right); |
1152 | TNode<Word64T> Word64Shl(SloppyTNode<Word64T> left, |
1153 | SloppyTNode<Word64T> right); |
1154 | TNode<Word64T> Word64Shr(SloppyTNode<Word64T> left, |
1155 | SloppyTNode<Word64T> right); |
1156 | TNode<Word64T> Word64Sar(SloppyTNode<Word64T> left, |
1157 | SloppyTNode<Word64T> right); |
1158 | |
1159 | // Unary |
1160 | #define DECLARE_CODE_ASSEMBLER_UNARY_OP(name, ResType, ArgType) \ |
1161 | TNode<ResType> name(SloppyTNode<ArgType> a); |
1162 | CODE_ASSEMBLER_UNARY_OP_LIST(DECLARE_CODE_ASSEMBLER_UNARY_OP) |
1163 | #undef DECLARE_CODE_ASSEMBLER_UNARY_OP |
1164 | |
1165 | // Changes a double to an inptr_t for pointer arithmetic outside of Smi range. |
1166 | // Assumes that the double can be exactly represented as an int. |
1167 | TNode<UintPtrT> ChangeFloat64ToUintPtr(SloppyTNode<Float64T> value); |
1168 | // Same in the opposite direction. |
1169 | TNode<Float64T> ChangeUintPtrToFloat64(TNode<UintPtrT> value); |
1170 | |
1171 | // Changes an intptr_t to a double, e.g. for storing an element index |
1172 | // outside Smi range in a HeapNumber. Lossless on 32-bit, |
1173 | // rounds on 64-bit (which doesn't affect valid element indices). |
1174 | Node* RoundIntPtrToFloat64(Node* value); |
1175 | // No-op on 32-bit, otherwise zero extend. |
1176 | TNode<UintPtrT> ChangeUint32ToWord(SloppyTNode<Word32T> value); |
1177 | // No-op on 32-bit, otherwise sign extend. |
1178 | TNode<IntPtrT> ChangeInt32ToIntPtr(SloppyTNode<Word32T> value); |
1179 | |
1180 | // No-op that guarantees that the value is kept alive till this point even |
1181 | // if GC happens. |
1182 | Node* Retain(Node* value); |
1183 | |
1184 | // Projections |
1185 | Node* Projection(int index, Node* value); |
1186 | |
1187 | // Pointer compression and decompression. |
1188 | Node* ChangeTaggedToCompressed(Node* tagged); |
1189 | Node* ChangeCompressedToTagged(Node* compressed); |
1190 | |
1191 | template <int index, class T1, class T2> |
1192 | TNode<typename std::tuple_element<index, std::tuple<T1, T2>>::type> |
1193 | Projection(TNode<PairT<T1, T2>> value) { |
1194 | return UncheckedCast< |
1195 | typename std::tuple_element<index, std::tuple<T1, T2>>::type>( |
1196 | Projection(index, value)); |
1197 | } |
1198 | |
1199 | // Calls |
1200 | template <class... TArgs> |
1201 | TNode<Object> CallRuntime(Runtime::FunctionId function, |
1202 | SloppyTNode<Object> context, TArgs... args) { |
1203 | return CallRuntimeImpl(function, context, |
1204 | {implicit_cast<SloppyTNode<Object>>(args)...}); |
1205 | } |
1206 | |
1207 | template <class... TArgs> |
1208 | TNode<Object> CallRuntimeWithCEntry(Runtime::FunctionId function, |
1209 | TNode<Code> centry, |
1210 | SloppyTNode<Object> context, |
1211 | TArgs... args) { |
1212 | return CallRuntimeWithCEntryImpl(function, centry, context, {args...}); |
1213 | } |
1214 | |
1215 | template <class... TArgs> |
1216 | void TailCallRuntime(Runtime::FunctionId function, |
1217 | SloppyTNode<Object> context, TArgs... args) { |
1218 | int argc = static_cast<int>(sizeof...(args)); |
1219 | TNode<Int32T> arity = Int32Constant(argc); |
1220 | return TailCallRuntimeImpl(function, arity, context, |
1221 | {implicit_cast<SloppyTNode<Object>>(args)...}); |
1222 | } |
1223 | |
1224 | template <class... TArgs> |
1225 | void TailCallRuntime(Runtime::FunctionId function, TNode<Int32T> arity, |
1226 | SloppyTNode<Object> context, TArgs... args) { |
1227 | return TailCallRuntimeImpl(function, arity, context, |
1228 | {implicit_cast<SloppyTNode<Object>>(args)...}); |
1229 | } |
1230 | |
1231 | template <class... TArgs> |
1232 | void TailCallRuntimeWithCEntry(Runtime::FunctionId function, |
1233 | TNode<Code> centry, TNode<Object> context, |
1234 | TArgs... args) { |
1235 | int argc = sizeof...(args); |
1236 | TNode<Int32T> arity = Int32Constant(argc); |
1237 | return TailCallRuntimeWithCEntryImpl( |
1238 | function, arity, centry, context, |
1239 | {implicit_cast<SloppyTNode<Object>>(args)...}); |
1240 | } |
1241 | |
1242 | // |
1243 | // If context passed to CallStub is nullptr, it won't be passed to the stub. |
1244 | // |
1245 | |
1246 | template <class T = Object, class... TArgs> |
1247 | TNode<T> CallStub(Callable const& callable, SloppyTNode<Object> context, |
1248 | TArgs... args) { |
1249 | TNode<Code> target = HeapConstant(callable.code()); |
1250 | return CallStub<T>(callable.descriptor(), target, context, args...); |
1251 | } |
1252 | |
1253 | template <class T = Object, class... TArgs> |
1254 | TNode<T> CallStub(const CallInterfaceDescriptor& descriptor, |
1255 | SloppyTNode<Code> target, SloppyTNode<Object> context, |
1256 | TArgs... args) { |
1257 | return UncheckedCast<T>(CallStubR(StubCallMode::kCallCodeObject, descriptor, |
1258 | 1, target, context, args...)); |
1259 | } |
1260 | |
1261 | template <class... TArgs> |
1262 | Node* CallStubR(StubCallMode call_mode, |
1263 | const CallInterfaceDescriptor& descriptor, size_t result_size, |
1264 | SloppyTNode<Object> target, SloppyTNode<Object> context, |
1265 | TArgs... args) { |
1266 | return CallStubRImpl(call_mode, descriptor, result_size, target, context, |
1267 | {args...}); |
1268 | } |
1269 | |
1270 | Node* CallStubN(StubCallMode call_mode, |
1271 | const CallInterfaceDescriptor& descriptor, size_t result_size, |
1272 | int input_count, Node* const* inputs); |
1273 | |
1274 | template <class T = Object, class... TArgs> |
1275 | TNode<T> CallBuiltinPointer(const CallInterfaceDescriptor& descriptor, |
1276 | TNode<BuiltinPtr> target, TNode<Object> context, |
1277 | TArgs... args) { |
1278 | return UncheckedCast<T>(CallStubR(StubCallMode::kCallBuiltinPointer, |
1279 | descriptor, 1, target, context, args...)); |
1280 | } |
1281 | |
1282 | template <class... TArgs> |
1283 | void TailCallStub(Callable const& callable, SloppyTNode<Object> context, |
1284 | TArgs... args) { |
1285 | TNode<Code> target = HeapConstant(callable.code()); |
1286 | return TailCallStub(callable.descriptor(), target, context, args...); |
1287 | } |
1288 | |
1289 | template <class... TArgs> |
1290 | void TailCallStub(const CallInterfaceDescriptor& descriptor, |
1291 | SloppyTNode<Code> target, SloppyTNode<Object> context, |
1292 | TArgs... args) { |
1293 | return TailCallStubImpl(descriptor, target, context, {args...}); |
1294 | } |
1295 | |
1296 | template <class... TArgs> |
1297 | Node* TailCallBytecodeDispatch(const CallInterfaceDescriptor& descriptor, |
1298 | Node* target, TArgs... args); |
1299 | |
1300 | template <class... TArgs> |
1301 | Node* TailCallStubThenBytecodeDispatch( |
1302 | const CallInterfaceDescriptor& descriptor, Node* target, Node* context, |
1303 | TArgs... args) { |
1304 | return TailCallStubThenBytecodeDispatchImpl(descriptor, target, context, |
1305 | {args...}); |
1306 | } |
1307 | |
1308 | // Tailcalls to the given code object with JSCall linkage. The JS arguments |
1309 | // (including receiver) are supposed to be already on the stack. |
1310 | // This is a building block for implementing trampoline stubs that are |
1311 | // installed instead of code objects with JSCall linkage. |
1312 | // Note that no arguments adaption is going on here - all the JavaScript |
1313 | // arguments are left on the stack unmodified. Therefore, this tail call can |
1314 | // only be used after arguments adaptation has been performed already. |
1315 | TNode<Object> TailCallJSCode(TNode<Code> code, TNode<Context> context, |
1316 | TNode<JSFunction> function, |
1317 | TNode<Object> new_target, |
1318 | TNode<Int32T> arg_count); |
1319 | |
1320 | template <class... TArgs> |
1321 | Node* CallJS(Callable const& callable, Node* context, Node* function, |
1322 | Node* receiver, TArgs... args) { |
1323 | int argc = static_cast<int>(sizeof...(args)); |
1324 | Node* arity = Int32Constant(argc); |
1325 | return CallStub(callable, context, function, arity, receiver, args...); |
1326 | } |
1327 | |
1328 | template <class... TArgs> |
1329 | Node* ConstructJSWithTarget(Callable const& callable, Node* context, |
1330 | Node* target, Node* new_target, TArgs... args) { |
1331 | int argc = static_cast<int>(sizeof...(args)); |
1332 | Node* arity = Int32Constant(argc); |
1333 | Node* receiver = LoadRoot(RootIndex::kUndefinedValue); |
1334 | |
1335 | // Construct(target, new_target, arity, receiver, arguments...) |
1336 | return CallStub(callable, context, target, new_target, arity, receiver, |
1337 | args...); |
1338 | } |
1339 | template <class... TArgs> |
1340 | Node* ConstructJS(Callable const& callable, Node* context, Node* new_target, |
1341 | TArgs... args) { |
1342 | return ConstructJSWithTarget(callable, context, new_target, new_target, |
1343 | args...); |
1344 | } |
1345 | |
1346 | Node* CallCFunctionN(Signature<MachineType>* signature, int input_count, |
1347 | Node* const* inputs); |
1348 | |
1349 | // Type representing C function argument with type info. |
1350 | using CFunctionArg = std::pair<MachineType, Node*>; |
1351 | |
1352 | // Call to a C function. |
1353 | template <class... CArgs> |
1354 | Node* CallCFunction(Node* function, MachineType return_type, CArgs... cargs) { |
1355 | static_assert(v8::internal::conjunction< |
1356 | std::is_convertible<CArgs, CFunctionArg>...>::value, |
1357 | "invalid argument types" ); |
1358 | return CallCFunction(function, return_type, {cargs...}); |
1359 | } |
1360 | |
1361 | // Call to a C function, while saving/restoring caller registers. |
1362 | template <class... CArgs> |
1363 | Node* CallCFunctionWithCallerSavedRegisters(Node* function, |
1364 | MachineType return_type, |
1365 | SaveFPRegsMode mode, |
1366 | CArgs... cargs) { |
1367 | static_assert(v8::internal::conjunction< |
1368 | std::is_convertible<CArgs, CFunctionArg>...>::value, |
1369 | "invalid argument types" ); |
1370 | return CallCFunctionWithCallerSavedRegisters(function, return_type, mode, |
1371 | {cargs...}); |
1372 | } |
1373 | |
1374 | // Exception handling support. |
1375 | void GotoIfException(Node* node, Label* if_exception, |
1376 | Variable* exception_var = nullptr); |
1377 | |
1378 | // Helpers which delegate to RawMachineAssembler. |
1379 | Factory* factory() const; |
1380 | Isolate* isolate() const; |
1381 | Zone* zone() const; |
1382 | |
1383 | CodeAssemblerState* state() { return state_; } |
1384 | |
1385 | void BreakOnNode(int node_id); |
1386 | |
1387 | bool UnalignedLoadSupported(MachineRepresentation rep) const; |
1388 | bool UnalignedStoreSupported(MachineRepresentation rep) const; |
1389 | |
1390 | bool IsExceptionHandlerActive() const; |
1391 | |
1392 | protected: |
1393 | void RegisterCallGenerationCallbacks( |
1394 | const CodeAssemblerCallback& call_prologue, |
1395 | const CodeAssemblerCallback& call_epilogue); |
1396 | void UnregisterCallGenerationCallbacks(); |
1397 | |
1398 | bool Word32ShiftIsSafe() const; |
1399 | PoisoningMitigationLevel poisoning_level() const; |
1400 | |
1401 | bool IsJSFunctionCall() const; |
1402 | |
1403 | private: |
1404 | void HandleException(Node* result); |
1405 | |
1406 | Node* CallCFunction(Node* function, MachineType return_type, |
1407 | std::initializer_list<CFunctionArg> args); |
1408 | |
1409 | Node* CallCFunctionWithCallerSavedRegisters( |
1410 | Node* function, MachineType return_type, SaveFPRegsMode mode, |
1411 | std::initializer_list<CFunctionArg> args); |
1412 | |
1413 | TNode<Object> CallRuntimeImpl(Runtime::FunctionId function, |
1414 | TNode<Object> context, |
1415 | std::initializer_list<TNode<Object>> args); |
1416 | |
1417 | TNode<Object> CallRuntimeWithCEntryImpl( |
1418 | Runtime::FunctionId function, TNode<Code> centry, TNode<Object> context, |
1419 | std::initializer_list<TNode<Object>> args); |
1420 | |
1421 | void TailCallRuntimeImpl(Runtime::FunctionId function, TNode<Int32T> arity, |
1422 | TNode<Object> context, |
1423 | std::initializer_list<TNode<Object>> args); |
1424 | |
1425 | void TailCallRuntimeWithCEntryImpl(Runtime::FunctionId function, |
1426 | TNode<Int32T> arity, TNode<Code> centry, |
1427 | TNode<Object> context, |
1428 | std::initializer_list<TNode<Object>> args); |
1429 | |
1430 | void TailCallStubImpl(const CallInterfaceDescriptor& descriptor, |
1431 | TNode<Code> target, TNode<Object> context, |
1432 | std::initializer_list<Node*> args); |
1433 | |
1434 | Node* TailCallStubThenBytecodeDispatchImpl( |
1435 | const CallInterfaceDescriptor& descriptor, Node* target, Node* context, |
1436 | std::initializer_list<Node*> args); |
1437 | |
1438 | Node* CallStubRImpl(StubCallMode call_mode, |
1439 | const CallInterfaceDescriptor& descriptor, |
1440 | size_t result_size, Node* target, |
1441 | SloppyTNode<Object> context, |
1442 | std::initializer_list<Node*> args); |
1443 | |
1444 | // These two don't have definitions and are here only for catching use cases |
1445 | // where the cast is not necessary. |
1446 | TNode<Int32T> Signed(TNode<Int32T> x); |
1447 | TNode<Uint32T> Unsigned(TNode<Uint32T> x); |
1448 | |
1449 | RawMachineAssembler* raw_assembler() const; |
1450 | |
1451 | // Calls respective callback registered in the state. |
1452 | void CallPrologue(); |
1453 | void CallEpilogue(); |
1454 | |
1455 | CodeAssemblerState* state_; |
1456 | |
1457 | DISALLOW_COPY_AND_ASSIGN(CodeAssembler); |
1458 | }; |
1459 | |
1460 | class V8_EXPORT_PRIVATE CodeAssemblerVariable { |
1461 | public: |
1462 | explicit CodeAssemblerVariable(CodeAssembler* assembler, |
1463 | MachineRepresentation rep); |
1464 | CodeAssemblerVariable(CodeAssembler* assembler, MachineRepresentation rep, |
1465 | Node* initial_value); |
1466 | #if DEBUG |
1467 | CodeAssemblerVariable(CodeAssembler* assembler, AssemblerDebugInfo debug_info, |
1468 | MachineRepresentation rep); |
1469 | CodeAssemblerVariable(CodeAssembler* assembler, AssemblerDebugInfo debug_info, |
1470 | MachineRepresentation rep, Node* initial_value); |
1471 | #endif // DEBUG |
1472 | |
1473 | ~CodeAssemblerVariable(); |
1474 | void Bind(Node* value); |
1475 | Node* value() const; |
1476 | MachineRepresentation rep() const; |
1477 | bool IsBound() const; |
1478 | |
1479 | private: |
1480 | class Impl; |
1481 | friend class CodeAssemblerLabel; |
1482 | friend class CodeAssemblerState; |
1483 | friend std::ostream& operator<<(std::ostream&, const Impl&); |
1484 | friend std::ostream& operator<<(std::ostream&, const CodeAssemblerVariable&); |
1485 | struct ImplComparator { |
1486 | bool operator()(const CodeAssemblerVariable::Impl* a, |
1487 | const CodeAssemblerVariable::Impl* b) const; |
1488 | }; |
1489 | Impl* impl_; |
1490 | CodeAssemblerState* state_; |
1491 | DISALLOW_COPY_AND_ASSIGN(CodeAssemblerVariable); |
1492 | }; |
1493 | |
1494 | std::ostream& operator<<(std::ostream&, const CodeAssemblerVariable&); |
1495 | std::ostream& operator<<(std::ostream&, const CodeAssemblerVariable::Impl&); |
1496 | |
1497 | template <class T> |
1498 | class TypedCodeAssemblerVariable : public CodeAssemblerVariable { |
1499 | public: |
1500 | TypedCodeAssemblerVariable(TNode<T> initial_value, CodeAssembler* assembler) |
1501 | : CodeAssemblerVariable(assembler, MachineRepresentationOf<T>::value, |
1502 | initial_value) {} |
1503 | explicit TypedCodeAssemblerVariable(CodeAssembler* assembler) |
1504 | : CodeAssemblerVariable(assembler, MachineRepresentationOf<T>::value) {} |
1505 | #if DEBUG |
1506 | TypedCodeAssemblerVariable(AssemblerDebugInfo debug_info, |
1507 | CodeAssembler* assembler) |
1508 | : CodeAssemblerVariable(assembler, debug_info, |
1509 | MachineRepresentationOf<T>::value) {} |
1510 | TypedCodeAssemblerVariable(AssemblerDebugInfo debug_info, |
1511 | TNode<T> initial_value, CodeAssembler* assembler) |
1512 | : CodeAssemblerVariable(assembler, debug_info, |
1513 | MachineRepresentationOf<T>::value, |
1514 | initial_value) {} |
1515 | #endif // DEBUG |
1516 | |
1517 | TNode<T> value() const { |
1518 | return TNode<T>::UncheckedCast(CodeAssemblerVariable::value()); |
1519 | } |
1520 | |
1521 | void operator=(TNode<T> value) { Bind(value); } |
1522 | void operator=(const TypedCodeAssemblerVariable<T>& variable) { |
1523 | Bind(variable.value()); |
1524 | } |
1525 | |
1526 | private: |
1527 | using CodeAssemblerVariable::Bind; |
1528 | }; |
1529 | |
1530 | class V8_EXPORT_PRIVATE CodeAssemblerLabel { |
1531 | public: |
1532 | enum Type { kDeferred, kNonDeferred }; |
1533 | |
1534 | explicit CodeAssemblerLabel( |
1535 | CodeAssembler* assembler, |
1536 | CodeAssemblerLabel::Type type = CodeAssemblerLabel::kNonDeferred) |
1537 | : CodeAssemblerLabel(assembler, 0, nullptr, type) {} |
1538 | CodeAssemblerLabel( |
1539 | CodeAssembler* assembler, |
1540 | const CodeAssemblerVariableList& merged_variables, |
1541 | CodeAssemblerLabel::Type type = CodeAssemblerLabel::kNonDeferred) |
1542 | : CodeAssemblerLabel(assembler, merged_variables.size(), |
1543 | &(merged_variables[0]), type) {} |
1544 | CodeAssemblerLabel( |
1545 | CodeAssembler* assembler, size_t count, |
1546 | CodeAssemblerVariable* const* vars, |
1547 | CodeAssemblerLabel::Type type = CodeAssemblerLabel::kNonDeferred); |
1548 | CodeAssemblerLabel( |
1549 | CodeAssembler* assembler, |
1550 | std::initializer_list<CodeAssemblerVariable*> vars, |
1551 | CodeAssemblerLabel::Type type = CodeAssemblerLabel::kNonDeferred) |
1552 | : CodeAssemblerLabel(assembler, vars.size(), vars.begin(), type) {} |
1553 | CodeAssemblerLabel( |
1554 | CodeAssembler* assembler, CodeAssemblerVariable* merged_variable, |
1555 | CodeAssemblerLabel::Type type = CodeAssemblerLabel::kNonDeferred) |
1556 | : CodeAssemblerLabel(assembler, 1, &merged_variable, type) {} |
1557 | ~CodeAssemblerLabel(); |
1558 | |
1559 | inline bool is_bound() const { return bound_; } |
1560 | inline bool is_used() const { return merge_count_ != 0; } |
1561 | |
1562 | private: |
1563 | friend class CodeAssembler; |
1564 | |
1565 | void Bind(); |
1566 | #if DEBUG |
1567 | void Bind(AssemblerDebugInfo debug_info); |
1568 | #endif // DEBUG |
1569 | void UpdateVariablesAfterBind(); |
1570 | void MergeVariables(); |
1571 | |
1572 | bool bound_; |
1573 | size_t merge_count_; |
1574 | CodeAssemblerState* state_; |
1575 | RawMachineLabel* label_; |
1576 | // Map of variables that need to be merged to their phi nodes (or placeholders |
1577 | // for those phis). |
1578 | std::map<CodeAssemblerVariable::Impl*, Node*, |
1579 | CodeAssemblerVariable::ImplComparator> |
1580 | variable_phis_; |
1581 | // Map of variables to the list of value nodes that have been added from each |
1582 | // merge path in their order of merging. |
1583 | std::map<CodeAssemblerVariable::Impl*, std::vector<Node*>, |
1584 | CodeAssemblerVariable::ImplComparator> |
1585 | variable_merges_; |
1586 | |
1587 | // Cannot be copied because the destructor explicitly call the destructor of |
1588 | // the underlying {RawMachineLabel}, hence only one pointer can point to it. |
1589 | DISALLOW_COPY_AND_ASSIGN(CodeAssemblerLabel); |
1590 | }; |
1591 | |
1592 | class CodeAssemblerParameterizedLabelBase { |
1593 | public: |
1594 | bool is_used() const { return plain_label_.is_used(); } |
1595 | explicit CodeAssemblerParameterizedLabelBase(CodeAssembler* assembler, |
1596 | size_t arity, |
1597 | CodeAssemblerLabel::Type type) |
1598 | : state_(assembler->state()), |
1599 | phi_inputs_(arity), |
1600 | plain_label_(assembler, type) {} |
1601 | |
1602 | protected: |
1603 | CodeAssemblerLabel* plain_label() { return &plain_label_; } |
1604 | void AddInputs(std::vector<Node*> inputs); |
1605 | Node* CreatePhi(MachineRepresentation rep, const std::vector<Node*>& inputs); |
1606 | const std::vector<Node*>& CreatePhis( |
1607 | std::vector<MachineRepresentation> representations); |
1608 | |
1609 | private: |
1610 | CodeAssemblerState* state_; |
1611 | std::vector<std::vector<Node*>> phi_inputs_; |
1612 | std::vector<Node*> phi_nodes_; |
1613 | CodeAssemblerLabel plain_label_; |
1614 | }; |
1615 | |
1616 | template <class... Types> |
1617 | class CodeAssemblerParameterizedLabel |
1618 | : public CodeAssemblerParameterizedLabelBase { |
1619 | public: |
1620 | static constexpr size_t kArity = sizeof...(Types); |
1621 | explicit CodeAssemblerParameterizedLabel(CodeAssembler* assembler, |
1622 | CodeAssemblerLabel::Type type) |
1623 | : CodeAssemblerParameterizedLabelBase(assembler, kArity, type) {} |
1624 | |
1625 | private: |
1626 | friend class CodeAssembler; |
1627 | |
1628 | void AddInputs(TNode<Types>... inputs) { |
1629 | CodeAssemblerParameterizedLabelBase::AddInputs( |
1630 | std::vector<Node*>{inputs...}); |
1631 | } |
1632 | void CreatePhis(TNode<Types>*... results) { |
1633 | const std::vector<Node*>& phi_nodes = |
1634 | CodeAssemblerParameterizedLabelBase::CreatePhis( |
1635 | {MachineRepresentationOf<Types>::value...}); |
1636 | auto it = phi_nodes.begin(); |
1637 | USE(it); |
1638 | ITERATE_PACK(AssignPhi(results, *(it++))); |
1639 | } |
1640 | template <class T> |
1641 | static void AssignPhi(TNode<T>* result, Node* phi) { |
1642 | if (phi != nullptr) *result = TNode<T>::UncheckedCast(phi); |
1643 | } |
1644 | }; |
1645 | |
1646 | using CodeAssemblerExceptionHandlerLabel = |
1647 | CodeAssemblerParameterizedLabel<Object>; |
1648 | |
1649 | class V8_EXPORT_PRIVATE CodeAssemblerState { |
1650 | public: |
1651 | // Create with CallStub linkage. |
1652 | // |result_size| specifies the number of results returned by the stub. |
1653 | // TODO(rmcilroy): move result_size to the CallInterfaceDescriptor. |
1654 | CodeAssemblerState(Isolate* isolate, Zone* zone, |
1655 | const CallInterfaceDescriptor& descriptor, Code::Kind kind, |
1656 | const char* name, PoisoningMitigationLevel poisoning_level, |
1657 | int32_t builtin_index = Builtins::kNoBuiltinId); |
1658 | |
1659 | // Create with JSCall linkage. |
1660 | CodeAssemblerState(Isolate* isolate, Zone* zone, int parameter_count, |
1661 | Code::Kind kind, const char* name, |
1662 | PoisoningMitigationLevel poisoning_level, |
1663 | int32_t builtin_index = Builtins::kNoBuiltinId); |
1664 | |
1665 | ~CodeAssemblerState(); |
1666 | |
1667 | const char* name() const { return name_; } |
1668 | int parameter_count() const; |
1669 | |
1670 | #if DEBUG |
1671 | void PrintCurrentBlock(std::ostream& os); |
1672 | #endif // DEBUG |
1673 | bool InsideBlock(); |
1674 | void SetInitialDebugInformation(const char* msg, const char* file, int line); |
1675 | |
1676 | private: |
1677 | friend class CodeAssembler; |
1678 | friend class CodeAssemblerLabel; |
1679 | friend class CodeAssemblerVariable; |
1680 | friend class CodeAssemblerTester; |
1681 | friend class CodeAssemblerParameterizedLabelBase; |
1682 | friend class CodeAssemblerScopedExceptionHandler; |
1683 | |
1684 | CodeAssemblerState(Isolate* isolate, Zone* zone, |
1685 | CallDescriptor* call_descriptor, Code::Kind kind, |
1686 | const char* name, PoisoningMitigationLevel poisoning_level, |
1687 | int32_t builtin_index); |
1688 | |
1689 | void PushExceptionHandler(CodeAssemblerExceptionHandlerLabel* label); |
1690 | void PopExceptionHandler(); |
1691 | |
1692 | std::unique_ptr<RawMachineAssembler> raw_assembler_; |
1693 | Code::Kind kind_; |
1694 | const char* name_; |
1695 | int32_t builtin_index_; |
1696 | bool code_generated_; |
1697 | ZoneSet<CodeAssemblerVariable::Impl*, CodeAssemblerVariable::ImplComparator> |
1698 | variables_; |
1699 | CodeAssemblerCallback call_prologue_; |
1700 | CodeAssemblerCallback call_epilogue_; |
1701 | std::vector<CodeAssemblerExceptionHandlerLabel*> exception_handler_labels_; |
1702 | using VariableId = uint32_t; |
1703 | VariableId next_variable_id_ = 0; |
1704 | |
1705 | VariableId NextVariableId() { return next_variable_id_++; } |
1706 | |
1707 | DISALLOW_COPY_AND_ASSIGN(CodeAssemblerState); |
1708 | }; |
1709 | |
1710 | class V8_EXPORT_PRIVATE CodeAssemblerScopedExceptionHandler { |
1711 | public: |
1712 | CodeAssemblerScopedExceptionHandler( |
1713 | CodeAssembler* assembler, CodeAssemblerExceptionHandlerLabel* label); |
1714 | |
1715 | // Use this constructor for compatability/ports of old CSA code only. New code |
1716 | // should use the CodeAssemblerExceptionHandlerLabel version. |
1717 | CodeAssemblerScopedExceptionHandler( |
1718 | CodeAssembler* assembler, CodeAssemblerLabel* label, |
1719 | TypedCodeAssemblerVariable<Object>* exception); |
1720 | |
1721 | ~CodeAssemblerScopedExceptionHandler(); |
1722 | |
1723 | private: |
1724 | bool has_handler_; |
1725 | CodeAssembler* assembler_; |
1726 | CodeAssemblerLabel* compatibility_label_; |
1727 | std::unique_ptr<CodeAssemblerExceptionHandlerLabel> label_; |
1728 | TypedCodeAssemblerVariable<Object>* exception_; |
1729 | }; |
1730 | |
1731 | } // namespace compiler |
1732 | |
1733 | #if defined(V8_HOST_ARCH_32_BIT) |
1734 | using BInt = Smi; |
1735 | #elif defined(V8_HOST_ARCH_64_BIT) |
1736 | using BInt = IntPtrT; |
1737 | #else |
1738 | #error Unknown architecture. |
1739 | #endif |
1740 | |
1741 | } // namespace internal |
1742 | } // namespace v8 |
1743 | |
1744 | #endif // V8_COMPILER_CODE_ASSEMBLER_H_ |
1745 | |