1// Copyright 2020 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
5namespace ic {
6namespace callable {
7
8extern macro IncrementCallCount(FeedbackVector, uintptr): void;
9const kCallFeedbackContentFieldMask: constexpr int32
10    generates 'FeedbackNexus::CallFeedbackContentField::kMask';
11const kCallFeedbackContentFieldShift: constexpr uint32
12    generates 'FeedbackNexus::CallFeedbackContentField::kShift';
13
14macro IsMonomorphic(feedback: MaybeObject, target: JSAny): bool {
15  return IsWeakReferenceToObject(feedback, target);
16}
17
18macro InSameNativeContext(lhs: Context, rhs: Context): bool {
19  return LoadNativeContext(lhs) == LoadNativeContext(rhs);
20}
21
22macro MaybeObjectToStrong(maybeObject: MaybeObject):
23    HeapObject labels IfCleared {
24  dcheck(IsWeakOrCleared(maybeObject));
25  const weakObject = %RawDownCast<Weak<HeapObject>>(maybeObject);
26  return WeakToStrong(weakObject) otherwise IfCleared;
27}
28
29macro TryInitializeAsMonomorphic(implicit context: Context)(
30    maybeTarget: JSAny, feedbackVector: FeedbackVector,
31    slotId: uintptr): void labels TransitionToMegamorphic {
32  const targetHeapObject =
33      Cast<HeapObject>(maybeTarget) otherwise TransitionToMegamorphic;
34
35  let unwrappedTarget = targetHeapObject;
36  while (Is<JSBoundFunction>(unwrappedTarget)) {
37    unwrappedTarget =
38        UnsafeCast<JSBoundFunction>(unwrappedTarget).bound_target_function;
39  }
40
41  const unwrappedTargetJSFunction =
42      Cast<JSFunction>(unwrappedTarget) otherwise TransitionToMegamorphic;
43  if (!InSameNativeContext(unwrappedTargetJSFunction.context, context)) {
44    goto TransitionToMegamorphic;
45  }
46
47  StoreWeakReferenceInFeedbackVector(feedbackVector, slotId, targetHeapObject);
48  ReportFeedbackUpdate(feedbackVector, slotId, 'Call:Initialize');
49}
50
51macro TransitionToMegamorphic(implicit context: Context)(
52    feedbackVector: FeedbackVector, slotId: uintptr): void {
53  StoreFeedbackVectorSlot(feedbackVector, slotId, kMegamorphicSymbol);
54  ReportFeedbackUpdate(feedbackVector, slotId, 'Call:TransitionMegamorphic');
55}
56
57macro TaggedEqualPrototypeApplyFunction(implicit context: Context)(
58    target: JSAny): bool {
59  return TaggedEqual(target, GetPrototypeApplyFunction());
60}
61
62macro FeedbackValueIsReceiver(implicit context: Context)(
63    feedbackVector: FeedbackVector, slotId: uintptr): bool {
64  const callCount: intptr = SmiUntag(Cast<Smi>(LoadFeedbackVectorSlot(
65      feedbackVector, slotId, kTaggedSize)) otherwise return false);
66  return (callCount & IntPtrConstant(kCallFeedbackContentFieldMask)) !=
67      IntPtrConstant(0);
68}
69
70macro SetCallFeedbackContent(implicit context: Context)(
71    feedbackVector: FeedbackVector, slotId: uintptr,
72    callFeedbackContent: constexpr CallFeedbackContent): void {
73  // Load the call count field from the feecback vector.
74  const callCount: intptr = SmiUntag(Cast<Smi>(LoadFeedbackVectorSlot(
75      feedbackVector, slotId, kTaggedSize)) otherwise return );
76  // The second lowest bits of the call count are used to state whether the
77  // feedback collected is a target or a receiver. Change that bit based on the
78  // callFeedbackContent input.
79  const callFeedbackContentFieldMask: intptr =
80      ~IntPtrConstant(kCallFeedbackContentFieldMask);
81  const newCount: intptr = (callCount & callFeedbackContentFieldMask) |
82      Convert<intptr>(Signed(
83          %RawConstexprCast<constexpr uint32>(callFeedbackContent)
84          << kCallFeedbackContentFieldShift));
85  StoreFeedbackVectorSlot(
86      feedbackVector, slotId, SmiTag(newCount), SKIP_WRITE_BARRIER,
87      kTaggedSize);
88  ReportFeedbackUpdate(feedbackVector, slotId, 'Call:SetCallFeedbackContent');
89}
90
91macro CollectCallFeedback(
92    maybeTarget: JSAny, maybeReceiver: Lazy<JSAny>, context: Context,
93    maybeFeedbackVector: Undefined|FeedbackVector, slotId: uintptr): void {
94  // TODO(v8:9891): Remove this dcheck once all callers are ported to Torque.
95  // This dcheck ensures correctness of maybeFeedbackVector's type which can
96  // be easily broken for calls from CSA.
97  dcheck(
98      IsUndefined(maybeFeedbackVector) ||
99      Is<FeedbackVector>(maybeFeedbackVector));
100  const feedbackVector =
101      Cast<FeedbackVector>(maybeFeedbackVector) otherwise return;
102  IncrementCallCount(feedbackVector, slotId);
103
104  try {
105    const feedback: MaybeObject =
106        LoadFeedbackVectorSlot(feedbackVector, slotId);
107    if (IsMonomorphic(feedback, maybeTarget)) return;
108    if (IsMegamorphic(feedback)) return;
109    if (IsUninitialized(feedback)) goto TryInitializeAsMonomorphic;
110
111    // If cleared, we have a new chance to become monomorphic.
112    const feedbackValue: HeapObject =
113        MaybeObjectToStrong(feedback) otherwise TryReinitializeAsMonomorphic;
114
115    if (FeedbackValueIsReceiver(feedbackVector, slotId) &&
116        TaggedEqualPrototypeApplyFunction(maybeTarget)) {
117      // If the Receiver is recorded and the target is
118      // Function.prototype.apply, check whether we can stay monomorphic based
119      // on the receiver.
120      if (IsMonomorphic(feedback, RunLazy(maybeReceiver))) {
121        return;
122      } else {
123        // If not, reinitialize the feedback with target.
124        SetCallFeedbackContent(
125            feedbackVector, slotId, CallFeedbackContent::kTarget);
126        TryInitializeAsMonomorphic(maybeTarget, feedbackVector, slotId)
127            otherwise TransitionToMegamorphic;
128        return;
129      }
130    }
131
132    // Try transitioning to a feedback cell.
133    // Check if {target}s feedback cell matches the {feedbackValue}.
134    const target =
135        Cast<JSFunction>(maybeTarget) otherwise TransitionToMegamorphic;
136    const targetFeedbackCell: FeedbackCell = target.feedback_cell;
137    if (TaggedEqual(feedbackValue, targetFeedbackCell)) return;
138
139    // Check if {target} and {feedbackValue} are both JSFunctions with
140    // the same feedback vector cell, and that those functions were
141    // actually compiled already.
142    const feedbackValueJSFunction =
143        Cast<JSFunction>(feedbackValue) otherwise TransitionToMegamorphic;
144    const feedbackCell: FeedbackCell = feedbackValueJSFunction.feedback_cell;
145    if (!TaggedEqual(feedbackCell, targetFeedbackCell))
146      goto TransitionToMegamorphic;
147
148    StoreWeakReferenceInFeedbackVector(feedbackVector, slotId, feedbackCell);
149    ReportFeedbackUpdate(feedbackVector, slotId, 'Call:FeedbackVectorCell');
150  } label TryReinitializeAsMonomorphic {
151    SetCallFeedbackContent(
152        feedbackVector, slotId, CallFeedbackContent::kTarget);
153    goto TryInitializeAsMonomorphic;
154  } label TryInitializeAsMonomorphic {
155    let recordedFunction = maybeTarget;
156    if (TaggedEqualPrototypeApplyFunction(maybeTarget)) {
157      recordedFunction = RunLazy(maybeReceiver);
158      SetCallFeedbackContent(
159          feedbackVector, slotId, CallFeedbackContent::kReceiver);
160    } else {
161      dcheck(!FeedbackValueIsReceiver(feedbackVector, slotId));
162    }
163    TryInitializeAsMonomorphic(recordedFunction, feedbackVector, slotId)
164        otherwise TransitionToMegamorphic;
165  } label TransitionToMegamorphic {
166    TransitionToMegamorphic(feedbackVector, slotId);
167  }
168}
169
170macro CollectInstanceOfFeedback(
171    maybeTarget: JSAny, context: Context,
172    maybeFeedbackVector: Undefined|FeedbackVector, slotId: uintptr): void {
173  // TODO(v8:9891): Remove this dcheck once all callers are ported to Torque.
174  // This dcheck ensures correctness of maybeFeedbackVector's type which can
175  // be easily broken for calls from CSA.
176  dcheck(
177      IsUndefined(maybeFeedbackVector) ||
178      Is<FeedbackVector>(maybeFeedbackVector));
179  const feedbackVector =
180      Cast<FeedbackVector>(maybeFeedbackVector) otherwise return;
181  // Note: The call count is not incremented.
182
183  try {
184    const feedback: MaybeObject =
185        LoadFeedbackVectorSlot(feedbackVector, slotId);
186    if (IsMonomorphic(feedback, maybeTarget)) return;
187    if (IsMegamorphic(feedback)) return;
188    if (IsUninitialized(feedback)) goto TryInitializeAsMonomorphic;
189
190    // If cleared, we have a new chance to become monomorphic.
191    const _feedbackValue: HeapObject =
192        MaybeObjectToStrong(feedback) otherwise TryInitializeAsMonomorphic;
193
194    goto TransitionToMegamorphic;
195  } label TryInitializeAsMonomorphic {
196    TryInitializeAsMonomorphic(maybeTarget, feedbackVector, slotId)
197        otherwise TransitionToMegamorphic;
198  } label TransitionToMegamorphic {
199    TransitionToMegamorphic(feedbackVector, slotId);
200  }
201}
202
203macro BothTaggedEqualArrayFunction(implicit context: Context)(
204    first: JSAny, second: JSAny): bool {
205  return TaggedEqual(first, second) && TaggedEqual(second, GetArrayFunction());
206}
207
208extern macro CreateAllocationSiteInFeedbackVector(
209    FeedbackVector, uintptr): AllocationSite;
210
211macro CastFeedbackVector(
212    maybeFeedbackVector: Undefined|FeedbackVector,
213    updateFeedbackMode: constexpr UpdateFeedbackMode):
214    FeedbackVector labels Fallback {
215  if constexpr (updateFeedbackMode == UpdateFeedbackMode::kGuaranteedFeedback) {
216    return UnsafeCast<FeedbackVector>(maybeFeedbackVector);
217  } else if constexpr (
218      updateFeedbackMode == UpdateFeedbackMode::kOptionalFeedback) {
219    return Cast<FeedbackVector>(maybeFeedbackVector) otherwise goto Fallback;
220  } else {
221    unreachable;
222  }
223}
224
225macro CollectConstructFeedback(implicit context: Context)(
226    target: JSAny, newTarget: JSAny,
227    maybeFeedbackVector: Undefined|FeedbackVector, slotId: uintptr,
228    updateFeedbackMode: constexpr UpdateFeedbackMode):
229    never labels ConstructGeneric,
230    ConstructArray(AllocationSite) {
231  // TODO(v8:9891): Remove this dcheck once all callers are ported to Torque.
232  // This dcheck ensures correctness of maybeFeedbackVector's type which can
233  // be easily broken for calls from CSA.
234  dcheck(
235      IsUndefined(maybeFeedbackVector) ||
236      Is<FeedbackVector>(maybeFeedbackVector));
237
238  const feedbackVector = CastFeedbackVector(
239      maybeFeedbackVector, updateFeedbackMode) otherwise goto ConstructGeneric;
240
241  IncrementCallCount(feedbackVector, slotId);
242
243  try {
244    const feedback: MaybeObject =
245        LoadFeedbackVectorSlot(feedbackVector, slotId);
246    if (IsMonomorphic(feedback, newTarget)) goto ConstructGeneric;
247    if (IsMegamorphic(feedback)) goto ConstructGeneric;
248    if (IsUninitialized(feedback)) goto TryInitializeAsMonomorphic;
249
250    if (!IsWeakOrCleared(feedback)) {
251      const feedbackAsStrong = %RawDownCast<Object>(feedback);
252      if (Is<AllocationSite>(feedbackAsStrong)) {
253        if (BothTaggedEqualArrayFunction(target, newTarget)) {
254          goto ConstructArray(UnsafeCast<AllocationSite>(feedbackAsStrong));
255        }
256        goto TransitionToMegamorphic;
257      }
258    }
259
260    // If cleared, we have a new chance to become monomorphic.
261    const _feedbackValue: HeapObject =
262        MaybeObjectToStrong(feedback) otherwise TryInitializeAsMonomorphic;
263
264    goto TransitionToMegamorphic;
265  } label TryInitializeAsMonomorphic {
266    if (BothTaggedEqualArrayFunction(target, newTarget)) {
267      // In this case we can skip unwrapping and context validation since we
268      // know the target is the current context's array function.
269      const allocationSite =
270          CreateAllocationSiteInFeedbackVector(feedbackVector, slotId);
271      ReportFeedbackUpdate(
272          feedbackVector, slotId, 'Construct:CreateAllocationSite');
273      goto ConstructArray(allocationSite);
274    }
275
276    TryInitializeAsMonomorphic(newTarget, feedbackVector, slotId)
277        otherwise TransitionToMegamorphic;
278  } label TransitionToMegamorphic {
279    TransitionToMegamorphic(feedbackVector, slotId);
280  }
281  goto ConstructGeneric;
282}
283
284}  // namespace callable
285}  // namespace ic
286