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