1 /*
2 * Copyright (c) 2021 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #ifndef ECMASCRIPT_IC_PROFILE_TYPE_INFO_H
17 #define ECMASCRIPT_IC_PROFILE_TYPE_INFO_H
18
19 #include "ecmascript/ic/mega_ic_cache.h"
20 #include "ecmascript/js_function.h"
21 #include "ecmascript/js_tagged_value.h"
22 #include "ecmascript/tagged_array.h"
23 #include "ecmascript/tagged_dictionary.h"
24
25 namespace panda::ecmascript {
26 enum class ICKind : uint32_t {
27 NamedLoadIC,
28 NamedStoreIC,
29 LoadIC,
30 StoreIC,
31 NamedGlobalLoadIC,
32 NamedGlobalStoreIC,
33 NamedGlobalTryLoadIC,
34 NamedGlobalTryStoreIC,
35 GlobalLoadIC,
36 GlobalStoreIC,
37 };
38
IsNamedGlobalIC(ICKind kind)39 static inline bool IsNamedGlobalIC(ICKind kind)
40 {
41 return (kind == ICKind::NamedGlobalLoadIC) || (kind == ICKind::NamedGlobalStoreIC) ||
42 (kind == ICKind::NamedGlobalTryLoadIC) || (kind == ICKind::NamedGlobalTryStoreIC);
43 }
44
IsValueGlobalIC(ICKind kind)45 static inline bool IsValueGlobalIC(ICKind kind)
46 {
47 return (kind == ICKind::GlobalLoadIC) || (kind == ICKind::GlobalStoreIC);
48 }
49
IsValueNormalIC(ICKind kind)50 static inline bool IsValueNormalIC(ICKind kind)
51 {
52 return (kind == ICKind::LoadIC) || (kind == ICKind::StoreIC);
53 }
54
IsValueIC(ICKind kind)55 static inline bool IsValueIC(ICKind kind)
56 {
57 return IsValueNormalIC(kind) || IsValueGlobalIC(kind);
58 }
59
IsNamedNormalIC(ICKind kind)60 static inline bool IsNamedNormalIC(ICKind kind)
61 {
62 return (kind == ICKind::NamedLoadIC) || (kind == ICKind::NamedStoreIC);
63 }
64
IsNamedIC(ICKind kind)65 static inline bool IsNamedIC(ICKind kind)
66 {
67 return IsNamedNormalIC(kind) || IsNamedGlobalIC(kind);
68 }
69
IsGlobalLoadIC(ICKind kind)70 static inline bool IsGlobalLoadIC(ICKind kind)
71 {
72 return (kind == ICKind::NamedGlobalLoadIC) || (kind == ICKind::GlobalLoadIC) ||
73 (kind == ICKind::NamedGlobalTryLoadIC);
74 }
75
IsGlobalStoreIC(ICKind kind)76 static inline bool IsGlobalStoreIC(ICKind kind)
77 {
78 return (kind == ICKind::NamedGlobalStoreIC) || (kind == ICKind::GlobalStoreIC) ||
79 (kind == ICKind::NamedGlobalTryStoreIC);
80 }
81
IsGlobalIC(ICKind kind)82 static inline bool IsGlobalIC(ICKind kind)
83 {
84 return IsValueGlobalIC(kind) || IsNamedGlobalIC(kind);
85 }
86
87 std::string ICKindToString(ICKind kind);
88
89 class ProfileTypeAccessorLockScope {
90 public:
ProfileTypeAccessorLockScope(JSThread * thread)91 ProfileTypeAccessorLockScope(JSThread *thread)
92 {
93 if (thread->GetEcmaVM()->IsEnableFastJit() || thread->GetEcmaVM()->IsEnableBaselineJit()) {
94 lockHolder_.emplace(thread->GetProfileTypeAccessorLock());
95 }
96 }
97
98 private:
99 std::optional<LockHolder> lockHolder_;
100 };
101
102 /**
103 * ProfileTypeInfo
104 * +--------------------------------+
105 * | ic slot |
106 * | ..... |
107 * +--------------------------------+
108 * | 64 bits jit osr |
109 * | tagged array address |
110 * +--------------------------------+
111 * | low 32 bits(PeriodCount) |
112 * | hight 32 bits(jit hotness) |
113 * +--------------------------------+
114 * | low 32 bits(osr hotness) |
115 * | hight 32 bits(baseline hotness)|
116 * +--------------------------------+
117 */
118 class ProfileTypeInfo : public TaggedArray {
119 public:
120 static const uint32_t MAX_FUNC_CACHE_INDEX = std::numeric_limits<uint32_t>::max();
121 static constexpr uint32_t INVALID_SLOT_INDEX = 0xFF;
122 static constexpr uint32_t MAX_SLOT_INDEX = 0xFFFF;
123 static constexpr size_t BIT_FIELD_INDEX = 2;
124 static constexpr size_t JIT_OSR_INDEX = 3;
125 static constexpr size_t EXTRA_INFO_MAP_INDEX = 4;
126 static constexpr size_t RESERVED_LENGTH = EXTRA_INFO_MAP_INDEX;
127 static constexpr size_t INITIAL_PERIOD_INDEX = 0;
128 static constexpr size_t PRE_DUMP_PERIOD_INDEX = 1;
129 static constexpr size_t DUMP_PERIOD_INDEX = 2;
130 static constexpr size_t BIG_METHOD_PERIOD_INDEX = 3;
131 static constexpr size_t INITIAL_OSR_HOTNESS_THRESHOLD = 0;
132 static constexpr size_t INITIAL_OSR_HOTNESS_CNT = 0;
133 static constexpr size_t INITIAL_JIT_CALL_THRESHOLD = 0;
134 static constexpr size_t INITIAL_JIT_CALL_CNT = 0;
135 static constexpr uint16_t JIT_DISABLE_FLAG = 0xFFFF;
136 static constexpr size_t JIT_HOTNESS_THRESHOLD_OFFSET_FROM_BITFIELD = 4; // 4 : 4 byte offset from bitfield
137 static constexpr size_t JIT_CNT_OFFSET_FROM_THRESHOLD = 2; // 2 : 2 byte offset from jit hotness threshold
138 static constexpr size_t OSR_HOTNESS_THRESHOLD_OFFSET_FROM_BITFIELD = 8; // 8 : 8 byte offset from bitfield
139 static constexpr size_t OSR_CNT_OFFSET_FROM_OSR_THRESHOLD = 2; // 2 : 2 byte offset from osr hotness threshold
140 static constexpr size_t BASELINEJIT_HOTNESS_THRESHOLD_OFFSET_FROM_BITFIELD = 12; // 12: bytes offset from bitfield
141 static constexpr size_t JIT_CALL_CNT_OFFSET_FROM_BITFIELD = 14; // 14 : 14 byte offset from bitfield
142
Cast(TaggedObject * object)143 static ProfileTypeInfo *Cast(TaggedObject *object)
144 {
145 ASSERT(JSTaggedValue(object).IsTaggedArray());
146 return static_cast<ProfileTypeInfo *>(object);
147 }
148
ComputeSize(uint32_t icSlotSize)149 static size_t ComputeSize(uint32_t icSlotSize)
150 {
151 return TaggedArray::ComputeSize(JSTaggedValue::TaggedTypeSize(), AdjustSlotSize(icSlotSize) + RESERVED_LENGTH);
152 }
153
GetIcSlotLength()154 inline uint32_t GetIcSlotLength() const
155 {
156 return GetLength() - RESERVED_LENGTH;
157 }
158
GetIcSlotToOsrLength()159 inline uint32_t GetIcSlotToOsrLength() const
160 {
161 return GetLength() - BIT_FIELD_INDEX;
162 }
163
AdjustSlotSize(uint32_t icSlotSize)164 static inline uint32_t AdjustSlotSize(uint32_t icSlotSize)
165 {
166 // if ic slot size is 0xff comes from frontend, it means the actual size is 0x100.
167 // 0xff is a invalid slot index, which value is hole.
168 if (icSlotSize == INVALID_SLOT_INDEX) {
169 ++icSlotSize;
170 }
171 return icSlotSize;
172 }
173
SetPrimitiveOfSlot(JSTaggedValue initValue,uint32_t icSlotSize)174 inline void SetPrimitiveOfSlot(JSTaggedValue initValue, uint32_t icSlotSize)
175 {
176 for (uint32_t i = 0; i < icSlotSize; i++) {
177 size_t offset = JSTaggedValue::TaggedTypeSize() * i;
178 if (i == INVALID_SLOT_INDEX) {
179 Barriers::SetPrimitive<JSTaggedType>(GetData(), offset, JSTaggedValue::Hole().GetRawData());
180 } else {
181 Barriers::SetPrimitive<JSTaggedType>(GetData(), offset, initValue.GetRawData());
182 }
183 }
184 }
185
SetSpecialValue()186 inline void SetSpecialValue()
187 {
188 SetPeriodIndex(INITIAL_PERIOD_INDEX);
189 SetJitHotnessThreshold(JIT_DISABLE_FLAG);
190 SetJitHotnessCnt(0);
191 SetBaselineJitHotnessThreshold(JIT_DISABLE_FLAG);
192 SetOsrHotnessThreshold(INITIAL_OSR_HOTNESS_THRESHOLD);
193 SetOsrHotnessCnt(INITIAL_OSR_HOTNESS_CNT);
194 SetJitCallThreshold(INITIAL_JIT_CALL_THRESHOLD);
195 }
196
InitializeExtraInfoMap()197 inline void InitializeExtraInfoMap()
198 {
199 // the last-1 of the cache is used to save extra info map
200 Barriers::SetPrimitive<JSTaggedType>(
201 GetData(), GetIcSlotLength() * JSTaggedValue::TaggedTypeSize(),
202 JSTaggedValue::Undefined().GetRawData());
203 }
204
InitializeJitOsr()205 inline void InitializeJitOsr()
206 {
207 // the last of the cache is used to save osr jit tagged array
208 Barriers::SetPrimitive<JSTaggedType>(
209 GetData(), (GetIcSlotLength() + 1) * JSTaggedValue::TaggedTypeSize(),
210 JSTaggedValue::Undefined().GetRawData());
211 }
212
213 inline void InitializeWithSpecialValue(JSTaggedValue initValue, uint32_t icSlotSize, uint32_t extraLength = 0)
214 {
215 ASSERT(initValue.IsSpecial());
216 icSlotSize = AdjustSlotSize(icSlotSize);
217 SetLength(icSlotSize + RESERVED_LENGTH);
218 SetExtraLength(extraLength);
219 SetPrimitiveOfSlot(initValue, icSlotSize);
220 InitializeExtraInfoMap();
221 InitializeJitOsr();
222 SetSpecialValue();
223 }
224
SetPreDumpPeriodIndex()225 void SetPreDumpPeriodIndex()
226 {
227 SetPeriodIndex(PRE_DUMP_PERIOD_INDEX);
228 }
229
IsProfileTypeInfoPreDumped()230 bool IsProfileTypeInfoPreDumped() const
231 {
232 return GetPeriodIndex() == PRE_DUMP_PERIOD_INDEX;
233 }
234
SetBigMethodPeriodIndex()235 void SetBigMethodPeriodIndex()
236 {
237 SetPeriodIndex(BIG_METHOD_PERIOD_INDEX);
238 }
239
IsProfileTypeInfoWithBigMethod()240 bool IsProfileTypeInfoWithBigMethod() const
241 {
242 return GetPeriodIndex() == BIG_METHOD_PERIOD_INDEX;
243 }
244
GetExtraInfoMap()245 JSTaggedValue GetExtraInfoMap() const
246 {
247 return JSTaggedValue(Barriers::GetValue<JSTaggedType>(GetData(), GetExtraInfoMapOffset()));
248 }
249
SetExtraInfoMap(const JSThread * thread,JSHandle<NumberDictionary> extraInfoMap)250 void SetExtraInfoMap(const JSThread *thread, JSHandle<NumberDictionary> extraInfoMap)
251 {
252 Barriers::SetObject<true>(thread, GetData(), GetExtraInfoMapOffset(),
253 extraInfoMap.GetTaggedValue().GetRawData());
254 }
255
GetJitHotnessThreshold()256 uint16_t GetJitHotnessThreshold() const
257 {
258 return Barriers::GetValue<uint16_t>(GetData(), GetJitHotnessThresholdBitfieldOffset());
259 }
260
SetJitHotnessThreshold(uint16_t count)261 void SetJitHotnessThreshold(uint16_t count)
262 {
263 Barriers::SetPrimitive(GetData(), GetJitHotnessThresholdBitfieldOffset(), count);
264 }
265
GetOsrHotnessThreshold()266 uint16_t GetOsrHotnessThreshold() const
267 {
268 return Barriers::GetValue<uint16_t>(GetData(), GetOsrHotnessThresholdBitfieldOffset());
269 }
270
SetOsrHotnessThreshold(uint16_t count)271 void SetOsrHotnessThreshold(uint16_t count)
272 {
273 Barriers::SetPrimitive(GetData(), GetOsrHotnessThresholdBitfieldOffset(), count);
274 }
275
GetBaselineJitHotnessThreshold()276 uint16_t GetBaselineJitHotnessThreshold() const
277 {
278 return Barriers::GetValue<uint16_t>(GetData(), GetBaselineJitHotnessThresholdBitfieldOffset());
279 }
280
SetBaselineJitHotnessThreshold(uint16_t count)281 void SetBaselineJitHotnessThreshold(uint16_t count)
282 {
283 Barriers::SetPrimitive(GetData(), GetBaselineJitHotnessThresholdBitfieldOffset(), count);
284 }
285
SetJitCallThreshold(uint16_t count)286 void SetJitCallThreshold(uint16_t count)
287 {
288 Barriers::SetPrimitive(GetData(), GetJitCallCntBitfieldOffset(), count);
289 }
290
GetJitHotnessCnt()291 uint16_t GetJitHotnessCnt() const
292 {
293 return Barriers::GetValue<uint16_t>(GetData(), GetJitHotnessCntBitfieldOffset());
294 }
295
SetJitHotnessCnt(uint16_t count)296 void SetJitHotnessCnt(uint16_t count)
297 {
298 Barriers::SetPrimitive(GetData(), GetJitHotnessCntBitfieldOffset(), count);
299 }
300
SetOsrHotnessCnt(uint16_t count)301 void SetOsrHotnessCnt(uint16_t count)
302 {
303 Barriers::SetPrimitive(GetData(), GetOsrHotnessCntBitfieldOffset(), count);
304 }
305
GetIcSlot(uint32_t idx)306 inline JSTaggedValue GetIcSlot(uint32_t idx) const
307 {
308 ASSERT(idx < GetIcSlotLength());
309 return TaggedArray::Get(idx);
310 }
311
SetIcSlot(const JSThread * thread,uint32_t idx,const JSTaggedValue & value)312 inline void SetIcSlot(const JSThread* thread, uint32_t idx, const JSTaggedValue& value)
313 {
314 ASSERT(idx < GetIcSlotLength());
315 TaggedArray::Set(thread, idx, value);
316 }
317
SetMultiIcSlotLocked(JSThread * thread,uint32_t firstIdx,const JSTaggedValue & firstValue,uint32_t secondIdx,const JSTaggedValue & secondValue)318 inline void SetMultiIcSlotLocked(JSThread* thread, uint32_t firstIdx, const JSTaggedValue& firstValue,
319 uint32_t secondIdx, const JSTaggedValue& secondValue)
320 {
321 ProfileTypeAccessorLockScope accessorLockScope(thread);
322 ASSERT(firstIdx < GetIcSlotLength());
323 ASSERT(secondIdx < GetIcSlotLength());
324 TaggedArray::Set(thread, firstIdx, firstValue);
325 TaggedArray::Set(thread, secondIdx, secondValue);
326 }
327
CreateOrGetExtraInfoMap(const JSThread * thread,JSHandle<ProfileTypeInfo> profileTypeInfo)328 static JSHandle<NumberDictionary> CreateOrGetExtraInfoMap(const JSThread *thread,
329 JSHandle<ProfileTypeInfo> profileTypeInfo)
330 {
331 if (profileTypeInfo->GetExtraInfoMap().IsUndefined()) {
332 JSHandle<NumberDictionary> dictJShandle = NumberDictionary::Create(thread);
333 profileTypeInfo->SetExtraInfoMap(thread, dictJShandle);
334 return dictJShandle;
335 }
336 JSHandle<NumberDictionary> dictJShandle(thread, profileTypeInfo->GetExtraInfoMap());
337 return dictJShandle;
338 }
339
UpdateExtraInfoMap(const JSThread * thread,JSHandle<NumberDictionary> dictJShandle,JSHandle<JSTaggedValue> key,JSHandle<JSTaggedValue> receiverHClassHandle,JSHandle<JSTaggedValue> holderHClassHandle,JSHandle<ProfileTypeInfo> profileTypeInfo)340 static void UpdateExtraInfoMap(const JSThread *thread, JSHandle<NumberDictionary> dictJShandle,
341 JSHandle<JSTaggedValue> key, JSHandle<JSTaggedValue> receiverHClassHandle,
342 JSHandle<JSTaggedValue> holderHClassHandle,
343 JSHandle<ProfileTypeInfo> profileTypeInfo)
344 {
345 JSHandle<JSTaggedValue> info(thread->GetEcmaVM()->GetFactory()->NewExtraProfileTypeInfo());
346 JSHandle<ExtraProfileTypeInfo> infoHandle(info);
347 infoHandle->SetReceiver(thread, receiverHClassHandle.GetTaggedValue().CreateAndGetWeakRef());
348 infoHandle->SetHolder(thread, holderHClassHandle.GetTaggedValue());
349 JSHandle<NumberDictionary> dict = NumberDictionary::PutIfAbsent(thread,
350 dictJShandle,
351 key,
352 info,
353 PropertyAttributes::Default());
354 profileTypeInfo->SetExtraInfoMap(thread, dict);
355 }
356
357 DECL_VISIT_ARRAY(DATA_OFFSET, GetIcSlotToOsrLength(), GetIcSlotToOsrLength());
358
DECL_DUMP()359 DECL_DUMP()
360
361 private:
362 uint32_t GetPeriodIndex() const
363 {
364 return Barriers::GetValue<uint32_t>(GetData(), GetBitfieldOffset());
365 }
366
SetPeriodIndex(uint32_t count)367 void SetPeriodIndex(uint32_t count)
368 {
369 Barriers::SetPrimitive(GetData(), GetBitfieldOffset(), count);
370 }
371
GetBitfieldOffset()372 inline size_t GetBitfieldOffset() const
373 {
374 return JSTaggedValue::TaggedTypeSize() * (GetLength() - BIT_FIELD_INDEX);
375 }
376
GetExtraInfoMapOffset()377 inline size_t GetExtraInfoMapOffset() const
378 {
379 return JSTaggedValue::TaggedTypeSize() * (GetLength() - EXTRA_INFO_MAP_INDEX);
380 }
381
382 // jit hotness(16bits) + count(16bits)
GetJitHotnessThresholdBitfieldOffset()383 inline size_t GetJitHotnessThresholdBitfieldOffset() const
384 {
385 return GetBitfieldOffset() + JIT_HOTNESS_THRESHOLD_OFFSET_FROM_BITFIELD;
386 }
387
GetJitHotnessCntBitfieldOffset()388 inline size_t GetJitHotnessCntBitfieldOffset() const
389 {
390 return GetJitHotnessThresholdBitfieldOffset() + JIT_CNT_OFFSET_FROM_THRESHOLD;
391 }
392
393 // osr hotness(16bits) + count(16bits)
GetOsrHotnessThresholdBitfieldOffset()394 inline size_t GetOsrHotnessThresholdBitfieldOffset() const
395 {
396 return GetBitfieldOffset() + OSR_HOTNESS_THRESHOLD_OFFSET_FROM_BITFIELD;
397 }
398
399 // baselinejit hotness(16bits)
GetBaselineJitHotnessThresholdBitfieldOffset()400 inline size_t GetBaselineJitHotnessThresholdBitfieldOffset() const
401 {
402 return GetBitfieldOffset() + BASELINEJIT_HOTNESS_THRESHOLD_OFFSET_FROM_BITFIELD;
403 }
404
GetOsrHotnessCntBitfieldOffset()405 inline size_t GetOsrHotnessCntBitfieldOffset() const
406 {
407 return GetOsrHotnessThresholdBitfieldOffset() + OSR_CNT_OFFSET_FROM_OSR_THRESHOLD;
408 }
409
410 // jit call count(16bits)
GetJitCallCntBitfieldOffset()411 inline size_t GetJitCallCntBitfieldOffset() const
412 {
413 return GetBitfieldOffset() + JIT_CALL_CNT_OFFSET_FROM_BITFIELD;
414 }
415 };
416
417 class ProfileTypeAccessor {
418 public:
419 static constexpr size_t CACHE_MAX_LEN = 8;
420 static constexpr size_t MONO_CASE_NUM = 2;
421 static constexpr size_t POLY_CASE_NUM = 4;
422
423 enum ICState {
424 UNINIT,
425 MONO,
426 POLY,
427 IC_MEGA,
428 MEGA,
429 };
430
431 #if ECMASCRIPT_ENABLE_TRACE_LOAD
432 enum MegaState {
433 NONE,
434 NOTFOUND_MEGA,
435 DICT_MEGA,
436 };
437 #endif
438
ProfileTypeAccessor(JSThread * thread,JSHandle<ProfileTypeInfo> profileTypeInfo,uint32_t slotId,ICKind kind)439 ProfileTypeAccessor(JSThread* thread, JSHandle<ProfileTypeInfo> profileTypeInfo, uint32_t slotId, ICKind kind)
440 : thread_(thread), profileTypeInfo_(profileTypeInfo), slotId_(slotId), kind_(kind)
441 {
442 enableICMega_ = thread_->GetEcmaVM()->GetJSOptions().IsEnableMegaIC();
443 }
444 ~ProfileTypeAccessor() = default;
445 ICState GetMegaState() const;
446 ICState GetICState() const;
447 static std::string ICStateToString(ICState state);
448 void AddHandlerWithoutKey(JSHandle<JSTaggedValue> hclass, JSHandle<JSTaggedValue> handler,
449 JSHandle<JSTaggedValue> keyForMegaIC = JSHandle<JSTaggedValue>(),
450 MegaICCache::MegaICKind kind = MegaICCache::MegaICKind::None) const;
451 void AddWithoutKeyPoly(JSHandle<JSTaggedValue> hclass, JSHandle<JSTaggedValue> handler, uint32_t index,
452 JSTaggedValue profileData, JSHandle<JSTaggedValue> keyForMegaIC = JSHandle<JSTaggedValue>(),
453 MegaICCache::MegaICKind kind = MegaICCache::MegaICKind::None) const;
454
455 void AddElementHandler(JSHandle<JSTaggedValue> hclass, JSHandle<JSTaggedValue> handler) const;
456 void AddHandlerWithKey(JSHandle<JSTaggedValue> key, JSHandle<JSTaggedValue> hclass,
457 JSHandle<JSTaggedValue> handler) const;
458 void AddGlobalHandlerKey(JSHandle<JSTaggedValue> key, JSHandle<JSTaggedValue> handler) const;
459 void AddGlobalRecordHandler(JSHandle<JSTaggedValue> handler) const;
460
GetWeakRef(JSTaggedValue value)461 JSTaggedValue GetWeakRef(JSTaggedValue value) const
462 {
463 return JSTaggedValue(value.CreateAndGetWeakRef());
464 }
465
GetRefFromWeak(const JSTaggedValue & value)466 JSTaggedValue GetRefFromWeak(const JSTaggedValue &value) const
467 {
468 return JSTaggedValue(value.GetWeakReferent());
469 }
470 void SetAsMega() const;
471 void SetAsMegaForTraceSlowMode(ObjectOperator& op) const;
472 void SetAsMegaForTrace(JSTaggedValue value) const;
473
GetKind()474 ICKind GetKind() const
475 {
476 return kind_;
477 }
478
GetSlotId()479 uint32_t GetSlotId() const
480 {
481 return slotId_;
482 }
483
484 private:
485 JSThread* thread_;
486 JSHandle<ProfileTypeInfo> profileTypeInfo_;
487 uint32_t slotId_;
488 ICKind kind_;
489 bool enableICMega_;
490 };
491 } // namespace panda::ecmascript
492
493 #endif // ECMASCRIPT_IC_PROFILE_TYPE_INFO_H
494